From 29e54410a6ce3fbf96fff1a305db2b5233e7754a Mon Sep 17 00:00:00 2001 From: Martin Tomka Date: Tue, 20 Jun 2023 13:27:57 +0200 Subject: [PATCH 1/4] Introduce Outcome --- .../MultipleStrategiesBenchmark.cs | 2 +- .../ResilienceStrategyBenchmark.cs | 2 +- .../TelemetryBenchmark.cs | 2 +- .../Utils/Helper.CircuitBreaker.cs | 2 +- .../Utils/Helper.Hedging.cs | 2 +- bench/Polly.Core.Benchmarks/Utils/Helper.cs | 2 +- .../Controller/CircuitStateController.cs | 8 +- .../Fallback/FallbackResilienceStrategy.cs | 2 +- .../Hedging/Controller/TaskExecution.cs | 6 +- .../Hedging/HedgingResilienceStrategy.cs | 4 +- src/Polly.Core/Outcome.TResult.cs | 121 +++++++++++++++ src/Polly.Core/Outcome.cs | 138 ++++-------------- .../ResilienceStrategy.Async.ValueTask.cs | 16 +- .../ResilienceStrategy.Async.ValueTaskT.cs | 16 +- src/Polly.Core/ResilienceStrategy.Sync.cs | 24 +-- src/Polly.Core/ResilienceStrategy.SyncT.cs | 28 ++-- src/Polly.Core/ResilienceStrategy.cs | 6 +- .../Retry/RetryResilienceStrategy.cs | 2 +- .../Timeout/TimeoutResilienceStrategy.cs | 2 +- .../Utils/ReloadableResilienceStrategy.cs | 2 +- .../Utils/ResilienceStrategyPipeline.cs | 4 +- src/Polly.Core/VoidResult.cs | 2 - .../Telemetry/TelemetryResilienceStrategy.cs | 4 +- .../RateLimiterResilienceStrategy.cs | 2 +- .../AdvancedCircuitBreakerOptionsTests.cs | 6 +- .../CircuitBreakerStateProviderTests.cs | 2 +- .../Controller/CircuitStateControllerTests.cs | 34 ++--- .../SimpleCircuitBreakerOptionsTests.cs | 6 +- .../Fallback/FallbackHandlerTests.cs | 8 +- .../Fallback/FallbackHelper.cs | 2 +- ...esilienceStrategyBuilderExtensionsTests.cs | 6 +- .../FallbackResilienceStrategyTests.cs | 10 +- .../Fallback/FallbackStrategyOptionsTests.cs | 6 +- .../Controller/HedgingControllerTests.cs | 2 +- .../HedgingExecutionContextTests.cs | 14 +- .../Hedging/Controller/TaskExecutionTests.cs | 14 +- .../HedgingActionGeneratorArgumentsTests.cs | 2 +- .../Hedging/HedgingActions.cs | 6 +- .../Hedging/HedgingHandlerTests.cs | 10 +- ...esilienceStrategyBuilderExtensionsTests.cs | 6 +- .../Hedging/HedgingResilienceStrategyTests.cs | 46 +++--- .../Hedging/HedgingStrategyOptionsTests.cs | 8 +- .../Polly.Core.Tests/OutcomeArgumentsTests.cs | 4 +- test/Polly.Core.Tests/OutcomeTests.cs | 12 +- .../Polly.Core.Tests/PredicateBuilderTests.cs | 36 ++--- ...esilienceStrategyTests.Async.ValueTaskT.cs | 2 +- .../ResilienceStrategyTests.TResult.Async.cs | 2 +- .../Retry/RetryResilienceStrategyTests.cs | 4 +- .../Retry/RetryStrategyOptionsTests.cs | 6 +- .../ResilienceStrategyTelemetryTests.cs | 6 +- .../Telemetry/TelemetryEventArgumentsTests.cs | 19 ++- .../Timeout/TimeoutResilienceStrategyTests.cs | 2 +- .../Utils/ResilienceStrategyPipelineTests.cs | 6 +- .../PollyServiceCollectionExtensionTests.cs | 2 +- ...esilienceTelemetryDiagnosticSourceTests.cs | 14 +- .../RateLimiterResilienceStrategyTests.cs | 2 +- test/Polly.TestUtils/Outcome.cs | 12 -- 57 files changed, 374 insertions(+), 342 deletions(-) create mode 100644 src/Polly.Core/Outcome.TResult.cs delete mode 100644 test/Polly.TestUtils/Outcome.cs diff --git a/bench/Polly.Core.Benchmarks/MultipleStrategiesBenchmark.cs b/bench/Polly.Core.Benchmarks/MultipleStrategiesBenchmark.cs index a1d4d594fd..94c17b2ee4 100644 --- a/bench/Polly.Core.Benchmarks/MultipleStrategiesBenchmark.cs +++ b/bench/Polly.Core.Benchmarks/MultipleStrategiesBenchmark.cs @@ -38,7 +38,7 @@ public async ValueTask ExecuteStrategyPipeline_NonGeneric_V8() var context = ResilienceContext.Get(); await _nonGeneric!.ExecuteOutcomeAsync( - static (_, _) => new ValueTask>(new Outcome("dummy")), + static (_, _) => new ValueTask>(Outcome.FromResult("dummy")), context, string.Empty).ConfigureAwait(false); diff --git a/bench/Polly.Core.Benchmarks/ResilienceStrategyBenchmark.cs b/bench/Polly.Core.Benchmarks/ResilienceStrategyBenchmark.cs index 8abeb05068..c5656c4a36 100644 --- a/bench/Polly.Core.Benchmarks/ResilienceStrategyBenchmark.cs +++ b/bench/Polly.Core.Benchmarks/ResilienceStrategyBenchmark.cs @@ -10,7 +10,7 @@ public class ResilienceStrategyBenchmark public async ValueTask ExecuteOutcomeAsync() { var context = ResilienceContext.Get(); - await NullResilienceStrategy.Instance.ExecuteOutcomeAsync((_, _) => new ValueTask>(new Outcome("dummy")), context, "state").ConfigureAwait(false); + await NullResilienceStrategy.Instance.ExecuteOutcomeAsync((_, _) => Outcome.FromResultAsTask("dummy"), context, "state").ConfigureAwait(false); ResilienceContext.Return(context); } diff --git a/bench/Polly.Core.Benchmarks/TelemetryBenchmark.cs b/bench/Polly.Core.Benchmarks/TelemetryBenchmark.cs index e1fa5a0b60..ab0e2048f2 100644 --- a/bench/Polly.Core.Benchmarks/TelemetryBenchmark.cs +++ b/bench/Polly.Core.Benchmarks/TelemetryBenchmark.cs @@ -34,7 +34,7 @@ public void Prepare() public async ValueTask Execute() { var context = ResilienceContext.Get(); - await _strategy!.ExecuteOutcomeAsync((_, _) => new ValueTask>(new Outcome("dummy")), context, "state").ConfigureAwait(false); + await _strategy!.ExecuteOutcomeAsync((_, _) => Outcome.FromResultAsTask("dummy"), context, "state").ConfigureAwait(false); ResilienceContext.Return(context); } diff --git a/bench/Polly.Core.Benchmarks/Utils/Helper.CircuitBreaker.cs b/bench/Polly.Core.Benchmarks/Utils/Helper.CircuitBreaker.cs index 46c6d614e0..362f842e96 100644 --- a/bench/Polly.Core.Benchmarks/Utils/Helper.CircuitBreaker.cs +++ b/bench/Polly.Core.Benchmarks/Utils/Helper.CircuitBreaker.cs @@ -75,7 +75,7 @@ private class OutcomeHandlingStrategy : ResilienceStrategy if (result.Exception is not null) { - return new Outcome(default(TResult)!); + return Outcome.FromResult(default); } return result; diff --git a/bench/Polly.Core.Benchmarks/Utils/Helper.Hedging.cs b/bench/Polly.Core.Benchmarks/Utils/Helper.Hedging.cs index 20ef51d431..ac44c060d7 100644 --- a/bench/Polly.Core.Benchmarks/Utils/Helper.Hedging.cs +++ b/bench/Polly.Core.Benchmarks/Utils/Helper.Hedging.cs @@ -13,7 +13,7 @@ public static ResilienceStrategy CreateHedging() builder.AddHedging(new HedgingStrategyOptions { ShouldHandle = args => new ValueTask(args.Result == Failure), - HedgingActionGenerator = args => () => new ValueTask>(new Outcome("hedged response")), + HedgingActionGenerator = args => () => Outcome.FromResultAsTask("hedged response"), }); }); } diff --git a/bench/Polly.Core.Benchmarks/Utils/Helper.cs b/bench/Polly.Core.Benchmarks/Utils/Helper.cs index 15c9e30fb3..d6f7177d1e 100644 --- a/bench/Polly.Core.Benchmarks/Utils/Helper.cs +++ b/bench/Polly.Core.Benchmarks/Utils/Helper.cs @@ -15,7 +15,7 @@ public static async ValueTask ExecuteAsync(this object obj, PollyVersion version var context = ResilienceContext.Get(); await ((ResilienceStrategy)obj).ExecuteOutcomeAsync( - static (_, _) => new ValueTask>(new Outcome("dummy")), + static (_, _) => Outcome.FromResultAsTask("dummy"), context, string.Empty).ConfigureAwait(false); diff --git a/src/Polly.Core/CircuitBreaker/Controller/CircuitStateController.cs b/src/Polly.Core/CircuitBreaker/Controller/CircuitStateController.cs index cbb46ca06d..130de1495f 100644 --- a/src/Polly.Core/CircuitBreaker/Controller/CircuitStateController.cs +++ b/src/Polly.Core/CircuitBreaker/Controller/CircuitStateController.cs @@ -89,8 +89,8 @@ public ValueTask IsolateCircuitAsync(ResilienceContext context) lock (_lock) { - SetLastHandledOutcome_NeedsLock(new Outcome(new IsolatedCircuitException())); - OpenCircuitFor_NeedsLock(new Outcome(default(T)), TimeSpan.MaxValue, manual: true, context, out task); + SetLastHandledOutcome_NeedsLock(Outcome.FromException(new IsolatedCircuitException())); + OpenCircuitFor_NeedsLock(Outcome.FromResult(default), TimeSpan.MaxValue, manual: true, context, out task); _circuitState = CircuitState.Isolated; } @@ -107,7 +107,7 @@ public ValueTask CloseCircuitAsync(ResilienceContext context) lock (_lock) { - CloseCircuit_NeedsLock(new Outcome(default(T)), manual: true, context, out task); + CloseCircuit_NeedsLock(Outcome.FromResult(default), manual: true, context, out task); } return ExecuteScheduledTaskAsync(task, context); @@ -150,7 +150,7 @@ public ValueTask CloseCircuitAsync(ResilienceContext context) if (exception is not null) { - return new Outcome(exception); + return Outcome.FromException(exception); } return null; diff --git a/src/Polly.Core/Fallback/FallbackResilienceStrategy.cs b/src/Polly.Core/Fallback/FallbackResilienceStrategy.cs index b84173ebfd..fd6db6966e 100644 --- a/src/Polly.Core/Fallback/FallbackResilienceStrategy.cs +++ b/src/Polly.Core/Fallback/FallbackResilienceStrategy.cs @@ -42,7 +42,7 @@ protected override async ValueTask> ExecuteCallbackAsync(Func } catch (Exception e) { - return new Outcome(e); + return Outcome.FromException(e); } } } diff --git a/src/Polly.Core/Hedging/Controller/TaskExecution.cs b/src/Polly.Core/Hedging/Controller/TaskExecution.cs index 862ad8bb8f..4af6d5064a 100644 --- a/src/Polly.Core/Hedging/Controller/TaskExecution.cs +++ b/src/Polly.Core/Hedging/Controller/TaskExecution.cs @@ -197,7 +197,7 @@ private async Task ExecuteSecondaryActionAsync(Func>> actio } catch (Exception e) { - outcome = new Outcome(e); + outcome = Polly.Outcome.FromException(e); } _stopExecutionTimestamp = _timeProvider.GetTimestamp(); @@ -206,7 +206,7 @@ private async Task ExecuteSecondaryActionAsync(Func>> actio private async Task ExecuteCreateActionException(Exception e) { - await UpdateOutcomeAsync(new Outcome(e)).ConfigureAwait(Context.ContinueOnCapturedContext); + await UpdateOutcomeAsync(Polly.Outcome.FromException(e)).ConfigureAwait(Context.ContinueOnCapturedContext); } private async Task ExecutePrimaryActionAsync(Func>> primaryCallback, TState state) @@ -219,7 +219,7 @@ private async Task ExecutePrimaryActionAsync(Func(e); + outcome = Polly.Outcome.FromException(e); } _stopExecutionTimestamp = _timeProvider.GetTimestamp(); diff --git a/src/Polly.Core/Hedging/HedgingResilienceStrategy.cs b/src/Polly.Core/Hedging/HedgingResilienceStrategy.cs index 2a0800bcab..68bf4e38a3 100644 --- a/src/Polly.Core/Hedging/HedgingResilienceStrategy.cs +++ b/src/Polly.Core/Hedging/HedgingResilienceStrategy.cs @@ -77,7 +77,7 @@ internal sealed class HedgingResilienceStrategy : OutcomeResilienceStrategy(new OperationCanceledException(cancellationToken).TrySetStackTrace()); + return Outcome.FromException(new OperationCanceledException(cancellationToken).TrySetStackTrace()); } var loadedExecution = await hedgingContext.LoadExecutionAsync(callback, state).ConfigureAwait(context.ContinueOnCapturedContext); @@ -95,7 +95,7 @@ internal sealed class HedgingResilienceStrategy : OutcomeResilienceStrategy(default(T)), + Outcome.FromResult(default), new OnHedgingArguments(attempt, HasOutcome: false, ExecutionTime: delay)).ConfigureAwait(context.ContinueOnCapturedContext); continue; } diff --git a/src/Polly.Core/Outcome.TResult.cs b/src/Polly.Core/Outcome.TResult.cs new file mode 100644 index 0000000000..4f4858e8a7 --- /dev/null +++ b/src/Polly.Core/Outcome.TResult.cs @@ -0,0 +1,121 @@ +#pragma warning disable CA1815 // Override equals and operator equals on value types + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.ExceptionServices; + +namespace Polly; + +/// +/// Represents the outcome of an operation which could be a result of type or an exception. +/// +/// The result type of the operation. +/// +/// Always use the constructor when creating this struct, otherwise we do not guarantee binary compatibility. +/// +public readonly struct Outcome +{ + internal Outcome(Exception exception) + : this() => ExceptionDispatchInfo = ExceptionDispatchInfo.Capture(Guard.NotNull(exception)); + + internal Outcome(TResult? result) + : this() => Result = result; + + private Outcome(ExceptionDispatchInfo exceptionDispatchInfo) + : this() => ExceptionDispatchInfo = Guard.NotNull(exceptionDispatchInfo); + + /// + /// Gets the exception that occurred during the operation, if any. + /// + public Exception? Exception => ExceptionDispatchInfo?.SourceException; + + /// + /// Gets the associated with the exception, if any. + /// + internal ExceptionDispatchInfo? ExceptionDispatchInfo { get; } + + /// + /// Gets the result of the operation, if any. + /// + public TResult? Result { get; } + + /// + /// Gets a value indicating whether the operation produced a result. + /// + /// + /// Returns even if the result is void. Use to check for void results. + /// + public bool HasResult => ExceptionDispatchInfo == null; + + /// + /// Gets a value indicating whether the operation produced a void result. + /// + public bool IsVoidResult => Result is VoidResult; + + /// + /// Throws an exception if the operation produced an exception. + /// + /// + /// If the operation produced a result, this method does nothing. The thrown exception maintains its original stack trace. + /// + public void EnsureSuccess() => ExceptionDispatchInfo?.Throw(); + + /// + /// Tries to get the result, if available. + /// + /// Output parameter for the result. + /// if the result is available; otherwise. + public bool TryGetResult(out TResult? result) + { + if (HasResult && !IsVoidResult) + { + result = Result!; + return true; + } + + result = default; + return false; + } + + /// + /// Returns the string representation of the outcome. + /// + /// + /// The exception message if the outcome is an exception; otherwise, the string representation of the result. + /// + public override string ToString() => ExceptionDispatchInfo != null + ? Exception!.Message + : Result?.ToString() ?? string.Empty; + + internal TResult GetResultOrRethrow() + { + ExceptionDispatchInfo?.Throw(); + 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 d3d08f8487..3e08bef2a2 100644 --- a/src/Polly.Core/Outcome.cs +++ b/src/Polly.Core/Outcome.cs @@ -1,132 +1,58 @@ -#pragma warning disable CA1815 // Override equals and operator equals on value types - -using System; -using System.Runtime.CompilerServices; -using System.Runtime.ExceptionServices; +#pragma warning disable CA1815 // Override equals and operator equals on value types namespace Polly; /// -/// Represents the outcome of an operation which could be a result of type or an exception. +/// Produces instances of . /// -/// The result type of the operation. -/// -/// Always use the constructor when creating this struct, otherwise we do not guarantee binary compatibility. -/// -public readonly struct Outcome +public static class Outcome { /// - /// Initializes a new instance of the struct. - /// - /// The occurred exception during the operation. - /// Thrown if is . - public Outcome(Exception exception) - : this() => ExceptionDispatchInfo = ExceptionDispatchInfo.Capture(Guard.NotNull(exception)); - - internal Outcome(ExceptionDispatchInfo exceptionDispatchInfo) - : this() => ExceptionDispatchInfo = Guard.NotNull(exceptionDispatchInfo); - - /// - /// Initializes a new instance of the struct. - /// - /// The result of the operation. - public Outcome(TResult? result) - : this() => Result = result; - - /// - /// Gets the exception that occurred during the operation, if any. - /// - public Exception? Exception => ExceptionDispatchInfo?.SourceException; - - /// - /// Gets the associated with the exception, if any. - /// - internal ExceptionDispatchInfo? ExceptionDispatchInfo { get; } - - internal ValueTask> AsValueTask() => new(this); - - /// - /// Gets the result of the operation, if any. - /// - public TResult? Result { get; } - - /// - /// Gets a value indicating whether the operation produced a result. - /// - /// - /// Returns even if the result is void. Use to check for void results. - /// - public bool HasResult => ExceptionDispatchInfo == null; - - /// - /// Gets a value indicating whether the operation produced a void result. + /// Returns a with the given . /// - public bool IsVoidResult => Result is VoidResult; + /// The type of the result. + /// The result value. + /// An instance of . + public static Outcome FromResult(TResult? value) => new(value); /// - /// Throws an exception if the operation produced an exception. + /// Returns a with the given wrapped as . /// - /// - /// If the operation produced a result, this method does nothing. The thrown exception maintains its original stack trace. - /// - public void EnsureSuccess() => ExceptionDispatchInfo?.Throw(); + /// The type of the result. + /// The result value. + /// A completed that produces . + public static ValueTask> FromResultAsTask(TResult value) => new(FromResult(value)); /// - /// Tries to get the result, if available. + /// Returns a with the given . /// - /// Output parameter for the result. - /// if the result is available; otherwise. - public bool TryGetResult(out TResult? result) + /// The type of the result. + /// The exception. + /// An instance of . + /// Thrown when is . + public static Outcome FromException(Exception exception) { - if (HasResult && !IsVoidResult) - { - result = Result!; - return true; - } + Guard.NotNull(exception); - result = default; - return false; + return new(exception); } /// - /// Returns the string representation of the outcome. + /// Returns a with the given wrapped as . /// - /// - /// The exception message if the outcome is an exception; otherwise, the string representation of the result. - /// - public override string ToString() => ExceptionDispatchInfo != null - ? Exception!.Message - : Result?.ToString() ?? string.Empty; - - internal TResult GetResultOrRethrow() + /// The type of the result. + /// The exception. + /// A completed that produces . + /// Thrown when is . + public static ValueTask> FromExceptionAsTask(Exception exception) { - ExceptionDispatchInfo?.Throw(); - return Result!; - } + Guard.NotNull(exception); - 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)); - } + return new(FromException(exception)); + } - if (typeof(T) == typeof(TResult)) - { - var result = Result; + internal static Outcome Void => FromResult(VoidResult.Instance); - // 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)); - } + internal static Outcome FromException(Exception exception) => FromException(exception); - return new Outcome((T)(object)Result); - } } diff --git a/src/Polly.Core/ResilienceStrategy.Async.ValueTask.cs b/src/Polly.Core/ResilienceStrategy.Async.ValueTask.cs index dfeed64ad5..5ca27c175c 100644 --- a/src/Polly.Core/ResilienceStrategy.Async.ValueTask.cs +++ b/src/Polly.Core/ResilienceStrategy.Async.ValueTask.cs @@ -29,11 +29,11 @@ public abstract partial class ResilienceStrategy try { await state.callback(context, state.state).ConfigureAwait(context.ContinueOnCapturedContext); - return VoidResult.Outcome; + return Outcome.Void; } catch (Exception e) { - return new Outcome(e); + return Outcome.FromException(e); } }, context, @@ -64,11 +64,11 @@ public abstract partial class ResilienceStrategy try { await state(context).ConfigureAwait(context.ContinueOnCapturedContext); - return VoidResult.Outcome; + return Outcome.Void; } catch (Exception e) { - return new Outcome(e); + return Outcome.FromException(e); } }, context, @@ -103,11 +103,11 @@ public abstract partial class ResilienceStrategy try { await state.callback(state.state, context.CancellationToken).ConfigureAwait(context.ContinueOnCapturedContext); - return VoidResult.Outcome; + return Outcome.Void; } catch (Exception e) { - return new Outcome(e); + return Outcome.FromException(e); } }, context, @@ -144,11 +144,11 @@ public abstract partial class ResilienceStrategy try { await state(context.CancellationToken).ConfigureAwait(context.ContinueOnCapturedContext); - return VoidResult.Outcome; + return Outcome.Void; } catch (Exception e) { - return new Outcome(e); + return Outcome.FromException(e); } }, diff --git a/src/Polly.Core/ResilienceStrategy.Async.ValueTaskT.cs b/src/Polly.Core/ResilienceStrategy.Async.ValueTaskT.cs index 71f4c27a28..a13ac39973 100644 --- a/src/Polly.Core/ResilienceStrategy.Async.ValueTaskT.cs +++ b/src/Polly.Core/ResilienceStrategy.Async.ValueTaskT.cs @@ -56,11 +56,11 @@ public abstract partial class ResilienceStrategy { try { - return new Outcome(await state.callback(context, state.state).ConfigureAwait(context.ContinueOnCapturedContext)); + return Outcome.FromResult(await state.callback(context, state.state).ConfigureAwait(context.ContinueOnCapturedContext)); } catch (Exception e) { - return new Outcome(e); + return Outcome.FromException(e); } }, context, @@ -91,11 +91,11 @@ public abstract partial class ResilienceStrategy { try { - return new Outcome(await state(context).ConfigureAwait(context.ContinueOnCapturedContext)); + return Outcome.FromResult(await state(context).ConfigureAwait(context.ContinueOnCapturedContext)); } catch (Exception e) { - return new Outcome(e); + return Outcome.FromException(e); } }, context, @@ -130,11 +130,11 @@ public abstract partial class ResilienceStrategy { try { - return new Outcome(await state.callback(state.state, context.CancellationToken).ConfigureAwait(context.ContinueOnCapturedContext)); + return Outcome.FromResult(await state.callback(state.state, context.CancellationToken).ConfigureAwait(context.ContinueOnCapturedContext)); } catch (Exception e) { - return new Outcome(e); + return Outcome.FromException(e); } }, context, @@ -171,11 +171,11 @@ public abstract partial class ResilienceStrategy { try { - return new Outcome(await state(context.CancellationToken).ConfigureAwait(context.ContinueOnCapturedContext)); + return Outcome.FromResult(await state(context.CancellationToken).ConfigureAwait(context.ContinueOnCapturedContext)); } catch (Exception e) { - return new Outcome(e); + return Outcome.FromException(e); } }, context, diff --git a/src/Polly.Core/ResilienceStrategy.Sync.cs b/src/Polly.Core/ResilienceStrategy.Sync.cs index 68f2d64dc9..17de37afe0 100644 --- a/src/Polly.Core/ResilienceStrategy.Sync.cs +++ b/src/Polly.Core/ResilienceStrategy.Sync.cs @@ -28,11 +28,11 @@ public abstract partial class ResilienceStrategy try { state.callback(context, state.state); - return VoidResult.Outcome; + return Outcome.Void; } catch (Exception e) { - return new Outcome(e); + return Outcome.FromException(e); } }, context, @@ -60,11 +60,11 @@ public abstract partial class ResilienceStrategy try { state(context); - return VoidResult.Outcome; + return Outcome.Void; } catch (Exception e) { - return new Outcome(e); + return Outcome.FromException(e); } }, context, @@ -96,11 +96,11 @@ public abstract partial class ResilienceStrategy try { state.callback(state.state, context.CancellationToken); - return VoidResult.Outcome; + return Outcome.Void; } catch (Exception e) { - return new Outcome(e); + return Outcome.FromException(e); } }, context, @@ -134,11 +134,11 @@ public abstract partial class ResilienceStrategy try { state(context.CancellationToken); - return VoidResult.Outcome; + return Outcome.Void; } catch (Exception e) { - return new Outcome(e); + return Outcome.FromException(e); } }, context, @@ -173,11 +173,11 @@ public abstract partial class ResilienceStrategy try { state.callback(state.state); - return VoidResult.Outcome; + return Outcome.Void; } catch (Exception e) { - return new Outcome(e); + return Outcome.FromException(e); } }, context, @@ -208,11 +208,11 @@ public void Execute(Action callback) try { state(); - return VoidResult.Outcome; + return Outcome.Void; } catch (Exception e) { - return new Outcome(e); + return Outcome.FromException(e); } }, context, diff --git a/src/Polly.Core/ResilienceStrategy.SyncT.cs b/src/Polly.Core/ResilienceStrategy.SyncT.cs index 41b2bdfac1..2a99db62f5 100644 --- a/src/Polly.Core/ResilienceStrategy.SyncT.cs +++ b/src/Polly.Core/ResilienceStrategy.SyncT.cs @@ -30,11 +30,11 @@ public abstract partial class ResilienceStrategy try { var result = state.callback(context, state.state); - return new Outcome(result); + return Outcome.FromResult(result); } catch (Exception e) { - return new Outcome(e); + return Outcome.FromException(e); } }, context, @@ -64,11 +64,11 @@ public abstract partial class ResilienceStrategy try { var result = state(context); - return new Outcome(result); + return Outcome.FromResult(result); } catch (Exception e) { - return new Outcome(e); + return Outcome.FromException(e); } }, context, @@ -98,12 +98,11 @@ public abstract partial class ResilienceStrategy { try { - var result = state(context.CancellationToken); - return new Outcome(result); + return Outcome.FromResult(state(context.CancellationToken)); } catch (Exception e) { - return new Outcome(e); + return Outcome.FromException(e); } }, context, @@ -135,12 +134,11 @@ public TResult Execute(Func callback) { try { - var result = state(); - return new Outcome(result); + return Outcome.FromResult(state()); } catch (Exception e) { - return new Outcome(e); + return Outcome.FromException(e); } }, context, @@ -174,12 +172,11 @@ public TResult Execute(Func callback) { try { - var result = state.callback(state.state); - return new Outcome(result); + return Outcome.FromResult(state.callback(state.state)); } catch (Exception e) { - return new Outcome(e); + return Outcome.FromException(e); } }, context, @@ -217,12 +214,11 @@ public TResult Execute(Func callback) { try { - var result = state.callback(state.state, context.CancellationToken); - return new Outcome(result); + return Outcome.FromResult(state.callback(state.state, context.CancellationToken)); } catch (Exception e) { - return new Outcome(e); + return Outcome.FromException(e); } }, context, diff --git a/src/Polly.Core/ResilienceStrategy.cs b/src/Polly.Core/ResilienceStrategy.cs index d2717432d5..6ab5449f12 100644 --- a/src/Polly.Core/ResilienceStrategy.cs +++ b/src/Polly.Core/ResilienceStrategy.cs @@ -58,7 +58,7 @@ public abstract partial class ResilienceStrategy { if (context.CancellationToken.IsCancellationRequested) { - return new ValueTask>(new Outcome(new OperationCanceledException(context.CancellationToken))); + return new ValueTask>(Outcome.FromException(new OperationCanceledException(context.CancellationToken))); } try @@ -73,7 +73,7 @@ public abstract partial class ResilienceStrategy } catch (Exception e) { - return new ValueTask>(new Outcome(e)); + return new ValueTask>(Outcome.FromException(e)); } static async ValueTask> AwaitTask(ValueTask> task, bool continueOnCapturedContext) @@ -84,7 +84,7 @@ static async ValueTask> AwaitTask(ValueTask> task, bool } catch (Exception e) { - return new Outcome(e); + return Outcome.FromException(e); } } } diff --git a/src/Polly.Core/Retry/RetryResilienceStrategy.cs b/src/Polly.Core/Retry/RetryResilienceStrategy.cs index 092f3db58d..6b869424f4 100644 --- a/src/Polly.Core/Retry/RetryResilienceStrategy.cs +++ b/src/Polly.Core/Retry/RetryResilienceStrategy.cs @@ -94,7 +94,7 @@ protected override async ValueTask> ExecuteCallbackAsync(Func } catch (OperationCanceledException e) { - return new Outcome(e); + return Outcome.FromException(e); } } diff --git a/src/Polly.Core/Timeout/TimeoutResilienceStrategy.cs b/src/Polly.Core/Timeout/TimeoutResilienceStrategy.cs index 79153cbb98..a26babb514 100644 --- a/src/Polly.Core/Timeout/TimeoutResilienceStrategy.cs +++ b/src/Polly.Core/Timeout/TimeoutResilienceStrategy.cs @@ -74,7 +74,7 @@ public TimeoutResilienceStrategy(TimeoutStrategyOptions options, TimeProvider ti timeout, e); - return new Outcome(timeoutException.TrySetStackTrace()); + return Outcome.FromException(timeoutException.TrySetStackTrace()); } return outcome; diff --git a/src/Polly.Core/Utils/ReloadableResilienceStrategy.cs b/src/Polly.Core/Utils/ReloadableResilienceStrategy.cs index 0d2d624378..14d4c57ac8 100644 --- a/src/Polly.Core/Utils/ReloadableResilienceStrategy.cs +++ b/src/Polly.Core/Utils/ReloadableResilienceStrategy.cs @@ -58,7 +58,7 @@ private void RegisterOnReload(CancellationToken previousToken) catch (Exception e) { var context = ResilienceContext.Get().Initialize(isSynchronous: true); - var args = new OutcomeArguments(context, new Outcome(e), new ReloadFailedArguments(e)); + var args = new OutcomeArguments(context, Outcome.FromException(e), new ReloadFailedArguments(e)); _telemetry.Report(ReloadFailedEvent, args); ResilienceContext.Return(context); } diff --git a/src/Polly.Core/Utils/ResilienceStrategyPipeline.cs b/src/Polly.Core/Utils/ResilienceStrategyPipeline.cs index 8dcf9930ff..37b34225b0 100644 --- a/src/Polly.Core/Utils/ResilienceStrategyPipeline.cs +++ b/src/Polly.Core/Utils/ResilienceStrategyPipeline.cs @@ -59,7 +59,7 @@ private ResilienceStrategyPipeline(ResilienceStrategy pipeline, IReadOnlyList>(new Outcome(new OperationCanceledException(context.CancellationToken).TrySetStackTrace())); + return Outcome.FromExceptionAsTask(new OperationCanceledException(context.CancellationToken).TrySetStackTrace()); } return _pipeline.ExecuteCoreAsync(callback, context, state); @@ -86,7 +86,7 @@ private sealed class DelegatingResilienceStrategy : ResilienceStrategy { if (context.CancellationToken.IsCancellationRequested) { - return new ValueTask>(new Outcome(new OperationCanceledException(context.CancellationToken).TrySetStackTrace())); + return Outcome.FromExceptionAsTask(new OperationCanceledException(context.CancellationToken).TrySetStackTrace()); } return state.Next!.ExecuteCoreAsync(state.callback, context, state.state); diff --git a/src/Polly.Core/VoidResult.cs b/src/Polly.Core/VoidResult.cs index 4783abd6b8..4747e1d0d3 100644 --- a/src/Polly.Core/VoidResult.cs +++ b/src/Polly.Core/VoidResult.cs @@ -11,7 +11,5 @@ private VoidResult() public static readonly VoidResult Instance = new(); - public static readonly Outcome Outcome = new(Instance); - public override string ToString() => "void"; } diff --git a/src/Polly.Extensions/Telemetry/TelemetryResilienceStrategy.cs b/src/Polly.Extensions/Telemetry/TelemetryResilienceStrategy.cs index 738fb6b803..d9a5be0db4 100644 --- a/src/Polly.Extensions/Telemetry/TelemetryResilienceStrategy.cs +++ b/src/Polly.Extensions/Telemetry/TelemetryResilienceStrategy.cs @@ -76,8 +76,8 @@ internal sealed class TelemetryResilienceStrategy : ResilienceStrategy } private static Outcome CreateOutcome(Outcome outcome) => outcome.HasResult ? - new Outcome(outcome.Result) : - new Outcome(outcome.Exception!); + Outcome.FromResult(outcome.Result) : + Outcome.FromException(outcome.Exception!); private void RecordDuration(ResilienceContext context, Outcome outcome, TimeSpan duration) { diff --git a/src/Polly.RateLimiting/RateLimiterResilienceStrategy.cs b/src/Polly.RateLimiting/RateLimiterResilienceStrategy.cs index 859cc79138..4e35648d82 100644 --- a/src/Polly.RateLimiting/RateLimiterResilienceStrategy.cs +++ b/src/Polly.RateLimiting/RateLimiterResilienceStrategy.cs @@ -53,6 +53,6 @@ internal sealed class RateLimiterResilienceStrategy : ResilienceStrategy var exception = retryAfter.HasValue ? new RateLimiterRejectedException(retryAfter.Value) : new RateLimiterRejectedException(); - return new Outcome(exception.TrySetStackTrace()); + return Outcome.FromException(exception.TrySetStackTrace()); } } diff --git a/test/Polly.Core.Tests/CircuitBreaker/AdvancedCircuitBreakerOptionsTests.cs b/test/Polly.Core.Tests/CircuitBreaker/AdvancedCircuitBreakerOptionsTests.cs index 2859a9c088..f2a15ea515 100644 --- a/test/Polly.Core.Tests/CircuitBreaker/AdvancedCircuitBreakerOptionsTests.cs +++ b/test/Polly.Core.Tests/CircuitBreaker/AdvancedCircuitBreakerOptionsTests.cs @@ -37,9 +37,9 @@ public async Task ShouldHandle_EnsureDefaults() var args = new CircuitBreakerPredicateArguments(); var context = ResilienceContext.Get(); - (await options.ShouldHandle(new(context, new Outcome("dummy"), args))).Should().Be(false); - (await options.ShouldHandle(new(context, new Outcome(new OperationCanceledException()), args))).Should().Be(false); - (await options.ShouldHandle(new(context, new Outcome(new InvalidOperationException()), args))).Should().Be(true); + (await options.ShouldHandle(new(context, Outcome.FromResult("dummy"), args))).Should().Be(false); + (await options.ShouldHandle(new(context, Outcome.FromException(new OperationCanceledException()), args))).Should().Be(false); + (await options.ShouldHandle(new(context, Outcome.FromException(new InvalidOperationException()), args))).Should().Be(true); } [Fact] diff --git a/test/Polly.Core.Tests/CircuitBreaker/CircuitBreakerStateProviderTests.cs b/test/Polly.Core.Tests/CircuitBreaker/CircuitBreakerStateProviderTests.cs index a858ae6cb1..325e436d14 100644 --- a/test/Polly.Core.Tests/CircuitBreaker/CircuitBreakerStateProviderTests.cs +++ b/test/Polly.Core.Tests/CircuitBreaker/CircuitBreakerStateProviderTests.cs @@ -60,7 +60,7 @@ public void Initialize_Ok() () => { exceptionCalled = true; - return new Outcome(new InvalidOperationException()); + return Outcome.FromException(new InvalidOperationException()); }); provider.CircuitState.Should().Be(CircuitState.HalfOpen); diff --git a/test/Polly.Core.Tests/CircuitBreaker/Controller/CircuitStateControllerTests.cs b/test/Polly.Core.Tests/CircuitBreaker/Controller/CircuitStateControllerTests.cs index eb059000f1..708b975a72 100644 --- a/test/Polly.Core.Tests/CircuitBreaker/Controller/CircuitStateControllerTests.cs +++ b/test/Polly.Core.Tests/CircuitBreaker/Controller/CircuitStateControllerTests.cs @@ -110,8 +110,8 @@ public async Task Disposed_EnsureThrows() await Assert.ThrowsAsync(async () => await controller.CloseCircuitAsync(ResilienceContext.Get())); await Assert.ThrowsAsync(async () => await controller.IsolateCircuitAsync(ResilienceContext.Get())); await Assert.ThrowsAsync(async () => await controller.OnActionPreExecuteAsync(ResilienceContext.Get())); - await Assert.ThrowsAsync(async () => await controller.OnActionSuccessAsync(new Outcome(10), ResilienceContext.Get())); - await Assert.ThrowsAsync(async () => await controller.OnActionFailureAsync(new Outcome(10), ResilienceContext.Get())); + await Assert.ThrowsAsync(async () => await controller.OnActionSuccessAsync(Outcome.FromResult(10), ResilienceContext.Get())); + await Assert.ThrowsAsync(async () => await controller.OnActionFailureAsync(Outcome.FromResult(10), ResilienceContext.Get())); } [Fact] @@ -119,7 +119,7 @@ public async Task OnActionPreExecute_CircuitOpenedByValue() { using var controller = CreateController(); - await OpenCircuit(controller, new Outcome(99)); + await OpenCircuit(controller, Outcome.FromResult(99)); var error = (BrokenCircuitException)(await controller.OnActionPreExecuteAsync(ResilienceContext.Get())).Value.Exception!; error.Should().BeOfType>(); error.Result.Should().Be(99); @@ -150,14 +150,14 @@ public async Task HalfOpen_EnsureCorrectStateTransitionAfterExecution(bool succe _circuitBehavior.Setup(v => v.OnActionSuccess(CircuitState.HalfOpen)); _circuitBehavior.Setup(v => v.OnCircuitClosed()); - await controller.OnActionSuccessAsync(new Outcome(0), ResilienceContext.Get()); + await controller.OnActionSuccessAsync(Outcome.FromResult(0), ResilienceContext.Get()); controller.CircuitState.Should().Be(CircuitState.Closed); } else { var shouldBreak = true; _circuitBehavior.Setup(v => v.OnActionFailure(CircuitState.HalfOpen, out shouldBreak)); - await controller.OnActionFailureAsync(new Outcome(0), ResilienceContext.Get()); + await controller.OnActionFailureAsync(Outcome.FromResult(0), ResilienceContext.Get()); controller.CircuitState.Should().Be(CircuitState.Open); } } @@ -167,7 +167,7 @@ public async Task OnActionPreExecute_CircuitOpenedByException() { using var controller = CreateController(); - await OpenCircuit(controller, new Outcome(new InvalidOperationException())); + await OpenCircuit(controller, Outcome.FromException(new InvalidOperationException())); var error = (BrokenCircuitException)(await controller.OnActionPreExecuteAsync(ResilienceContext.Get())).Value.Exception!; error.InnerException.Should().BeOfType(); } @@ -190,9 +190,9 @@ public async Task OnActionFailure_EnsureLock() using var controller = CreateController(); // act - var executeAction = Task.Run(() => controller.OnActionFailureAsync(new Outcome(0), ResilienceContext.Get())); + var executeAction = Task.Run(() => controller.OnActionFailureAsync(Outcome.FromResult(0), ResilienceContext.Get())); executing.WaitOne(); - var executeAction2 = Task.Run(() => controller.OnActionFailureAsync(new Outcome(0), ResilienceContext.Get())); + var executeAction2 = Task.Run(() => controller.OnActionFailureAsync(Outcome.FromResult(0), ResilienceContext.Get())); // assert executeAction.Wait(50).Should().BeFalse(); @@ -214,7 +214,7 @@ public async Task OnActionPreExecute_HalfOpen() using var controller = CreateController(); - await OpenCircuit(controller, new Outcome(10)); + await OpenCircuit(controller, Outcome.FromResult(10)); AdvanceTime(_options.BreakDuration); // act @@ -253,7 +253,7 @@ public async Task OnActionSuccess_EnsureCorrectBehavior(CircuitState state, Circ } // act - await controller.OnActionSuccessAsync(new Outcome(10), ResilienceContext.Get()); + await controller.OnActionSuccessAsync(Outcome.FromResult(10), ResilienceContext.Get()); // assert controller.CircuitState.Should().Be(expectedState); @@ -295,7 +295,7 @@ public async Task OnActionFailureAsync_EnsureCorrectBehavior(CircuitState state, _circuitBehavior.Setup(v => v.OnActionFailure(state, out shouldBreak)); // act - await controller.OnActionFailureAsync(new Outcome(99), ResilienceContext.Get()); + await controller.OnActionFailureAsync(Outcome.FromResult(99), ResilienceContext.Get()); // assert controller.LastHandledOutcome!.Value.Result.Should().Be(99); @@ -326,7 +326,7 @@ public async Task OnActionFailureAsync_EnsureBreakDurationNotOverflow(bool overf _circuitBehavior.Setup(v => v.OnActionFailure(CircuitState.HalfOpen, out shouldBreak)); // act - await controller.OnActionFailureAsync(new Outcome(99), ResilienceContext.Get()); + await controller.OnActionFailureAsync(Outcome.FromResult(99), ResilienceContext.Get()); // assert var blockedTill = GetBlockedTill(controller); @@ -351,7 +351,7 @@ public async Task OnActionFailureAsync_VoidResult_EnsureBreakingExceptionNotSet( _circuitBehavior.Setup(v => v.OnActionFailure(CircuitState.Open, out shouldBreak)); // act - await controller.OnActionFailureAsync(new Outcome(99), ResilienceContext.Get()); + await controller.OnActionFailureAsync(Outcome.FromResult(99), ResilienceContext.Get()); // assert controller.LastException.Should().BeNull(); @@ -368,7 +368,7 @@ public async Task Flow_Closed_HalfOpen_Closed() _circuitBehavior.Setup(v => v.OnActionSuccess(CircuitState.HalfOpen)); _circuitBehavior.Setup(v => v.OnCircuitClosed()); - await controller.OnActionSuccessAsync(new Outcome(0), ResilienceContext.Get()); + await controller.OnActionSuccessAsync(Outcome.FromResult(0), ResilienceContext.Get()); controller.CircuitState.Should().Be(CircuitState.Closed); } @@ -382,7 +382,7 @@ public async Task Flow_Closed_HalfOpen_Open_HalfOpen_Closed() await TransitionToState(controller, CircuitState.HalfOpen); _circuitBehavior.Setup(v => v.OnActionFailure(CircuitState.HalfOpen, out shouldBreak)); - await controller.OnActionFailureAsync(new Outcome(0), context); + await controller.OnActionFailureAsync(Outcome.FromResult(0), context); controller.CircuitState.Should().Be(CircuitState.Open); // execution rejected @@ -398,7 +398,7 @@ public async Task Flow_Closed_HalfOpen_Open_HalfOpen_Closed() // close circuit _circuitBehavior.Setup(v => v.OnActionSuccess(CircuitState.HalfOpen)); _circuitBehavior.Setup(v => v.OnCircuitClosed()); - await controller.OnActionSuccessAsync(new Outcome(0), ResilienceContext.Get()); + await controller.OnActionSuccessAsync(Outcome.FromResult(0), ResilienceContext.Get()); controller.CircuitState.Should().Be(CircuitState.Closed); } @@ -431,7 +431,7 @@ private async Task OpenCircuit(CircuitStateController controller, Outcome v.OnActionFailure(CircuitState.Closed, out breakCircuit)); - await controller.OnActionFailureAsync(outcome ?? new Outcome(10), ResilienceContext.Get().Initialize(true)); + await controller.OnActionFailureAsync(outcome ?? Outcome.FromResult(10), ResilienceContext.Get().Initialize(true)); } private void AdvanceTime(TimeSpan timespan) => _utcNow += timespan; diff --git a/test/Polly.Core.Tests/CircuitBreaker/SimpleCircuitBreakerOptionsTests.cs b/test/Polly.Core.Tests/CircuitBreaker/SimpleCircuitBreakerOptionsTests.cs index 43de4667c5..7eaac28b43 100644 --- a/test/Polly.Core.Tests/CircuitBreaker/SimpleCircuitBreakerOptionsTests.cs +++ b/test/Polly.Core.Tests/CircuitBreaker/SimpleCircuitBreakerOptionsTests.cs @@ -34,9 +34,9 @@ public async Task ShouldHandle_EnsureDefaults() var args = new CircuitBreakerPredicateArguments(); var context = ResilienceContext.Get(); - (await options.ShouldHandle(new(context, new Outcome(""), args))).Should().Be(false); - (await options.ShouldHandle(new(context, new Outcome(new OperationCanceledException()), args))).Should().Be(false); - (await options.ShouldHandle(new(context, new Outcome(new InvalidOperationException()), args))).Should().Be(true); + (await options.ShouldHandle(new(context, Outcome.FromResult(""), args))).Should().Be(false); + (await options.ShouldHandle(new(context, Outcome.FromException(new OperationCanceledException()), args))).Should().Be(false); + (await options.ShouldHandle(new(context, Outcome.FromException(new InvalidOperationException()), args))).Should().Be(true); } [Fact] diff --git a/test/Polly.Core.Tests/Fallback/FallbackHandlerTests.cs b/test/Polly.Core.Tests/Fallback/FallbackHandlerTests.cs index 246974b537..dc0bb83c23 100644 --- a/test/Polly.Core.Tests/Fallback/FallbackHandlerTests.cs +++ b/test/Polly.Core.Tests/Fallback/FallbackHandlerTests.cs @@ -6,9 +6,9 @@ public class FallbackHandlerTests [Fact] public async Task GenerateAction_Generic_Ok() { - var handler = FallbackHelper.CreateHandler(_ => true, () => "secondary".AsOutcome(), true); + var handler = FallbackHelper.CreateHandler(_ => true, () => Outcome.FromResult("secondary"), true); var context = ResilienceContext.Get(); - var outcome = await handler.GetFallbackOutcomeAsync(new OutcomeArguments(context, new Outcome("primary"), new FallbackPredicateArguments()))!; + var outcome = await handler.GetFallbackOutcomeAsync(new OutcomeArguments(context, Outcome.FromResult("primary"), new FallbackPredicateArguments()))!; outcome.Result.Should().Be("secondary"); } @@ -16,9 +16,9 @@ public async Task GenerateAction_Generic_Ok() [Fact] public async Task GenerateAction_NonGeneric_Ok() { - var handler = FallbackHelper.CreateHandler(_ => true, () => ((object)"secondary").AsOutcome(), false); + var handler = FallbackHelper.CreateHandler(_ => true, () => Outcome.FromResult((object)"secondary"), false); var context = ResilienceContext.Get(); - var outcome = await handler.GetFallbackOutcomeAsync(new OutcomeArguments(context, new Outcome("primary"), new FallbackPredicateArguments()))!; + var outcome = await handler.GetFallbackOutcomeAsync(new OutcomeArguments(context, Outcome.FromResult("primary"), new FallbackPredicateArguments()))!; outcome.Result.Should().Be("secondary"); } diff --git a/test/Polly.Core.Tests/Fallback/FallbackHelper.cs b/test/Polly.Core.Tests/Fallback/FallbackHelper.cs index 332b0b02b5..84c141a872 100644 --- a/test/Polly.Core.Tests/Fallback/FallbackHelper.cs +++ b/test/Polly.Core.Tests/Fallback/FallbackHelper.cs @@ -11,7 +11,7 @@ internal static class FallbackHelper { return new FallbackHandler( args => new ValueTask(shouldHandle(args.Outcome)), - _ => fallback().AsValueTask(), + _ => new ValueTask>(fallback()), isGeneric); } } diff --git a/test/Polly.Core.Tests/Fallback/FallbackResilienceStrategyBuilderExtensionsTests.cs b/test/Polly.Core.Tests/Fallback/FallbackResilienceStrategyBuilderExtensionsTests.cs index 4807014437..fcabf63d22 100644 --- a/test/Polly.Core.Tests/Fallback/FallbackResilienceStrategyBuilderExtensionsTests.cs +++ b/test/Polly.Core.Tests/Fallback/FallbackResilienceStrategyBuilderExtensionsTests.cs @@ -11,13 +11,13 @@ public class FallbackResilienceStrategyBuilderExtensionsTests { builder.AddFallback(new FallbackStrategyOptions { - FallbackAction = _ => 0.AsOutcomeAsync(), + FallbackAction = _ => Outcome.FromResultAsTask(0), ShouldHandle = _ => PredicateResult.False, }); }, builder => { - builder.AddFallback(handle => handle.HandleResult(1), _ => 0.AsOutcomeAsync()); + builder.AddFallback(handle => handle.HandleResult(1), _ => Outcome.FromResultAsTask(0)); }, }; @@ -41,7 +41,7 @@ public void AddFallback_Ok() { Result: -1 } => PredicateResult.True, _ => PredicateResult.False }, - FallbackAction = _ => ((object)1).AsOutcomeAsync() + FallbackAction = _ => Outcome.FromResultAsTask((object)1) }; var strategy = new ResilienceStrategyBuilder().AddFallback(options).Build(); diff --git a/test/Polly.Core.Tests/Fallback/FallbackResilienceStrategyTests.cs b/test/Polly.Core.Tests/Fallback/FallbackResilienceStrategyTests.cs index fb50f634b9..ba59c6f261 100644 --- a/test/Polly.Core.Tests/Fallback/FallbackResilienceStrategyTests.cs +++ b/test/Polly.Core.Tests/Fallback/FallbackResilienceStrategyTests.cs @@ -21,7 +21,7 @@ public void Ctor_Ok() [Fact] public void DoesntHandle_Skips() { - SetHandler(_ => true, () => "dummy".AsOutcome(), true); + SetHandler(_ => true, () => Outcome.FromResult("dummy"), true); Create().Execute(() => -1).Should().Be(-1); @@ -33,7 +33,7 @@ public void Handle_Result_Ok() { var called = false; _options.OnFallback = _ => { called = true; return default; }; - SetHandler(outcome => outcome.Result == "error", () => "success".AsOutcome()); + SetHandler(outcome => outcome.Result == "error", () => Outcome.FromResult("success")); Create().Execute(_ => "error").Should().Be("success"); @@ -54,7 +54,7 @@ public void Handle_Exception_Ok() var called = false; _options.OnFallback = _ => { called = true; return default; }; - SetHandler(outcome => outcome.Exception is InvalidOperationException, () => "secondary".AsOutcome()); + SetHandler(outcome => outcome.Exception is InvalidOperationException, () => Outcome.FromResult("secondary")); Create().Execute(_ => throw new InvalidOperationException()).Should().Be("secondary"); _args.Should().ContainSingle(v => v.Arguments is OnFallbackArguments); @@ -68,7 +68,7 @@ public void Handle_UnhandledException_Ok() var fallbackActionCalled = false; _options.OnFallback = _ => { called = true; return default; }; - SetHandler(outcome => outcome.Exception is InvalidOperationException, () => { fallbackActionCalled = true; return "secondary".AsOutcome(); }); + SetHandler(outcome => outcome.Exception is InvalidOperationException, () => { fallbackActionCalled = true; return Outcome.FromResult("secondary"); }); Create().Invoking(s => s.Execute(_ => throw new ArgumentException())).Should().Throw(); @@ -84,7 +84,7 @@ public void Handle_UnhandledResult_Ok() var fallbackActionCalled = false; _options.OnFallback = _ => { called = true; return default; }; - SetHandler(outcome => false, () => "secondary".AsOutcome()); + SetHandler(outcome => false, () => Outcome.FromResult("secondary")); Create().Execute(_ => "primary").Should().Be("primary"); _args.Should().BeEmpty(); diff --git a/test/Polly.Core.Tests/Fallback/FallbackStrategyOptionsTests.cs b/test/Polly.Core.Tests/Fallback/FallbackStrategyOptionsTests.cs index 8242d0b99f..fb43c95556 100644 --- a/test/Polly.Core.Tests/Fallback/FallbackStrategyOptionsTests.cs +++ b/test/Polly.Core.Tests/Fallback/FallbackStrategyOptionsTests.cs @@ -24,9 +24,9 @@ public async Task ShouldHandle_EnsureDefaults() var args = new FallbackPredicateArguments(); var context = ResilienceContext.Get(); - (await options.ShouldHandle(new(context, new Outcome(0), args))).Should().Be(false); - (await options.ShouldHandle(new(context, new Outcome(new OperationCanceledException()), args))).Should().Be(false); - (await options.ShouldHandle(new(context, new Outcome(new InvalidOperationException()), args))).Should().Be(true); + (await options.ShouldHandle(new(context, Outcome.FromResult(0), args))).Should().Be(false); + (await options.ShouldHandle(new(context, Outcome.FromException(new OperationCanceledException()), args))).Should().Be(false); + (await options.ShouldHandle(new(context, Outcome.FromException(new InvalidOperationException()), args))).Should().Be(true); } [Fact] diff --git a/test/Polly.Core.Tests/Hedging/Controller/HedgingControllerTests.cs b/test/Polly.Core.Tests/Hedging/Controller/HedgingControllerTests.cs index 0fca74985f..16be56e950 100644 --- a/test/Polly.Core.Tests/Hedging/Controller/HedgingControllerTests.cs +++ b/test/Polly.Core.Tests/Hedging/Controller/HedgingControllerTests.cs @@ -29,7 +29,7 @@ public async Task Pooling_Ok() private static async Task PrepareAsync(HedgingExecutionContext context) { - await context.LoadExecutionAsync((_, _) => new Outcome(10).AsValueTask(), "state"); + await context.LoadExecutionAsync((_, _) => Outcome.FromResultAsTask(10), "state"); await context.TryWaitForCompletedExecutionAsync(System.Threading.Timeout.InfiniteTimeSpan); context.Tasks[0].AcceptOutcome(); } diff --git a/test/Polly.Core.Tests/Hedging/Controller/HedgingExecutionContextTests.cs b/test/Polly.Core.Tests/Hedging/Controller/HedgingExecutionContextTests.cs index 8668936b96..c0a435df3a 100644 --- a/test/Polly.Core.Tests/Hedging/Controller/HedgingExecutionContextTests.cs +++ b/test/Polly.Core.Tests/Hedging/Controller/HedgingExecutionContextTests.cs @@ -93,7 +93,7 @@ public async Task TryWaitForCompletedExecutionAsync_FinishedTask_Ok() { var context = Create(); context.Initialize(_resilienceContext); - await context.LoadExecutionAsync((_, _) => new DisposableResult("dummy").AsOutcomeAsync(), "state"); + await context.LoadExecutionAsync((_, _) => Outcome.FromResultAsTask(new DisposableResult("dummy")), "state"); var task = await context.TryWaitForCompletedExecutionAsync(TimeSpan.Zero); @@ -177,8 +177,8 @@ public async Task TryWaitForCompletedExecutionAsync_TwiceWhenSecondaryGeneratorN var context = Create(); context.Initialize(_resilienceContext); - await context.LoadExecutionAsync((_, _) => new DisposableResult("dummy").AsOutcomeAsync(), "state"); - await context.LoadExecutionAsync((_, _) => new DisposableResult("dummy").AsOutcomeAsync(), "state"); + await context.LoadExecutionAsync((_, _) => Outcome.FromResultAsTask(new DisposableResult("dummy")), "state"); + await context.LoadExecutionAsync((_, _) => Outcome.FromResultAsTask(new DisposableResult("dummy")), "state"); var task = await context.TryWaitForCompletedExecutionAsync(TimeSpan.Zero); @@ -194,7 +194,7 @@ public async Task TryWaitForCompletedExecutionAsync_TwiceWhenSecondaryGeneratorR await LoadExecutionAsync(context); await LoadExecutionAsync(context); - Generator = args => () => new DisposableResult { Name = "secondary" }.AsOutcomeAsync(); + Generator = args => () => Outcome.FromResultAsTask(new DisposableResult { Name = "secondary" }); var task = await context.TryWaitForCompletedExecutionAsync(TimeSpan.Zero); task!.Type.Should().Be(HedgedTaskType.Primary); @@ -440,7 +440,7 @@ private async Task ExecuteAllTasksAsync(HedgingExecutionContext(new DisposableResult { Name = "primary" }); + return Outcome.FromResult(new DisposableResult { Name = "primary" }); }, "state"); } @@ -462,14 +462,14 @@ private void ConfigureSecondaryTasks(params TimeSpan[] delays) { args.ActionContext.Properties.Set(new ResiliencePropertyKey(attempt.ToString(CultureInfo.InvariantCulture)), attempt); await _timeProvider.Delay(delays[attempt], args.ActionContext.CancellationToken); - return new DisposableResult(delays[attempt].ToString()).AsOutcome(); + return Outcome.FromResult(new DisposableResult(delays[attempt].ToString())); }; }; } private Func, Func>>?> Generator { get; set; } = args => { - return () => new DisposableResult { Name = Handled }.AsOutcomeAsync(); + return () => Outcome.FromResultAsTask(new DisposableResult { Name = Handled }); }; private HedgingExecutionContext Create() diff --git a/test/Polly.Core.Tests/Hedging/Controller/TaskExecutionTests.cs b/test/Polly.Core.Tests/Hedging/Controller/TaskExecutionTests.cs index e5232821cd..0e46ae4458 100644 --- a/test/Polly.Core.Tests/Hedging/Controller/TaskExecutionTests.cs +++ b/test/Polly.Core.Tests/Hedging/Controller/TaskExecutionTests.cs @@ -52,7 +52,7 @@ public async Task Initialize_Primary_Ok(string value, bool handled) { AssertPrimaryContext(context, execution); state.Should().Be("dummy-state"); - return new Outcome(new DisposableResult { Name = value }).AsValueTask(); + return Outcome.FromResultAsTask(new DisposableResult { Name = value }); }, "dummy-state", 99); @@ -91,7 +91,7 @@ public async Task Initialize_Secondary_Ok(string value, bool handled) { AssertSecondaryContext(args.ActionContext, execution); args.Attempt.Should().Be(4); - return () => new DisposableResult { Name = value }.AsOutcomeAsync(); + return () => Outcome.FromResultAsTask(new DisposableResult { Name = value }); }; (await execution.InitializeAsync(HedgedTaskType.Secondary, _snapshot, null!, "dummy-state", 4)).Should().BeTrue(); @@ -174,7 +174,7 @@ public async Task Initialize_Cancelled_EnsureRespected(bool primary) return async () => { await _timeProvider.Delay(TimeSpan.FromDays(1), args.ActionContext.CancellationToken); - return new DisposableResult { Name = Handled }.AsOutcome(); + return Outcome.FromResult(new DisposableResult { Name = Handled }); }; }; @@ -186,11 +186,11 @@ public async Task Initialize_Cancelled_EnsureRespected(bool primary) try { await _timeProvider.Delay(TimeSpan.FromDays(1), context.CancellationToken); - return new Outcome(new DisposableResult()); + return Outcome.FromResult(new DisposableResult()); } catch (OperationCanceledException e) { - return new Outcome(e); + return Outcome.FromException(e); } }, "dummy-state", @@ -255,7 +255,7 @@ private async Task InitializePrimaryAsync(TaskExecution execut await execution.InitializeAsync(HedgedTaskType.Primary, _snapshot, (context, _) => { onContext?.Invoke(context); - return new Outcome(result ?? new DisposableResult { Name = Handled }).AsValueTask(); + return Outcome.FromResultAsTask(result ?? new DisposableResult { Name = Handled }); }, "dummy-state", 1); } @@ -296,7 +296,7 @@ private void CreateSnapshot(CancellationToken? token = null) private Func, Func>>?> Generator { get; set; } = args => { - return () => new DisposableResult { Name = Handled }.AsOutcomeAsync(); + return () => Outcome.FromResultAsTask(new DisposableResult { Name = Handled }); }; private TaskExecution Create() => new(_hedgingHandler, CancellationTokenSourcePool.Create(TimeProvider.System), _timeProvider, _telemetry); diff --git a/test/Polly.Core.Tests/Hedging/HedgingActionGeneratorArgumentsTests.cs b/test/Polly.Core.Tests/Hedging/HedgingActionGeneratorArgumentsTests.cs index c934e5748c..332bf75050 100644 --- a/test/Polly.Core.Tests/Hedging/HedgingActionGeneratorArgumentsTests.cs +++ b/test/Polly.Core.Tests/Hedging/HedgingActionGeneratorArgumentsTests.cs @@ -7,7 +7,7 @@ public class HedgingActionGeneratorArgumentsTests [Fact] public void Ctor_Ok() { - var args = new HedgingActionGeneratorArguments(ResilienceContext.Get(), ResilienceContext.Get(), 5, _ => "dummy".AsOutcomeAsync()); + var args = new HedgingActionGeneratorArguments(ResilienceContext.Get(), ResilienceContext.Get(), 5, _ => Outcome.FromResultAsTask("dummy")); args.PrimaryContext.Should().NotBeNull(); args.ActionContext.Should().NotBeNull(); diff --git a/test/Polly.Core.Tests/Hedging/HedgingActions.cs b/test/Polly.Core.Tests/Hedging/HedgingActions.cs index 08342c4c59..f1cbea46a9 100644 --- a/test/Polly.Core.Tests/Hedging/HedgingActions.cs +++ b/test/Polly.Core.Tests/Hedging/HedgingActions.cs @@ -41,19 +41,19 @@ public HedgingActions(TimeProvider timeProvider) private async ValueTask> GetApples(ResilienceContext context) { await _timeProvider.Delay(TimeSpan.FromSeconds(10), context.CancellationToken); - return "Apples".AsOutcome(); + return Outcome.FromResult("Apples"); } private async ValueTask> GetPears(ResilienceContext context) { await _timeProvider.Delay(TimeSpan.FromSeconds(3), context.CancellationToken); - return "Pears".AsOutcome(); + return Outcome.FromResult("Pears"); } private async ValueTask> GetOranges(ResilienceContext context) { await _timeProvider.Delay(TimeSpan.FromSeconds(2), context.CancellationToken); - return "Oranges".AsOutcome(); + return Outcome.FromResult("Oranges"); } public static Func, Func>>?> GetGenerator(Func>> task) diff --git a/test/Polly.Core.Tests/Hedging/HedgingHandlerTests.cs b/test/Polly.Core.Tests/Hedging/HedgingHandlerTests.cs index ce75d09e56..2fffc3ce8d 100644 --- a/test/Polly.Core.Tests/Hedging/HedgingHandlerTests.cs +++ b/test/Polly.Core.Tests/Hedging/HedgingHandlerTests.cs @@ -11,10 +11,10 @@ public async Task GenerateAction_Generic_Ok() { var handler = new HedgingHandler( args => PredicateResult.True, - args => () => "ok".AsOutcomeAsync(), + args => () => Outcome.FromResultAsTask("ok"), true); - var action = handler.GenerateAction(new HedgingActionGeneratorArguments(ResilienceContext.Get(), ResilienceContext.Get(), 0, _ => "primary".AsOutcomeAsync()))!; + var action = handler.GenerateAction(new HedgingActionGeneratorArguments(ResilienceContext.Get(), ResilienceContext.Get(), 0, _ => Outcome.FromResultAsTask("primary")))!; var res = await action(); res.Result.Should().Be("ok"); @@ -34,11 +34,11 @@ public async Task GenerateAction_NonGeneric_Ok(bool nullAction) return null; } - return () => ((object)"ok").AsOutcomeAsync(); + return () => Outcome.FromResultAsTask((object)"ok"); }, false); - var action = handler.GenerateAction(new HedgingActionGeneratorArguments(ResilienceContext.Get(), ResilienceContext.Get(), 0, _ => ((object)"primary").AsOutcomeAsync()))!; + var action = handler.GenerateAction(new HedgingActionGeneratorArguments(ResilienceContext.Get(), ResilienceContext.Get(), 0, _ => Outcome.FromResultAsTask((object)"primary")))!; if (nullAction) { action.Should().BeNull(); @@ -58,7 +58,7 @@ public async Task GenerateAction_NonGeneric_FromCallback() args => () => args.Callback(args.ActionContext), false); - var action = handler.GenerateAction(new HedgingActionGeneratorArguments(ResilienceContext.Get(), ResilienceContext.Get(), 0, _ => ((object)"callback").AsOutcomeAsync()))!; + var action = handler.GenerateAction(new HedgingActionGeneratorArguments(ResilienceContext.Get(), ResilienceContext.Get(), 0, _ => Outcome.FromResultAsTask((object)"callback")))!; var res = await action(); res.Result.Should().Be("callback"); } diff --git a/test/Polly.Core.Tests/Hedging/HedgingResilienceStrategyBuilderExtensionsTests.cs b/test/Polly.Core.Tests/Hedging/HedgingResilienceStrategyBuilderExtensionsTests.cs index 8c79ccdbdf..3aa5beb0ce 100644 --- a/test/Polly.Core.Tests/Hedging/HedgingResilienceStrategyBuilderExtensionsTests.cs +++ b/test/Polly.Core.Tests/Hedging/HedgingResilienceStrategyBuilderExtensionsTests.cs @@ -20,7 +20,7 @@ public void AddHedging_Generic_Ok() { _genericBuilder.AddHedging(new HedgingStrategyOptions { - HedgingActionGenerator = args => () => "dummy".AsOutcomeAsync(), + HedgingActionGenerator = args => () => Outcome.FromResultAsTask("dummy"), ShouldHandle = _ => PredicateResult.True }); _genericBuilder.Build().Strategy.Should().BeOfType>(); @@ -73,10 +73,10 @@ public async Task AddHedging_IntegrationTest() if (args.Attempt == 3) { - return "success".AsOutcome().AsOutcome(); + return Outcome.FromResult((object)"success"); } - return "error".AsOutcome().AsOutcome(); + return Outcome.FromResult((object)"error"); }; }, OnHedging = args => diff --git a/test/Polly.Core.Tests/Hedging/HedgingResilienceStrategyTests.cs b/test/Polly.Core.Tests/Hedging/HedgingResilienceStrategyTests.cs index 461ffc3592..6bac6b7346 100644 --- a/test/Polly.Core.Tests/Hedging/HedgingResilienceStrategyTests.cs +++ b/test/Polly.Core.Tests/Hedging/HedgingResilienceStrategyTests.cs @@ -74,7 +74,7 @@ public async Task Execute_CancellationRequested_Throws() var context = ResilienceContext.Get(); context.CancellationToken = _cts.Token; - var outcome = await strategy.ExecuteOutcomeAsync((_, _) => "dummy".AsOutcomeAsync(), context, "state"); + var outcome = await strategy.ExecuteOutcomeAsync((_, _) => Outcome.FromResultAsTask("dummy"), context, "state"); outcome.Exception.Should().BeOfType(); outcome.Exception!.StackTrace.Should().Contain("Execute_CancellationRequested_Throws"); } @@ -89,7 +89,7 @@ public void ExecutePrimaryAndSecondary_EnsureAttemptReported() return timeStamp; }; _options.MaxHedgedAttempts = 2; - ConfigureHedging(_ => true, args => () => "any".AsOutcomeAsync()); + ConfigureHedging(_ => true, args => () => Outcome.FromResultAsTask("any")); var strategy = Create(); strategy.Execute(_ => "dummy"); @@ -173,7 +173,7 @@ public async Task ExecuteAsync_EnsurePrimaryContextFlows() { args.PrimaryContext.Properties.GetValue(key, string.Empty).Should().Be("dummy"); args.PrimaryContext.Should().Be(primaryContext); - return () => Failure.AsOutcomeAsync(); + return () => Outcome.FromResultAsTask(Failure); }); var strategy = Create(); @@ -211,7 +211,7 @@ public async void ExecuteAsync_EnsureHedgedTasksCancelled_Ok() } #pragma warning restore CA1031 // Do not catch general exception types - return Failure.AsOutcome(); + return Outcome.FromResult(Failure); }); var strategy = Create(); @@ -242,7 +242,7 @@ public async Task ExecuteAsync_EnsurePrimaryTaskCancelled_Ok() ConfigureHedging(async context => { await _timeProvider.Delay(TimeSpan.FromHours(1), context.CancellationToken); - return Success.AsOutcome(); + return Outcome.FromResult(Success); }); var strategy = Create(); @@ -280,7 +280,7 @@ public async Task ExecuteAsync_EnsureSecondaryHedgedTaskReportedWithNoOutcome() return default; }; - ConfigureHedging(context => Success.AsOutcomeAsync()); + ConfigureHedging(context => Outcome.FromResultAsTask(Success)); var strategy = Create(); @@ -308,7 +308,7 @@ public async Task ExecuteAsync_EnsureDiscardedResultDisposed() { return () => { - return secondaryResult.AsOutcomeAsync(); + return Outcome.FromResultAsTask(secondaryResult); }; }); @@ -356,7 +356,7 @@ public async Task ExecuteAsync_EveryHedgedTaskShouldHaveDifferentContexts() contexts.Add(args.ActionContext); await Task.Yield(); args.ActionContext.Properties.Set(afterKey, "after"); - return "secondary".AsOutcome(); + return Outcome.FromResult("secondary"); }; }); @@ -425,7 +425,7 @@ public async Task ExecuteAsync_EnsurePropertiesConsistency(bool primaryFails) args.ActionContext.Properties.GetValue(primaryKey, string.Empty).Should().Be("primary"); args.ActionContext.Properties.Set(secondaryKey, "secondary"); await _timeProvider.Delay(TimeSpan.FromHours(1), args.ActionContext.CancellationToken); - return (primaryFails ? Success : Failure).AsOutcome(); + return Outcome.FromResult(primaryFails ? Success : Failure); }; }); var strategy = Create(); @@ -512,7 +512,7 @@ public async Task ExecuteAsync_Secondary_CustomPropertiesAvailable() args.ActionContext.Properties.TryGetValue(key2, out var val).Should().BeTrue(); val.Should().Be("my-value-2"); args.ActionContext.Properties.Set(key, "my-value"); - return Success.AsOutcomeAsync(); + return Outcome.FromResultAsTask(Success); }; }); var strategy = Create(); @@ -531,7 +531,7 @@ public async Task ExecuteAsync_Secondary_CustomPropertiesAvailable() public async Task ExecuteAsync_OnHedgingEventThrows_EnsureExceptionRethrown() { // arrange - ConfigureHedging(args => () => Success.AsOutcomeAsync()); + ConfigureHedging(args => () => Outcome.FromResultAsTask(Success)); _options.OnHedging = _ => throw new InvalidOperationException("my-exception"); var strategy = Create(); @@ -563,10 +563,10 @@ public async Task ExecuteAsync_CancellationLinking_Ok() catch (OperationCanceledException e) { secondaryCancelled.Set(); - return e.AsOutcome(); + return Outcome.FromException(e); } - return Success.AsOutcome(); + return Outcome.FromResult(Success); }); var strategy = Create(); @@ -633,7 +633,7 @@ ValueTask> BackgroundWork(ResilienceContext resilienceContext) { var delay = Task.Delay(TimeSpan.FromDays(24), resilienceContext.CancellationToken); backgroundTasks.Add(delay); - return Success.AsOutcomeAsync(); + return Outcome.FromResultAsTask(Success); } } @@ -662,7 +662,7 @@ async ValueTask> Execute(CancellationToken token) } await _timeProvider.Delay(LongDelay, token); - return Success.AsOutcome(); + return Outcome.FromResult(Success); } } @@ -685,7 +685,7 @@ async ValueTask> Execute(CancellationToken token) { if (executing) { - return new Outcome(new InvalidOperationException("Concurrent execution detected!")); + return Outcome.FromException(new InvalidOperationException("Concurrent execution detected!")); } executing = true; @@ -698,7 +698,7 @@ async ValueTask> Execute(CancellationToken token) await _timeProvider.Delay(LongDelay, token); - return "dummy".AsOutcome(); + return Outcome.FromResult("dummy"); } finally { @@ -815,10 +815,10 @@ public async Task ExecuteAsync_ExceptionsHandled_ShouldReturnLastResult() { if (exception != null) { - return exception.AsOutcomeAsync(); + return Outcome.FromExceptionAsTask(exception); } - return Success.AsOutcomeAsync(); + return Outcome.FromResultAsTask(Success); }; }); @@ -833,7 +833,7 @@ public async Task ExecuteAsync_EnsureHedgingDelayGeneratorRespected() var delay = TimeSpan.FromMilliseconds(12345); _options.HedgingDelayGenerator = _ => new ValueTask(TimeSpan.FromMilliseconds(12345)); - ConfigureHedging(res => false, args => () => Success.AsOutcomeAsync()); + ConfigureHedging(res => false, args => () => Outcome.FromResultAsTask(Success)); var strategy = Create(); var task = strategy.ExecuteAsync(async token => @@ -874,7 +874,7 @@ public async Task ExecuteAsync_EnsureOnHedgingCalled() return default; }; - ConfigureHedging(res => res.Result == Failure, args => () => Failure.AsOutcomeAsync()); + ConfigureHedging(res => res.Result == Failure, args => () => Outcome.FromResultAsTask(Failure)); var strategy = Create(); await strategy.ExecuteAsync(_ => new ValueTask(Failure)); @@ -889,7 +889,7 @@ public async Task ExecuteAsync_EnsureOnHedgingTelemetry() { var context = ResilienceContext.Get(); - ConfigureHedging(res => res.Result == Failure, args => () => Failure.AsOutcomeAsync()); + ConfigureHedging(res => res.Result == Failure, args => () => Outcome.FromResultAsTask(Failure)); var strategy = Create(); await strategy.ExecuteAsync((_, _) => new ValueTask(Failure), context, "state"); @@ -933,7 +933,7 @@ private void ConfigureHedging(Func, Func private void ConfigureHedging(TimeSpan delay) => ConfigureHedging(args => async () => { await Task.Delay(delay); - return "secondary".AsOutcome(); + return Outcome.FromResult("secondary"); }); private HedgingResilienceStrategy Create() => Create(_handler!, _options.OnHedging); diff --git a/test/Polly.Core.Tests/Hedging/HedgingStrategyOptionsTests.cs b/test/Polly.Core.Tests/Hedging/HedgingStrategyOptionsTests.cs index b8bbbe67c1..0965e710f6 100644 --- a/test/Polly.Core.Tests/Hedging/HedgingStrategyOptionsTests.cs +++ b/test/Polly.Core.Tests/Hedging/HedgingStrategyOptionsTests.cs @@ -39,7 +39,7 @@ public async Task HedgingActionGenerator_EnsureDefaults(bool synchronous) Thread.CurrentThread.ManagedThreadId.Should().Be(threadId); } - return 99.AsOutcomeAsync(); + return Outcome.FromResultAsTask(99); }))!; action.Should().NotBeNull(); @@ -53,9 +53,9 @@ public async Task ShouldHandle_EnsureDefaults() var args = new HedgingPredicateArguments(); var context = ResilienceContext.Get(); - (await options.ShouldHandle(new(context, new Outcome(0), args))).Should().Be(false); - (await options.ShouldHandle(new(context, new Outcome(new OperationCanceledException()), args))).Should().Be(false); - (await options.ShouldHandle(new(context, new Outcome(new InvalidOperationException()), args))).Should().Be(true); + (await options.ShouldHandle(new(context, Outcome.FromResult(0), args))).Should().Be(false); + (await options.ShouldHandle(new(context, Outcome.FromException(new OperationCanceledException()), args))).Should().Be(false); + (await options.ShouldHandle(new(context, Outcome.FromException(new InvalidOperationException()), args))).Should().Be(true); } [Fact] diff --git a/test/Polly.Core.Tests/OutcomeArgumentsTests.cs b/test/Polly.Core.Tests/OutcomeArgumentsTests.cs index d26a431f52..c65f18f9ed 100644 --- a/test/Polly.Core.Tests/OutcomeArgumentsTests.cs +++ b/test/Polly.Core.Tests/OutcomeArgumentsTests.cs @@ -7,7 +7,7 @@ public void Ctor_Result_Ok() { var args = new OutcomeArguments( ResilienceContext.Get(), - new Outcome("dummy"), + Outcome.FromResult("dummy"), "args"); args.Context.Should().NotBeNull(); @@ -22,7 +22,7 @@ public void Ctor_Exception_Ok() { var args = new OutcomeArguments( ResilienceContext.Get(), - new Outcome(new InvalidOperationException()), + Outcome.FromException(new InvalidOperationException()), "args"); args.Context.Should().NotBeNull(); diff --git a/test/Polly.Core.Tests/OutcomeTests.cs b/test/Polly.Core.Tests/OutcomeTests.cs index 64bee9c8d0..e0e3af20bb 100644 --- a/test/Polly.Core.Tests/OutcomeTests.cs +++ b/test/Polly.Core.Tests/OutcomeTests.cs @@ -4,7 +4,7 @@ public class OutcomeTests [Fact] public void Ctor_Result_Ok() { - var outcome = new Outcome(10); + var outcome = Outcome.FromResult(10); outcome.HasResult.Should().BeTrue(); outcome.Exception.Should().BeNull(); outcome.ExceptionDispatchInfo.Should().BeNull(); @@ -23,7 +23,7 @@ public void Ctor_Result_Ok() [Fact] public void Ctor_VoidResult_Ok() { - var outcome = new Outcome(VoidResult.Instance); + var outcome = Outcome.Void; outcome.HasResult.Should().BeTrue(); outcome.Exception.Should().BeNull(); outcome.IsVoidResult.Should().BeTrue(); @@ -41,7 +41,7 @@ public void Ctor_VoidResult_Ok() [Fact] public void Ctor_Exception_Ok() { - var outcome = new Outcome(new InvalidOperationException("Dummy message.")); + var outcome = Outcome.FromException(new InvalidOperationException("Dummy message.")); outcome.HasResult.Should().BeFalse(); outcome.Exception.Should().NotBeNull(); outcome.ExceptionDispatchInfo.Should().NotBeNull(); @@ -59,14 +59,14 @@ public void Ctor_Exception_Ok() [Fact] public void ToString_NullResult_ShouldBeEmpty() { - var outcome = new Outcome((object)null!); + var outcome = Outcome.FromResult(default); outcome.ToString().Should().BeEmpty(); } [Fact] public void EnsureSuccess_Result() { - var outcome = new Outcome("dummy"); + var outcome = Outcome.FromResult("dummy"); outcome.Invoking(o => o.EnsureSuccess()).Should().NotThrow(); } @@ -74,7 +74,7 @@ public void EnsureSuccess_Result() [Fact] public void EnsureSuccess_Exception() { - var outcome = new Outcome(new InvalidOperationException()); + var outcome = Outcome.FromException(new InvalidOperationException()); outcome.Invoking(o => o.EnsureSuccess()).Should().Throw(); } diff --git a/test/Polly.Core.Tests/PredicateBuilderTests.cs b/test/Polly.Core.Tests/PredicateBuilderTests.cs index eb74eddb65..26d81010ba 100644 --- a/test/Polly.Core.Tests/PredicateBuilderTests.cs +++ b/test/Polly.Core.Tests/PredicateBuilderTests.cs @@ -6,24 +6,24 @@ public class PredicateBuilderTests { public static TheoryData>, Outcome, bool> HandleResultData = new() { - { builder => builder.HandleResult("val"), new Outcome("val"), true }, - { builder => builder.HandleResult("val"), new Outcome("val2"), false }, - { builder => builder.HandleResult("val"), new Outcome(new InvalidOperationException()), false }, - { builder => builder.HandleResult("val", StringComparer.OrdinalIgnoreCase) ,new Outcome("VAL"), true }, - { builder => builder.HandleResult(r => r == "val"), new Outcome("val"), true }, - { builder => builder.HandleResult(r => r == "val2"), new Outcome("val"), false }, - { builder => builder.Handle(), new Outcome(new InvalidOperationException()), true }, - { builder => builder.Handle(), new Outcome(new FormatException()), false }, - { builder => builder.Handle(e => false), new Outcome(new InvalidOperationException()), false }, - { builder => builder.HandleInner(e => false), new Outcome(new InvalidOperationException()), false }, - { builder => builder.HandleInner(), new Outcome("value"), false }, - { builder => builder.Handle(), new Outcome("value"), false }, - { builder => builder.Handle().HandleResult("value"), new Outcome("value"), true }, - { builder => builder.Handle().HandleResult("value"), new Outcome("value2"), false }, - { builder => builder.HandleInner(), new Outcome(new InvalidOperationException("dummy", new FormatException() )), true }, - { builder => builder.HandleInner(e => false), new Outcome(new InvalidOperationException("dummy", new FormatException() )), false }, - { builder => builder.HandleInner(e => e.Message == "m"), new Outcome(new InvalidOperationException("dummy", new FormatException("m") )), true }, - { builder => builder.HandleInner(e => e.Message == "x"), new Outcome(new InvalidOperationException("dummy", new FormatException("m") )), false }, + { builder => builder.HandleResult("val"), Outcome.FromResult("val"), true }, + { builder => builder.HandleResult("val"), Outcome.FromResult("val2"), false }, + { builder => builder.HandleResult("val"), Outcome.FromException(new InvalidOperationException()), false }, + { builder => builder.HandleResult("val", StringComparer.OrdinalIgnoreCase) ,Outcome.FromResult("VAL"), true }, + { builder => builder.HandleResult(r => r == "val"), Outcome.FromResult("val"), true }, + { builder => builder.HandleResult(r => r == "val2"), Outcome.FromResult("val"), false }, + { builder => builder.Handle(), Outcome.FromException(new InvalidOperationException()), true }, + { builder => builder.Handle(), Outcome.FromException(new FormatException()), false }, + { builder => builder.Handle(e => false), Outcome.FromException(new InvalidOperationException()), false }, + { builder => builder.HandleInner(e => false), Outcome.FromException(new InvalidOperationException()), false }, + { builder => builder.HandleInner(), Outcome.FromResult("value"), false }, + { builder => builder.Handle(), Outcome.FromResult("value"), false }, + { builder => builder.Handle().HandleResult("value"), Outcome.FromResult("value"), true }, + { builder => builder.Handle().HandleResult("value"), Outcome.FromResult("value2"), false }, + { builder => builder.HandleInner(), Outcome.FromException(new InvalidOperationException("dummy", new FormatException() )), true }, + { builder => builder.HandleInner(e => false), Outcome.FromException(new InvalidOperationException("dummy", new FormatException() )), false }, + { builder => builder.HandleInner(e => e.Message == "m"), Outcome.FromException(new InvalidOperationException("dummy", new FormatException("m") )), true }, + { builder => builder.HandleInner(e => e.Message == "x"), Outcome.FromException(new InvalidOperationException("dummy", new FormatException("m") )), false }, }; [MemberData(nameof(HandleResultData))] diff --git a/test/Polly.Core.Tests/ResilienceStrategyTests.Async.ValueTaskT.cs b/test/Polly.Core.Tests/ResilienceStrategyTests.Async.ValueTaskT.cs index a563d09a3b..5d00ef0b44 100644 --- a/test/Polly.Core.Tests/ResilienceStrategyTests.Async.ValueTaskT.cs +++ b/test/Polly.Core.Tests/ResilienceStrategyTests.Async.ValueTaskT.cs @@ -122,7 +122,7 @@ public async Task ExecuteOutcomeAsync_Ok() state.Should().Be("state"); context.IsSynchronous.Should().BeFalse(); context.ResultType.Should().Be(typeof(int)); - return new Outcome(12345).AsValueTask(); + return Outcome.FromResultAsTask(12345); }, ResilienceContext.Get(), "state"); diff --git a/test/Polly.Core.Tests/ResilienceStrategyTests.TResult.Async.cs b/test/Polly.Core.Tests/ResilienceStrategyTests.TResult.Async.cs index 776b342e7e..29c635bec9 100644 --- a/test/Polly.Core.Tests/ResilienceStrategyTests.TResult.Async.cs +++ b/test/Polly.Core.Tests/ResilienceStrategyTests.TResult.Async.cs @@ -84,7 +84,7 @@ public async Task ExecuteOutcomeAsync_GenericStrategy_Ok() state.Should().Be("state"); context.IsSynchronous.Should().BeFalse(); context.ResultType.Should().Be(typeof(int)); - return new Outcome(12345).AsValueTask(); + return Outcome.FromResultAsTask(12345); }, ResilienceContext.Get(), "state"); diff --git a/test/Polly.Core.Tests/Retry/RetryResilienceStrategyTests.cs b/test/Polly.Core.Tests/Retry/RetryResilienceStrategyTests.cs index 8a6cbcbc35..bb59f0e31d 100644 --- a/test/Polly.Core.Tests/Retry/RetryResilienceStrategyTests.cs +++ b/test/Polly.Core.Tests/Retry/RetryResilienceStrategyTests.cs @@ -41,7 +41,7 @@ public async Task ExecuteAsync_CancellationRequested_EnsureNotRetried() context.CancellationToken = cancellationToken.Token; var executed = false; - var result = await sut.ExecuteOutcomeAsync((_, _) => { executed = true; return "dummy".AsOutcomeAsync(); }, context, "state"); + var result = await sut.ExecuteOutcomeAsync((_, _) => { executed = true; return Outcome.FromResultAsTask("dummy"); }, context, "state"); result.Exception.Should().BeOfType(); executed.Should().BeFalse(); } @@ -63,7 +63,7 @@ public async Task ExecuteAsync_CancellationRequestedAfterCallback_EnsureNotRetri context.CancellationToken = cancellationToken.Token; var executed = false; - var result = await sut.ExecuteOutcomeAsync((_, _) => { executed = true; return "dummy".AsOutcomeAsync(); }, context, "state"); + var result = await sut.ExecuteOutcomeAsync((_, _) => { executed = true; return Outcome.FromResultAsTask("dummy"); }, context, "state"); result.Exception.Should().BeOfType(); executed.Should().BeTrue(); } diff --git a/test/Polly.Core.Tests/Retry/RetryStrategyOptionsTests.cs b/test/Polly.Core.Tests/Retry/RetryStrategyOptionsTests.cs index fa958f1cb2..73d460efba 100644 --- a/test/Polly.Core.Tests/Retry/RetryStrategyOptionsTests.cs +++ b/test/Polly.Core.Tests/Retry/RetryStrategyOptionsTests.cs @@ -30,9 +30,9 @@ public async Task ShouldHandle_EnsureDefaults() var args = new RetryPredicateArguments(0); var context = ResilienceContext.Get(); - (await options.ShouldHandle(new(context, new Outcome(0), args))).Should().Be(false); - (await options.ShouldHandle(new(context, new Outcome(new OperationCanceledException()), args))).Should().Be(false); - (await options.ShouldHandle(new(context, new Outcome(new InvalidOperationException()), args))).Should().Be(true); + (await options.ShouldHandle(new(context, Outcome.FromResult(0), args))).Should().Be(false); + (await options.ShouldHandle(new(context, Outcome.FromException(new OperationCanceledException()), args))).Should().Be(false); + (await options.ShouldHandle(new(context, Outcome.FromException(new InvalidOperationException()), args))).Should().Be(true); } [Fact] diff --git a/test/Polly.Core.Tests/Telemetry/ResilienceStrategyTelemetryTests.cs b/test/Polly.Core.Tests/Telemetry/ResilienceStrategyTelemetryTests.cs index bce36341bb..ee53c762c1 100644 --- a/test/Polly.Core.Tests/Telemetry/ResilienceStrategyTelemetryTests.cs +++ b/test/Polly.Core.Tests/Telemetry/ResilienceStrategyTelemetryTests.cs @@ -67,7 +67,7 @@ public void ResilienceStrategyTelemetry_NoDiagnosticSource_Ok() var context = ResilienceContext.Get(); sut.Invoking(s => s.Report("dummy", context, new TestArguments())).Should().NotThrow(); - sut.Invoking(s => s.Report("dummy", new OutcomeArguments(context, new Outcome(1), new TestArguments()))).Should().NotThrow(); + sut.Invoking(s => s.Report("dummy", new OutcomeArguments(context, Outcome.FromResult(1), new TestArguments()))).Should().NotThrow(); } [Fact] @@ -93,7 +93,7 @@ public void Report_Outcome_OK() }); var context = ResilienceContext.Get(); - _sut.Report("dummy-event", new OutcomeArguments(context, new Outcome(99), new TestArguments())); + _sut.Report("dummy-event", new OutcomeArguments(context, Outcome.FromResult(99), new TestArguments())); _diagnosticSource.VerifyAll(); } @@ -103,7 +103,7 @@ public void Report_OutcomeWhenNotSubscribed_None() { _diagnosticSource.Setup(o => o.IsEnabled("dummy-event")).Returns(false); var context = ResilienceContext.Get(); - _sut.Report("dummy-event", new OutcomeArguments(context, new Outcome(10), new TestArguments())); + _sut.Report("dummy-event", new OutcomeArguments(context, Outcome.FromResult(10), new TestArguments())); _diagnosticSource.VerifyAll(); _diagnosticSource.VerifyNoOtherCalls(); diff --git a/test/Polly.Core.Tests/Telemetry/TelemetryEventArgumentsTests.cs b/test/Polly.Core.Tests/Telemetry/TelemetryEventArgumentsTests.cs index a75aa1cdb0..0334a82417 100644 --- a/test/Polly.Core.Tests/Telemetry/TelemetryEventArgumentsTests.cs +++ b/test/Polly.Core.Tests/Telemetry/TelemetryEventArgumentsTests.cs @@ -11,7 +11,7 @@ public class TelemetryEventArgumentsTests public void Get_Ok() { var context = ResilienceContext.Get(); - var args = TelemetryEventArguments.Get(_source, "ev", context, new Outcome("dummy"), "arg"); + var args = TelemetryEventArguments.Get(_source, "ev", context, Outcome.FromResult("dummy"), "arg"); args.Outcome!.Value.Result.Should().Be("dummy"); args.Context.Should().Be(context); @@ -25,15 +25,18 @@ public void Get_Ok() public void Return_EnsurePropertiesCleared() { var context = ResilienceContext.Get(); - var args = TelemetryEventArguments.Get(_source, "ev", context, new Outcome("dummy"), "arg"); + var args = TelemetryEventArguments.Get(_source, "ev", context, Outcome.FromResult("dummy"), "arg"); TelemetryEventArguments.Return(args); - args.Outcome.Should().BeNull(); - args.Context.Should().BeNull(); - args.EventName.Should().BeNull(); - args.Source.Should().BeNull(); - args.Arguments.Should().BeNull(); - args.Context.Should().BeNull(); + TestUtilities.AssertWithTimeoutAsync(() => + { + args.Outcome.Should().BeNull(); + args.Context.Should().BeNull(); + args.EventName.Should().BeNull(); + args.Source.Should().BeNull(); + args.Arguments.Should().BeNull(); + args.Context.Should().BeNull(); + }); } } diff --git a/test/Polly.Core.Tests/Timeout/TimeoutResilienceStrategyTests.cs b/test/Polly.Core.Tests/Timeout/TimeoutResilienceStrategyTests.cs index 9efcece2ae..7d82e679ae 100644 --- a/test/Polly.Core.Tests/Timeout/TimeoutResilienceStrategyTests.cs +++ b/test/Polly.Core.Tests/Timeout/TimeoutResilienceStrategyTests.cs @@ -113,7 +113,7 @@ public async Task Execute_Timeout_EnsureStackTrace() _timeProvider.SetupCancelAfterNow(TimeSpan.FromSeconds(2)); var sut = CreateSut(); - var outcome = await sut.ExecuteOutcomeAsync(async (c, _) => { await Delay(c.CancellationToken); return "dummy".AsOutcome(); }, ResilienceContext.Get(), "state"); + var outcome = await sut.ExecuteOutcomeAsync(async (c, _) => { await Delay(c.CancellationToken); return Outcome.FromResult("dummy"); }, ResilienceContext.Get(), "state"); outcome.Exception.Should().BeOfType(); outcome.Exception!.StackTrace.Should().Contain("Execute_Timeout_EnsureStackTrace"); } diff --git a/test/Polly.Core.Tests/Utils/ResilienceStrategyPipelineTests.cs b/test/Polly.Core.Tests/Utils/ResilienceStrategyPipelineTests.cs index b509400d48..153e64a34a 100644 --- a/test/Polly.Core.Tests/Utils/ResilienceStrategyPipelineTests.cs +++ b/test/Polly.Core.Tests/Utils/ResilienceStrategyPipelineTests.cs @@ -48,7 +48,7 @@ public async Task CreatePipeline_EnsureExceptionsNotWrapped() var pipeline = ResilienceStrategyPipeline.CreatePipeline(strategies); await pipeline - .Invoking(p => p.ExecuteCoreAsync((_, _) => new Outcome(10).AsValueTask(), ResilienceContext.Get(), "state").AsTask()) + .Invoking(p => p.ExecuteCoreAsync((_, _) => Outcome.FromResultAsTask(10), ResilienceContext.Get(), "state").AsTask()) .Should() .ThrowAsync(); } @@ -87,7 +87,7 @@ public async Task CreatePipeline_Cancelled_EnsureNoExecution() var context = ResilienceContext.Get(); context.CancellationToken = cancellation.Token; - var result = await pipeline.ExecuteOutcomeAsync((_, _) => "result".AsOutcomeAsync(), context, "state"); + var result = await pipeline.ExecuteOutcomeAsync((_, _) => Outcome.FromResultAsTask("result"), context, "state"); result.Exception.Should().BeOfType(); } @@ -106,7 +106,7 @@ public async Task CreatePipeline_CancelledLater_EnsureNoExecution() var context = ResilienceContext.Get(); context.CancellationToken = cancellation.Token; - var result = await pipeline.ExecuteOutcomeAsync((_, _) => "result".AsOutcomeAsync(), context, "state"); + var result = await pipeline.ExecuteOutcomeAsync((_, _) => Outcome.FromResultAsTask("result"), context, "state"); result.Exception.Should().BeOfType(); executed.Should().BeTrue(); } diff --git a/test/Polly.Extensions.Tests/DependencyInjection/PollyServiceCollectionExtensionTests.cs b/test/Polly.Extensions.Tests/DependencyInjection/PollyServiceCollectionExtensionTests.cs index b63246bb47..b693221931 100644 --- a/test/Polly.Extensions.Tests/DependencyInjection/PollyServiceCollectionExtensionTests.cs +++ b/test/Polly.Extensions.Tests/DependencyInjection/PollyServiceCollectionExtensionTests.cs @@ -287,6 +287,6 @@ private class TestStrategy : ResilienceStrategy protected override ValueTask> ExecuteCoreAsync( Func>> callback, ResilienceContext context, - TState state) => new(new Outcome(new NotSupportedException())); + TState state) => new(Outcome.FromException(new NotSupportedException())); } } diff --git a/test/Polly.Extensions.Tests/Telemetry/ResilienceTelemetryDiagnosticSourceTests.cs b/test/Polly.Extensions.Tests/Telemetry/ResilienceTelemetryDiagnosticSourceTests.cs index 4fe0e37cc1..232cfdbd48 100644 --- a/test/Polly.Extensions.Tests/Telemetry/ResilienceTelemetryDiagnosticSourceTests.cs +++ b/test/Polly.Extensions.Tests/Telemetry/ResilienceTelemetryDiagnosticSourceTests.cs @@ -64,7 +64,7 @@ public void WriteEvent_LoggingWithOutcome_Ok(bool noOutcome) { var telemetry = Create(); using var response = new HttpResponseMessage(System.Net.HttpStatusCode.OK); - ReportEvent(telemetry, noOutcome ? null : new Outcome(response)); + ReportEvent(telemetry, noOutcome ? null : Outcome.FromResult(response)); var messages = _logger.GetRecords(new EventId(0, "ResilienceEvent")).ToList(); messages.Should().HaveCount(1); @@ -85,7 +85,7 @@ public void WriteEvent_LoggingWithOutcome_Ok(bool noOutcome) public void WriteEvent_LoggingWithException_Ok(bool noOutcome) { var telemetry = Create(); - ReportEvent(telemetry, noOutcome ? null : new Outcome(new InvalidOperationException("Dummy message."))); + ReportEvent(telemetry, noOutcome ? null : Outcome.FromException(new InvalidOperationException("Dummy message."))); var messages = _logger.GetRecords(new EventId(0, "ResilienceEvent")).ToList(); @@ -128,8 +128,8 @@ public void WriteEvent_MeteringWithoutEnrichers_Ok(bool noOutcome, bool exceptio Outcome? outcome = noOutcome switch { false => null, - true when exception => new Outcome(new InvalidOperationException("Dummy message.")), - _ => new Outcome(true) + true when exception => Outcome.FromException(new InvalidOperationException("Dummy message.")), + _ => Outcome.FromResult(true) }; ReportEvent(telemetry, outcome, context: ResilienceContext.Get().WithResultType()); @@ -167,8 +167,8 @@ public void WriteExecutionAttemptEvent_Metering_Ok(bool noOutcome, bool exceptio Outcome? outcome = noOutcome switch { false => null, - true when exception => new Outcome(new InvalidOperationException("Dummy message.")), - _ => new Outcome(true) + true when exception => Outcome.FromException(new InvalidOperationException("Dummy message.")), + _ => Outcome.FromResult(true) }; ReportEvent(telemetry, outcome, context: ResilienceContext.Get().WithResultType(), arg: attemptArg); @@ -220,7 +220,7 @@ public void WriteEvent_MeteringWithEnrichers_Ok(int count) }); }); - ReportEvent(telemetry, new Outcome(true)); + ReportEvent(telemetry, Outcome.FromResult(true)); var events = GetEvents("resilience-events"); var ev = events[0]; diff --git a/test/Polly.RateLimiting.Tests/RateLimiterResilienceStrategyTests.cs b/test/Polly.RateLimiting.Tests/RateLimiterResilienceStrategyTests.cs index ed6bbae990..e2c4cfe3bc 100644 --- a/test/Polly.RateLimiting.Tests/RateLimiterResilienceStrategyTests.cs +++ b/test/Polly.RateLimiting.Tests/RateLimiterResilienceStrategyTests.cs @@ -72,7 +72,7 @@ public async Task Execute_LeaseRejected(bool hasEvents, bool hasRetryAfter) var strategy = Create(); var context = ResilienceContext.Get(); context.CancellationToken = cts.Token; - var outcome = await strategy.ExecuteOutcomeAsync((_, _) => new ValueTask>(new Outcome("dummy")), context, "state"); + var outcome = await strategy.ExecuteOutcomeAsync((_, _) => Outcome.FromResultAsTask("dummy"), context, "state"); outcome.Exception .Should() diff --git a/test/Polly.TestUtils/Outcome.cs b/test/Polly.TestUtils/Outcome.cs deleted file mode 100644 index a28374f2c5..0000000000 --- a/test/Polly.TestUtils/Outcome.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Polly.TestUtils; - -public static class Outcome -{ - public static Outcome AsOutcome(this TResult value) => new(value); - - public static Outcome AsOutcome(this Exception error) => new(error); - - public static ValueTask> AsOutcomeAsync(this TResult value) => AsOutcome(value).AsValueTask(); - - public static ValueTask> AsOutcomeAsync(this Exception error) => AsOutcome(error).AsValueTask(); -} From e40d29854ddb404a44080880fb3642359859a837 Mon Sep 17 00:00:00 2001 From: Martin Tomka Date: Tue, 20 Jun 2023 15:18:57 +0200 Subject: [PATCH 2/4] PR comments --- src/Polly.Core/Outcome.TResult.cs | 2 +- src/Polly.Core/Outcome.cs | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Polly.Core/Outcome.TResult.cs b/src/Polly.Core/Outcome.TResult.cs index 4f4858e8a7..1639ef1cff 100644 --- a/src/Polly.Core/Outcome.TResult.cs +++ b/src/Polly.Core/Outcome.TResult.cs @@ -83,7 +83,7 @@ public bool TryGetResult(out TResult? result) /// /// The exception message if the outcome is an exception; otherwise, the string representation of the result. /// - public override string ToString() => ExceptionDispatchInfo != null + public override string ToString() => ExceptionDispatchInfo is null ? Exception!.Message : Result?.ToString() ?? string.Empty; diff --git a/src/Polly.Core/Outcome.cs b/src/Polly.Core/Outcome.cs index 3e08bef2a2..275fe5089d 100644 --- a/src/Polly.Core/Outcome.cs +++ b/src/Polly.Core/Outcome.cs @@ -1,6 +1,4 @@ -#pragma warning disable CA1815 // Override equals and operator equals on value types - -namespace Polly; +namespace Polly; /// /// Produces instances of . From 3b93dfff55f9eddf8d4c40ab0f7172ce713fc459 Mon Sep 17 00:00:00 2001 From: Martin Tomka Date: Tue, 20 Jun 2023 15:35:24 +0200 Subject: [PATCH 3/4] Cleanup --- src/Polly.Core/Outcome.TResult.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Polly.Core/Outcome.TResult.cs b/src/Polly.Core/Outcome.TResult.cs index 1639ef1cff..3d380425b0 100644 --- a/src/Polly.Core/Outcome.TResult.cs +++ b/src/Polly.Core/Outcome.TResult.cs @@ -45,7 +45,7 @@ private Outcome(ExceptionDispatchInfo exceptionDispatchInfo) /// /// Returns even if the result is void. Use to check for void results. /// - public bool HasResult => ExceptionDispatchInfo == null; + public bool HasResult => ExceptionDispatchInfo is null; /// /// Gets a value indicating whether the operation produced a void result. From 1ffeac89186b38f71048d7f3aac3b85c166a42b5 Mon Sep 17 00:00:00 2001 From: Martin Tomka Date: Tue, 20 Jun 2023 15:46:46 +0200 Subject: [PATCH 4/4] fixes --- src/Polly.Core/Outcome.TResult.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Polly.Core/Outcome.TResult.cs b/src/Polly.Core/Outcome.TResult.cs index 3d380425b0..8dff566cfe 100644 --- a/src/Polly.Core/Outcome.TResult.cs +++ b/src/Polly.Core/Outcome.TResult.cs @@ -83,7 +83,7 @@ public bool TryGetResult(out TResult? result) /// /// The exception message if the outcome is an exception; otherwise, the string representation of the result. /// - public override string ToString() => ExceptionDispatchInfo is null + public override string ToString() => ExceptionDispatchInfo is not null ? Exception!.Message : Result?.ToString() ?? string.Empty;