From 75dce3a4c502529c28a49b73228f47833b63902e Mon Sep 17 00:00:00 2001 From: kolan72 Date: Mon, 18 Nov 2024 13:36:34 +0300 Subject: [PATCH 01/51] Introduce `TryCatchBuilder.AddCatchBlock` method overloads with `NonEmptyCatchBlockFilter`, `IBulkErrorProcessor` parameters. --- src/CatchBlockHandlers/CatchBlockHandler.cs | 7 ++++++- src/TryCatch/TryCatchBuilder.cs | 13 ++++++++++++ tests/TryCatchBuilderTests.cs | 22 +++++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/CatchBlockHandlers/CatchBlockHandler.cs b/src/CatchBlockHandlers/CatchBlockHandler.cs index d0d1aed..e4ab9f0 100644 --- a/src/CatchBlockHandlers/CatchBlockHandler.cs +++ b/src/CatchBlockHandlers/CatchBlockHandler.cs @@ -47,7 +47,12 @@ protected CatchBlockHandler(CatchBlockFilter catchBlockFilter) internal PolicyProcessor.ExceptionFilter ErrorFilter => CatchBlockFilter.ErrorFilter; - internal IBulkErrorProcessor BulkErrorProcessor { get; } = new BulkErrorProcessor(); + internal void SetBulkErrorProcessor(IBulkErrorProcessor processor) + { + BulkErrorProcessor = processor; + } + + internal IBulkErrorProcessor BulkErrorProcessor { get; private set; } = new BulkErrorProcessor(); } /// diff --git a/src/TryCatch/TryCatchBuilder.cs b/src/TryCatch/TryCatchBuilder.cs index 7d3642b..25be474 100644 --- a/src/TryCatch/TryCatchBuilder.cs +++ b/src/TryCatch/TryCatchBuilder.cs @@ -60,6 +60,19 @@ public ITryCatchBuilder AddCatchBlock(CatchBlockForAllHandler filteredHandler) return this; } + /// + /// Adds a handler consisting of and to builder. + /// + /// + /// + /// + public TryCatchBuilder AddCatchBlock(NonEmptyCatchBlockFilter nonEmptyCatchBlockFilter, IBulkErrorProcessor bulkErrorProcessor) + { + var handler = new CatchBlockFilteredHandler(nonEmptyCatchBlockFilter); + handler.SetBulkErrorProcessor(bulkErrorProcessor); + return AddCatchBlock(handler); + } + public ITryCatch Build() => new TryCatch(_catchBlockHandlers, _hasCatchBlockForAll); } } diff --git a/tests/TryCatchBuilderTests.cs b/tests/TryCatchBuilderTests.cs index 15e5936..cc500bb 100644 --- a/tests/TryCatchBuilderTests.cs +++ b/tests/TryCatchBuilderTests.cs @@ -48,5 +48,27 @@ public void Should_AddCatchBlock_Really_Add(bool forAll) Assert.That(tryCatch.CatchBlockCount, Is.EqualTo(2)); Assert.That(tryCatch.HasCatchBlockForAll, Is.EqualTo(forAll)); } + + [Test] + public void Should_AddCatchBlock_With_IBulkErrorProcessor_Param_Really_Add() + { + int i = 0; + void act(Exception _) + { + i++; + } + + var bulkErrorProcessor = new BulkErrorProcessor().WithErrorProcessorOf(act); + var tryCatchBuilder = + TryCatchBuilder + .CreateFrom(CatchBlockHandlerFactory.FilterExceptionsBy(NonEmptyCatchBlockFilter.CreateByIncluding())) + .AddCatchBlock(NonEmptyCatchBlockFilter.CreateByIncluding(), bulkErrorProcessor); + var tryCatch = tryCatchBuilder.Build(); + Assert.That(tryCatch.CatchBlockCount, Is.EqualTo(2)); + + var res = tryCatch.Execute(() => throw new InvalidOperationException()); + Assert.That(res.IsError, Is.True); + Assert.That(i, Is.EqualTo(1)); + } } } From 2df42bd4108a04a238c476fda33b1254154e36ac Mon Sep 17 00:00:00 2001 From: kolan72 Date: Tue, 19 Nov 2024 16:03:21 +0300 Subject: [PATCH 02/51] Call the `ConfigureAwait` method in the `ErrorProcessorBase.ProcessAsync` method with the `configAwait` parameter passed. --- src/ErrorProcessors/ErrorProcessorBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ErrorProcessors/ErrorProcessorBase.cs b/src/ErrorProcessors/ErrorProcessorBase.cs index e42b976..d912158 100644 --- a/src/ErrorProcessors/ErrorProcessorBase.cs +++ b/src/ErrorProcessors/ErrorProcessorBase.cs @@ -53,7 +53,7 @@ public async Task ProcessAsync(Exception error, ProcessingErrorInfo c if (NoRunners) return error; - await(_asyncRunner ?? _syncRunner).RunAsync(error, ParameterConverter(catchBlockProcessErrorInfo), configAwait, cancellationToken); + await(_asyncRunner ?? _syncRunner).RunAsync(error, ParameterConverter(catchBlockProcessErrorInfo), configAwait, cancellationToken).ConfigureAwait(configAwait); return error; } From 40a707dd8badeab70291c73ca63428b9ddf7a57a Mon Sep 17 00:00:00 2001 From: kolan72 Date: Wed, 20 Nov 2024 11:29:31 +0300 Subject: [PATCH 03/51] Introduce `TryCatchBuilder.CreateFrom` method overload with `NonEmptyCatchBlockFilter`, `IBulkErrorProcessor` parameters. --- src/TryCatch/TryCatchBuilder.cs | 12 ++++++++++++ tests/TryCatchBuilderTests.cs | 21 +++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/src/TryCatch/TryCatchBuilder.cs b/src/TryCatch/TryCatchBuilder.cs index 25be474..34b3baf 100644 --- a/src/TryCatch/TryCatchBuilder.cs +++ b/src/TryCatch/TryCatchBuilder.cs @@ -26,6 +26,18 @@ public static TryCatchBuilder CreateFrom(CatchBlockFilteredHandler filteredHandl return builder.AddCatchBlock(filteredHandler); } + /// + /// Creates a class based on the and the . Other handlers may be added to the created object. + /// + /// + /// + /// + public static TryCatchBuilder CreateFrom(NonEmptyCatchBlockFilter nonEmptyCatchBlockFilter, IBulkErrorProcessor bulkErrorProcessor) + { + var builder = new TryCatchBuilder(); + return builder.AddCatchBlock(nonEmptyCatchBlockFilter, bulkErrorProcessor); + } + /// /// Creates a class based on the . No other handlers may be added to the created object. /// diff --git a/tests/TryCatchBuilderTests.cs b/tests/TryCatchBuilderTests.cs index cc500bb..c2b9178 100644 --- a/tests/TryCatchBuilderTests.cs +++ b/tests/TryCatchBuilderTests.cs @@ -70,5 +70,26 @@ void act(Exception _) Assert.That(res.IsError, Is.True); Assert.That(i, Is.EqualTo(1)); } + + [Test] + public void Should_AddCatchBlock_With_IBulkErrorProcessor_Param_Really_Create() + { + int i = 0; + void act(Exception _) + { + i++; + } + + var bulkErrorProcessor = new BulkErrorProcessor().WithErrorProcessorOf(act); + var tryCatchBuilder = + TryCatchBuilder + .CreateFrom(NonEmptyCatchBlockFilter.CreateByIncluding(), bulkErrorProcessor); + var tryCatch = tryCatchBuilder.Build(); + Assert.That(tryCatch.CatchBlockCount, Is.EqualTo(1)); + + var res = tryCatch.Execute(() => throw new InvalidOperationException()); + Assert.That(res.IsError, Is.True); + Assert.That(i, Is.EqualTo(1)); + } } } From 09f819fa2f68fc8db5484884140a7610fa4c264c Mon Sep 17 00:00:00 2001 From: kolan72 Date: Wed, 20 Nov 2024 14:03:48 +0300 Subject: [PATCH 04/51] Introduce `DefaultErrorProcessor`, `ProcessingErrorContext` and `ProcessingErrorInfo` classes. --- .../DefaultErrorProcessor.T.cs | 61 ++++++++++++++ .../ProcessingErrorContext.T.cs | 19 +++++ src/ErrorProcessors/ProcessingErrorInfo.T.cs | 16 ++++ tests/BulkErrorProcessorTests.cs | 80 +++++++++++++++++++ 4 files changed, 176 insertions(+) create mode 100644 src/ErrorProcessors/DefaultErrorProcessor.T.cs create mode 100644 src/ErrorProcessors/ProcessingErrorContext.T.cs create mode 100644 src/ErrorProcessors/ProcessingErrorInfo.T.cs diff --git a/src/ErrorProcessors/DefaultErrorProcessor.T.cs b/src/ErrorProcessors/DefaultErrorProcessor.T.cs new file mode 100644 index 0000000..b323477 --- /dev/null +++ b/src/ErrorProcessors/DefaultErrorProcessor.T.cs @@ -0,0 +1,61 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace PoliNorError +{ + public class DefaultErrorProcessor : IErrorProcessor + { + private readonly DefaultErrorProcessorT _errorProcessor; + public DefaultErrorProcessor(Action> actionProcessor) + { + _errorProcessor = DefaultErrorProcessorT.Create(actionProcessor); + } + + public DefaultErrorProcessor(Func, Task> funcProcessor) + { + _errorProcessor = DefaultErrorProcessorT.Create(funcProcessor); + } + + public Exception Process(Exception error, ProcessingErrorInfo catchBlockProcessErrorInfo = null, CancellationToken cancellationToken = default) + { + return _errorProcessor.Process(error, catchBlockProcessErrorInfo, cancellationToken); + } + + public async Task ProcessAsync(Exception error, ProcessingErrorInfo catchBlockProcessErrorInfo = null, bool configAwait = false, CancellationToken cancellationToken = default) + { + return await _errorProcessor.ProcessAsync(error, catchBlockProcessErrorInfo, configAwait, cancellationToken).ConfigureAwait(configAwait); + } + } + + internal class DefaultErrorProcessorT : ErrorProcessorBase + { + public static DefaultErrorProcessorT Create(Action> actionProcessor) + { + void action(Exception ex, ProcessingErrorInfo pi) + { + if (pi is ProcessingErrorInfo gpi) + actionProcessor(ex, gpi); + } + var res = new DefaultErrorProcessorT(); + res.SetSyncRunner(action); + return res; + } + + public static DefaultErrorProcessorT Create(Func, Task> funcProcessor) + { + Task func(Exception ex, ProcessingErrorInfo pi) + { + if (pi is ProcessingErrorInfo gpi) + return funcProcessor(ex, gpi); + else + return Task.CompletedTask; + } + var res = new DefaultErrorProcessorT(); + res.SetAsyncRunner(func); + return res; + } + + protected override Func ParameterConverter => (_) => _; + } +} diff --git a/src/ErrorProcessors/ProcessingErrorContext.T.cs b/src/ErrorProcessors/ProcessingErrorContext.T.cs new file mode 100644 index 0000000..58328e5 --- /dev/null +++ b/src/ErrorProcessors/ProcessingErrorContext.T.cs @@ -0,0 +1,19 @@ +namespace PoliNorError +{ + public class ProcessingErrorContext : ProcessingErrorContext + { + public ProcessingErrorContext(PolicyAlias policyKind, TParam param) : base(policyKind) + { + Param = param; + } + public TParam Param { get; set; } + + internal override ProcessingErrorInfo ToProcessingErrorInfo(PolicyAlias policyAlias) + { + if (policyAlias != PolicyAlias.NotSet) + return new ProcessingErrorInfo(policyAlias, this); + else + return new ProcessingErrorInfo(this); + } + } +} diff --git a/src/ErrorProcessors/ProcessingErrorInfo.T.cs b/src/ErrorProcessors/ProcessingErrorInfo.T.cs new file mode 100644 index 0000000..60be9a6 --- /dev/null +++ b/src/ErrorProcessors/ProcessingErrorInfo.T.cs @@ -0,0 +1,16 @@ +namespace PoliNorError +{ + public class ProcessingErrorInfo : ProcessingErrorInfo + { + internal ProcessingErrorInfo(ProcessingErrorContext currentContext) : this(currentContext.PolicyKind, currentContext) { } + + public ProcessingErrorInfo(PolicyAlias policyKind, ProcessingErrorContext currentContext = null) : base(policyKind, currentContext) + { + if (currentContext != null) + { + Param = currentContext.Param; + } + } + public TParam Param { get; private set; } + } +} diff --git a/tests/BulkErrorProcessorTests.cs b/tests/BulkErrorProcessorTests.cs index 3d60821..373521b 100644 --- a/tests/BulkErrorProcessorTests.cs +++ b/tests/BulkErrorProcessorTests.cs @@ -292,5 +292,85 @@ public async Task Should_WithInnerErrorProcessor_HandleError_Correctly(bool sync Assert.That(innerProcessors.L, Is.EqualTo(1)); } + + [Test] + [TestCase(true)] + [TestCase(false)] + public async Task Should_BulkErrorProcessor_Process_Generic_DefaultErrorProcessor_Created_By_Action(bool syncRun) + { + var bulkProcessor = new BulkErrorProcessor(); + + const int contextParam = 2; + var processingErrorContext = new ProcessingErrorContext(PolicyAlias.NotSet, contextParam); + + bool errorProcessorWorksFlag = false; + var errorProcessor = new DefaultErrorProcessor((_, pir) => + { + if (pir.Param == contextParam) + { + errorProcessorWorksFlag = true; + } + }); + + bool errorProcessorThatShoulNotWorkFlag = false; + + var errorProcessorThatShoulNotWork = new DefaultErrorProcessor((_, __) => errorProcessorThatShoulNotWorkFlag = true); + + bulkProcessor.AddProcessor(errorProcessor); + bulkProcessor.AddProcessor(errorProcessorThatShoulNotWork); + + if (syncRun) + { + bulkProcessor.Process(new Exception(), processingErrorContext); + } + else + { + await bulkProcessor.ProcessAsync(new Exception(), processingErrorContext); + } + + Assert.That(errorProcessorWorksFlag, Is.True); + Assert.That(errorProcessorThatShoulNotWorkFlag, Is.False); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public async Task Should_BulkErrorProcessor_Process_Generic_DefaultErrorProcessor_Created_By_AsyncFunc(bool syncRun) + { + var bulkProcessor = new BulkErrorProcessor(); + + const int contextParam = 2; + var processingErrorContext = new ProcessingErrorContext(PolicyAlias.NotSet, contextParam); + + bool errorProcessorWorksFlag = false; + var errorProcessor = new DefaultErrorProcessor(async (_, pir) => + { + await Task.Delay(1); + if (pir.Param == contextParam) + { + errorProcessorWorksFlag = true; + } + }); + + bool errorProcessorThatShoulNotWorkFlag = false; + + var errorProcessorThatShoulNotWork = new DefaultErrorProcessor((_, __) => errorProcessorThatShoulNotWorkFlag = true); + + bulkProcessor.AddProcessor(errorProcessor); + bulkProcessor.AddProcessor(errorProcessorThatShoulNotWork); + + if (syncRun) + { + bulkProcessor.Process(new Exception(), processingErrorContext); + } + else + { + await bulkProcessor.ProcessAsync(new Exception(), processingErrorContext); + } + + Assert.That(errorProcessorWorksFlag, Is.True); + Assert.That(errorProcessorThatShoulNotWorkFlag, Is.False); + } + } } \ No newline at end of file From 6df6321226b34683441f97461e8db98037797925 Mon Sep 17 00:00:00 2001 From: kolan72 Date: Thu, 21 Nov 2024 16:08:08 +0300 Subject: [PATCH 05/51] Introduce `Action.InvokeWithTryCatch` extension method. --- src/TryCatch/TryCatchDelegateInvoking.cs | 20 +++++++ tests/TryCatchTests.cs | 69 ++++++++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 src/TryCatch/TryCatchDelegateInvoking.cs diff --git a/src/TryCatch/TryCatchDelegateInvoking.cs b/src/TryCatch/TryCatchDelegateInvoking.cs new file mode 100644 index 0000000..b966751 --- /dev/null +++ b/src/TryCatch/TryCatchDelegateInvoking.cs @@ -0,0 +1,20 @@ +using System; +using System.Threading; + +namespace PoliNorError.TryCatch +{ + public static class TryCatchDelegateInvoking + { + /// + /// Invokes the delegate and attempts to catch an exception using the . + /// + /// A delegate to invoke. + /// + /// + /// + public static TryCatchResult InvokeWithTryCatch(this Action action, ITryCatch tryCatch, CancellationToken token = default) + { + return tryCatch.Execute(action, token); + } + } +} diff --git a/tests/TryCatchTests.cs b/tests/TryCatchTests.cs index 97d89e8..4a6f753 100644 --- a/tests/TryCatchTests.cs +++ b/tests/TryCatchTests.cs @@ -627,6 +627,75 @@ public async Task Should_TryCatchResult_IsCanceled_Equals_True_When_Canceled(boo } } + [Test] + [TestCase(true)] + [TestCase(false)] + public void Should_InvokeWithTryCatch_For_Action_Handle_Or_Throw_Exception_Correctly(bool canHandle) + { + Exception errorToThrow = null; + if (canHandle) + { + errorToThrow = new NullReferenceException(); + } + else + { + errorToThrow = new NotImplementedException(); + } + + var tryCatchFactory = new TryCatchBuilderFactoryForDelegateInvocationWithTryCatch(); + var tryCatch = tryCatchFactory.CreateTryCatch(); + + Action action = () => throw errorToThrow; + + if (canHandle) + { + var result = action.InvokeWithTryCatch(tryCatch); + Assert.That(result.IsError, Is.True); + Assert.That(result.Error, Is.EqualTo(errorToThrow)); + Assert.That(tryCatchFactory.IsErrorProcessorCalled, Is.True); + } + else + { + var resException = Assert.Throws(() => action.InvokeWithTryCatch(tryCatch)); + Assert.That(resException, Is.EqualTo(errorToThrow)); + } + } + + [Test] + public void Should_InvokeWithTryCatch_Returns_Success_If_NoError() + { + var tryCatchFactory = new TryCatchBuilderFactoryForDelegateInvocationWithTryCatch(); + var tryCatch = tryCatchFactory.CreateTryCatch(); + + Action action = () => {}; + + var result = action.InvokeWithTryCatch(tryCatch); + Assert.That(result.IsSuccess, Is.True); + Assert.That(tryCatchFactory.IsErrorProcessorCalled, Is.False); + } + + private class TryCatchBuilderFactoryForDelegateInvocationWithTryCatch + { + private readonly CatchBlockFilteredHandler _catchBlockFilteredHandler; + + public TryCatchBuilderFactoryForDelegateInvocationWithTryCatch() + { + _catchBlockFilteredHandler = CatchBlockHandlerFactory + .FilterExceptionsBy(NonEmptyCatchBlockFilter.CreateByIncluding()); + + _catchBlockFilteredHandler.WithErrorProcessorOf((_) => IsErrorProcessorCalled = true); + } + + public ITryCatch CreateTryCatch() + { + return TryCatchBuilder + .CreateFrom(_catchBlockFilteredHandler) + .Build(); + } + + public bool IsErrorProcessorCalled { get; private set; } + } + private class TryCatchBuilderFactoryWhenNoError { private readonly bool _withEmptyFilter; From 0502a0acda480825c08a3c1339023bf10fbaef00 Mon Sep 17 00:00:00 2001 From: kolan72 Date: Fri, 22 Nov 2024 11:12:29 +0300 Subject: [PATCH 06/51] Introduce `Func.InvokeWithTryCatch` extension method. --- src/TryCatch/TryCatchDelegateInvoking.cs | 13 +++++++++ tests/TryCatchTests.cs | 36 +++++++++++++++++++++++- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/TryCatch/TryCatchDelegateInvoking.cs b/src/TryCatch/TryCatchDelegateInvoking.cs index b966751..acb91e7 100644 --- a/src/TryCatch/TryCatchDelegateInvoking.cs +++ b/src/TryCatch/TryCatchDelegateInvoking.cs @@ -16,5 +16,18 @@ public static TryCatchResult InvokeWithTryCatch(this Action action, ITryCatch tr { return tryCatch.Execute(action, token); } + + /// + /// Invokes the delegate and attempts to catch an exception using the . + /// + /// The type of the return value of a delegate. + /// A delegate to invoke. + /// + /// + /// + public static TryCatchResult InvokeWithTryCatch(this Func func, ITryCatch tryCatch, CancellationToken token = default) + { + return tryCatch.Execute(func, token); + } } } diff --git a/tests/TryCatchTests.cs b/tests/TryCatchTests.cs index 4a6f753..173a648 100644 --- a/tests/TryCatchTests.cs +++ b/tests/TryCatchTests.cs @@ -662,7 +662,7 @@ public void Should_InvokeWithTryCatch_For_Action_Handle_Or_Throw_Exception_Corre } [Test] - public void Should_InvokeWithTryCatch_Returns_Success_If_NoError() + public void Should_InvokeWithTryCatch_For_Action_Returns_Success_If_NoError() { var tryCatchFactory = new TryCatchBuilderFactoryForDelegateInvocationWithTryCatch(); var tryCatch = tryCatchFactory.CreateTryCatch(); @@ -674,6 +674,40 @@ public void Should_InvokeWithTryCatch_Returns_Success_If_NoError() Assert.That(tryCatchFactory.IsErrorProcessorCalled, Is.False); } + [Test] + [TestCase(true)] + [TestCase(false)] + public void Should_InvokeWithTryCatch_For_Func_Handle_Or_Throw_Exception_Correctly(bool canHandle) + { + Exception errorToThrow = null; + if (canHandle) + { + errorToThrow = new NullReferenceException(); + } + else + { + errorToThrow = new NotImplementedException(); + } + + var tryCatchFactory = new TryCatchBuilderFactoryForDelegateInvocationWithTryCatch(); + var tryCatch = tryCatchFactory.CreateTryCatch(); + + Func action = () => throw errorToThrow; + + if (canHandle) + { + var result = action.InvokeWithTryCatch(tryCatch); + Assert.That(result.IsError, Is.True); + Assert.That(result.Error, Is.EqualTo(errorToThrow)); + Assert.That(tryCatchFactory.IsErrorProcessorCalled, Is.True); + } + else + { + var resException = Assert.Throws(() => action.InvokeWithTryCatch(tryCatch)); + Assert.That(resException, Is.EqualTo(errorToThrow)); + } + } + private class TryCatchBuilderFactoryForDelegateInvocationWithTryCatch { private readonly CatchBlockFilteredHandler _catchBlockFilteredHandler; From 9ec51633d0837127b1028f202c0b974840fb7b0d Mon Sep 17 00:00:00 2001 From: kolan72 Date: Fri, 22 Nov 2024 11:41:29 +0300 Subject: [PATCH 07/51] Add the test for the `Func.InvokeWithTryCatch` extension method for success. --- tests/TryCatchTests.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/TryCatchTests.cs b/tests/TryCatchTests.cs index 173a648..4b8d97a 100644 --- a/tests/TryCatchTests.cs +++ b/tests/TryCatchTests.cs @@ -706,6 +706,19 @@ public void Should_InvokeWithTryCatch_For_Func_Handle_Or_Throw_Exception_Correct var resException = Assert.Throws(() => action.InvokeWithTryCatch(tryCatch)); Assert.That(resException, Is.EqualTo(errorToThrow)); } + } + + [Test] + public void Should_InvokeWithTryCatch_For_Func_Returns_Success_If_NoError() + { + var tryCatchFactory = new TryCatchBuilderFactoryForDelegateInvocationWithTryCatch(); + var tryCatch = tryCatchFactory.CreateTryCatch(); + + Func action = () => 1; + + var result = action.InvokeWithTryCatch(tryCatch); + Assert.That(result.IsSuccess, Is.True); + Assert.That(tryCatchFactory.IsErrorProcessorCalled, Is.False); } private class TryCatchBuilderFactoryForDelegateInvocationWithTryCatch From 6414a44145b5fd2900b78bbaf693e3f97240bebc Mon Sep 17 00:00:00 2001 From: kolan72 Date: Sat, 23 Nov 2024 18:34:30 +0300 Subject: [PATCH 08/51] Introduce `Func.InvokeWithTryCatchAsync` extension method. --- src/TryCatch/TryCatchDelegateInvoking.cs | 16 +++++++- tests/TryCatchTests.cs | 47 ++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/src/TryCatch/TryCatchDelegateInvoking.cs b/src/TryCatch/TryCatchDelegateInvoking.cs index acb91e7..c84769b 100644 --- a/src/TryCatch/TryCatchDelegateInvoking.cs +++ b/src/TryCatch/TryCatchDelegateInvoking.cs @@ -1,6 +1,7 @@ using System; using System.Threading; - +using System.Threading.Tasks; + namespace PoliNorError.TryCatch { public static class TryCatchDelegateInvoking @@ -28,6 +29,19 @@ public static TryCatchResult InvokeWithTryCatch(this Action action, ITryCatch tr public static TryCatchResult InvokeWithTryCatch(this Func func, ITryCatch tryCatch, CancellationToken token = default) { return tryCatch.Execute(func, token); + } + + /// + /// Invokes Func<CancellationToken, Task> delegate and attempts to catch an exception using the . + /// + /// A delegate to invoke. + /// + /// Specifies whether the asynchronous execution should attempt to continue on the captured context. + /// + /// Task<TryCatchResult>> + public static Task InvokeWithTryCatchAsync(this Func func, ITryCatch tryCatch, bool configureAwait = false, CancellationToken token = default) + { + return tryCatch.ExecuteAsync(func, configureAwait, token); } } } diff --git a/tests/TryCatchTests.cs b/tests/TryCatchTests.cs index 4b8d97a..dd9eeef 100644 --- a/tests/TryCatchTests.cs +++ b/tests/TryCatchTests.cs @@ -721,6 +721,53 @@ public void Should_InvokeWithTryCatch_For_Func_Returns_Success_If_NoError() Assert.That(tryCatchFactory.IsErrorProcessorCalled, Is.False); } + [Test] + [TestCase(true)] + [TestCase(false)] + public async Task Should_InvokeWithTryCatchAsync_For_AsyncFunc_Handle_Or_Throw_Exception_Correctly(bool canHandle) + { + Exception errorToThrow = null; + if (canHandle) + { + errorToThrow = new NullReferenceException(); + } + else + { + errorToThrow = new NotImplementedException(); + } + + var tryCatchFactory = new TryCatchBuilderFactoryForDelegateInvocationWithTryCatch(); + var tryCatch = tryCatchFactory.CreateTryCatch(); + + Func action = async (_) => { await Task.Delay(1); throw errorToThrow; }; + + if (canHandle) + { + var result = await action.InvokeWithTryCatchAsync(tryCatch); + Assert.That(result.IsError, Is.True); + Assert.That(result.Error, Is.EqualTo(errorToThrow)); + Assert.That(tryCatchFactory.IsErrorProcessorCalled, Is.True); + } + else + { + var resException = Assert.ThrowsAsync(async() => await action.InvokeWithTryCatchAsync(tryCatch)); + Assert.That(resException, Is.EqualTo(errorToThrow)); + } + } + + [Test] + public async Task Should_InvokeWithTryCatchAsync_For_AsyncFunc_Returns_Success_If_NoError() + { + var tryCatchFactory = new TryCatchBuilderFactoryForDelegateInvocationWithTryCatch(); + var tryCatch = tryCatchFactory.CreateTryCatch(); + + Func action = async (_) => await Task.Delay(1); + + var result = await action.InvokeWithTryCatchAsync(tryCatch); + Assert.That(result.IsSuccess, Is.True); + Assert.That(tryCatchFactory.IsErrorProcessorCalled, Is.False); + } + private class TryCatchBuilderFactoryForDelegateInvocationWithTryCatch { private readonly CatchBlockFilteredHandler _catchBlockFilteredHandler; From 17a0e36364184a1642d99143feaa4d844c71b6e2 Mon Sep 17 00:00:00 2001 From: kolan72 Date: Sun, 24 Nov 2024 21:55:47 +0300 Subject: [PATCH 09/51] Introduce `Func>.InvokeWithTryCatchAsync` extension method. --- src/TryCatch/TryCatchDelegateInvoking.cs | 15 +++++++- tests/TryCatchTests.cs | 49 ++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/src/TryCatch/TryCatchDelegateInvoking.cs b/src/TryCatch/TryCatchDelegateInvoking.cs index c84769b..cce840c 100644 --- a/src/TryCatch/TryCatchDelegateInvoking.cs +++ b/src/TryCatch/TryCatchDelegateInvoking.cs @@ -38,8 +38,21 @@ public static TryCatchResult InvokeWithTryCatch(this Func func, ITryCat /// /// Specifies whether the asynchronous execution should attempt to continue on the captured context. /// - /// Task<TryCatchResult>> + /// Task<TryCatchResult> public static Task InvokeWithTryCatchAsync(this Func func, ITryCatch tryCatch, bool configureAwait = false, CancellationToken token = default) + { + return tryCatch.ExecuteAsync(func, configureAwait, token); + } + + /// + /// Invokes Func<CancellationToken, Task<T>> delegate and attempts to catch an exception using the . + /// + /// A delegate to invoke. + /// + /// Specifies whether the asynchronous execution should attempt to continue on the captured context. + /// + /// Task<TryCatchResult<T>> + public static Task> InvokeWithTryCatchAsync(this Func> func, ITryCatch tryCatch, bool configureAwait = false, CancellationToken token = default) { return tryCatch.ExecuteAsync(func, configureAwait, token); } diff --git a/tests/TryCatchTests.cs b/tests/TryCatchTests.cs index dd9eeef..fafba28 100644 --- a/tests/TryCatchTests.cs +++ b/tests/TryCatchTests.cs @@ -719,6 +719,7 @@ public void Should_InvokeWithTryCatch_For_Func_Returns_Success_If_NoError() var result = action.InvokeWithTryCatch(tryCatch); Assert.That(result.IsSuccess, Is.True); Assert.That(tryCatchFactory.IsErrorProcessorCalled, Is.False); + Assert.That(result.Result, Is.EqualTo(1)); } [Test] @@ -768,6 +769,54 @@ public async Task Should_InvokeWithTryCatchAsync_For_AsyncFunc_Returns_Success_I Assert.That(tryCatchFactory.IsErrorProcessorCalled, Is.False); } + [Test] + [TestCase(true)] + [TestCase(false)] + public async Task Should_InvokeWithTryCatchAsync_For_Generic_AsyncFunc_Handle_Or_Throw_Exception_Correctly(bool canHandle) + { + Exception errorToThrow = null; + if (canHandle) + { + errorToThrow = new NullReferenceException(); + } + else + { + errorToThrow = new NotImplementedException(); + } + + var tryCatchFactory = new TryCatchBuilderFactoryForDelegateInvocationWithTryCatch(); + var tryCatch = tryCatchFactory.CreateTryCatch(); + + Func> action = async (_) => { await Task.Delay(1); throw errorToThrow; }; + + if (canHandle) + { + var result = await action.InvokeWithTryCatchAsync(tryCatch); + Assert.That(result.IsError, Is.True); + Assert.That(result.Error, Is.EqualTo(errorToThrow)); + Assert.That(tryCatchFactory.IsErrorProcessorCalled, Is.True); + } + else + { + var resException = Assert.ThrowsAsync(async () => await action.InvokeWithTryCatchAsync(tryCatch)); + Assert.That(resException, Is.EqualTo(errorToThrow)); + } + } + + [Test] + public async Task Should_InvokeWithTryCatchAsync_For_Generic_AsyncFunc_Returns_Success_If_NoError() + { + var tryCatchFactory = new TryCatchBuilderFactoryForDelegateInvocationWithTryCatch(); + var tryCatch = tryCatchFactory.CreateTryCatch(); + + Func> action = async (_) => { await Task.Delay(1); return 1; }; + + var result = await action.InvokeWithTryCatchAsync(tryCatch); + Assert.That(result.IsSuccess, Is.True); + Assert.That(tryCatchFactory.IsErrorProcessorCalled, Is.False); + Assert.That(result.Result, Is.EqualTo(1)); + } + private class TryCatchBuilderFactoryForDelegateInvocationWithTryCatch { private readonly CatchBlockFilteredHandler _catchBlockFilteredHandler; From fec456b757e9d7af945d1045c9b6405f68154e32 Mon Sep 17 00:00:00 2001 From: kolan72 Date: Mon, 25 Nov 2024 11:11:38 +0300 Subject: [PATCH 10/51] Remove old references to NUnit-related nuget packages in the PoliNorError.Tests project file. --- tests/PoliNorError.Tests.csproj | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/PoliNorError.Tests.csproj b/tests/PoliNorError.Tests.csproj index 142d336..da016c9 100644 --- a/tests/PoliNorError.Tests.csproj +++ b/tests/PoliNorError.Tests.csproj @@ -1,11 +1,7 @@  - - - - Debug @@ -168,11 +164,7 @@ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - \ No newline at end of file From e4105587046d11d5c7e2d3bc3e09407704d54729 Mon Sep 17 00:00:00 2001 From: kolan72 Date: Mon, 25 Nov 2024 13:08:54 +0300 Subject: [PATCH 11/51] Add the Rider-related .gitignore --- .idea/.idea.PoliNorError/.idea/.gitignore | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .idea/.idea.PoliNorError/.idea/.gitignore diff --git a/.idea/.idea.PoliNorError/.idea/.gitignore b/.idea/.idea.PoliNorError/.idea/.gitignore new file mode 100644 index 0000000..e95e280 --- /dev/null +++ b/.idea/.idea.PoliNorError/.idea/.gitignore @@ -0,0 +1,15 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/contentModel.xml +/modules.xml +/.idea.PoliNorError.iml +/projectSettingsUpdater.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +/indexLayout.xml +/vcs.xml From 2009c824dbf6a002b50d20709b68db7441bad1a5 Mon Sep 17 00:00:00 2001 From: kolan72 Date: Tue, 26 Nov 2024 16:52:00 +0300 Subject: [PATCH 12/51] Introduce `InvokeWithTryCatchAsync` extension methods overloads for the `Func>` , `Func` delegates. --- src/TryCatch/TryCatchDelegateInvoking.cs | 30 ++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/TryCatch/TryCatchDelegateInvoking.cs b/src/TryCatch/TryCatchDelegateInvoking.cs index cce840c..ba3f0bc 100644 --- a/src/TryCatch/TryCatchDelegateInvoking.cs +++ b/src/TryCatch/TryCatchDelegateInvoking.cs @@ -31,6 +31,20 @@ public static TryCatchResult InvokeWithTryCatch(this Func func, ITryCat return tryCatch.Execute(func, token); } + /// + /// Invokes Func<CancellationToken, Task> delegate and attempts to catch an exception + /// using the method + /// with the configureAwait parameter is set to false. + /// + /// A delegate to invoke. + /// + /// + /// Task<TryCatchResult> + public static Task InvokeWithTryCatchAsync(this Func func, ITryCatch tryCatch, CancellationToken token) + { + return InvokeWithTryCatchAsync(func, tryCatch, false, token); + } + /// /// Invokes Func<CancellationToken, Task> delegate and attempts to catch an exception using the . /// @@ -44,9 +58,25 @@ public static Task InvokeWithTryCatchAsync(this Func + /// Invokes Func<CancellationToken, Task<T>> delegate and attempts to catch an exception + /// using the method + /// with the configureAwait parameter is set to false. + /// + /// The type of the return value of a delegate. + /// A delegate to invoke. + /// + /// + /// + public static Task> InvokeWithTryCatchAsync(this Func> func, ITryCatch tryCatch, CancellationToken token) + { + return InvokeWithTryCatchAsync(func, tryCatch, false, token); + } + /// /// Invokes Func<CancellationToken, Task<T>> delegate and attempts to catch an exception using the . /// + /// The type of the return value of a delegate. /// A delegate to invoke. /// /// Specifies whether the asynchronous execution should attempt to continue on the captured context. From 2b0d2f3c07aff24b57dd6ec7908136bc0223740a Mon Sep 17 00:00:00 2001 From: kolan72 Date: Wed, 27 Nov 2024 13:43:52 +0300 Subject: [PATCH 13/51] Introduce the `TryCatchBuilder.AddCatchBlock` method overload with the `IBulkErrorProcessor` parameter. --- src/TryCatch/TryCatchBuilder.cs | 12 ++++++++++++ tests/TryCatchBuilderTests.cs | 20 ++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/src/TryCatch/TryCatchBuilder.cs b/src/TryCatch/TryCatchBuilder.cs index 34b3baf..7cb71b5 100644 --- a/src/TryCatch/TryCatchBuilder.cs +++ b/src/TryCatch/TryCatchBuilder.cs @@ -72,6 +72,18 @@ public ITryCatchBuilder AddCatchBlock(CatchBlockForAllHandler filteredHandler) return this; } + /// + /// Adds handler with the . + /// + /// + /// + public ITryCatchBuilder AddCatchBlock(IBulkErrorProcessor bulkErrorProcessor) + { + var handlerToAdd = CatchBlockHandlerFactory.ForAllExceptions(); + handlerToAdd.SetBulkErrorProcessor(bulkErrorProcessor); + return AddCatchBlock(handlerToAdd); + } + /// /// Adds a handler consisting of and to builder. /// diff --git a/tests/TryCatchBuilderTests.cs b/tests/TryCatchBuilderTests.cs index c2b9178..1baaecf 100644 --- a/tests/TryCatchBuilderTests.cs +++ b/tests/TryCatchBuilderTests.cs @@ -49,6 +49,26 @@ public void Should_AddCatchBlock_Really_Add(bool forAll) Assert.That(tryCatch.HasCatchBlockForAll, Is.EqualTo(forAll)); } + [Test] + public void Should_AddCatchBlock_With_IBulkErrorProcessor_Param_Handle_Exception_Correctly() + { + var bulkErrorProcessor = new BulkErrorProcessor(); + int i = 0; + bulkErrorProcessor.WithErrorProcessorOf((_) => i++); + int m = 0; + bulkErrorProcessor.WithErrorProcessorOf((_) => m++); + var builder = TryCatchBuilder.CreateFrom(CatchBlockHandlerFactory.FilterExceptionsBy + (NonEmptyCatchBlockFilter. + CreateByExcluding())) + .AddCatchBlock(bulkErrorProcessor); + + var tryCatch = builder.Build(); + var result = tryCatch.Execute(() => throw new InvalidOperationException()); + Assert.That(result.IsError, Is.True); + Assert.That(i, Is.EqualTo(1)); + Assert.That(m, Is.EqualTo(1)); + } + [Test] public void Should_AddCatchBlock_With_IBulkErrorProcessor_Param_Really_Add() { From 192310c7858b1f100abfaf147e7813b7013ac18b Mon Sep 17 00:00:00 2001 From: kolan72 Date: Thu, 28 Nov 2024 17:04:06 +0300 Subject: [PATCH 14/51] Introduce the `TryCatchBuilder.CreateFrom` method overload with the `IBulkErrorProcessor` parameter. --- src/TryCatch/TryCatchBuilder.cs | 13 ++++++++++++- tests/TryCatchBuilderTests.cs | 16 ++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/TryCatch/TryCatchBuilder.cs b/src/TryCatch/TryCatchBuilder.cs index 7cb71b5..58ab201 100644 --- a/src/TryCatch/TryCatchBuilder.cs +++ b/src/TryCatch/TryCatchBuilder.cs @@ -49,6 +49,17 @@ public static ITryCatchBuilder CreateFrom(CatchBlockForAllHandler filteredHandle return builder.AddCatchBlock(filteredHandler); } + /// + /// Creates a class based on the with the . No other handlers may be added to the created object. + /// + /// + /// + public static ITryCatchBuilder CreateFrom(IBulkErrorProcessor bulkErrorProcessor) + { + var builder = new TryCatchBuilder(); + return builder.AddCatchBlock(bulkErrorProcessor); + } + /// /// Adds handler to builder. Other handlers may be added to the created object. /// @@ -73,7 +84,7 @@ public ITryCatchBuilder AddCatchBlock(CatchBlockForAllHandler filteredHandler) } /// - /// Adds handler with the . + /// Adds handler with the . No other handlers may be added to the created object. /// /// /// diff --git a/tests/TryCatchBuilderTests.cs b/tests/TryCatchBuilderTests.cs index 1baaecf..bdfb4a3 100644 --- a/tests/TryCatchBuilderTests.cs +++ b/tests/TryCatchBuilderTests.cs @@ -63,12 +63,27 @@ public void Should_AddCatchBlock_With_IBulkErrorProcessor_Param_Handle_Exception .AddCatchBlock(bulkErrorProcessor); var tryCatch = builder.Build(); + Assert.That(tryCatch.HasCatchBlockForAll, Is.True); + var result = tryCatch.Execute(() => throw new InvalidOperationException()); Assert.That(result.IsError, Is.True); Assert.That(i, Is.EqualTo(1)); Assert.That(m, Is.EqualTo(1)); } + [Test] + public void Should_CreateFrom_With_IBulkErrorProcessor_Param_Handle_Exception_Correctly() + { + var bulkErrorProcessor = new BulkErrorProcessor(); + int i = 0; + bulkErrorProcessor.WithErrorProcessorOf((_) => i++); + int m = 0; + bulkErrorProcessor.WithErrorProcessorOf((_) => m++); + var builder = TryCatchBuilder.CreateFrom(bulkErrorProcessor); + var tryCatch = builder.Build(); + Assert.That(tryCatch.HasCatchBlockForAll, Is.True); + } + [Test] public void Should_AddCatchBlock_With_IBulkErrorProcessor_Param_Really_Add() { @@ -83,6 +98,7 @@ void act(Exception _) TryCatchBuilder .CreateFrom(CatchBlockHandlerFactory.FilterExceptionsBy(NonEmptyCatchBlockFilter.CreateByIncluding())) .AddCatchBlock(NonEmptyCatchBlockFilter.CreateByIncluding(), bulkErrorProcessor); + var tryCatch = tryCatchBuilder.Build(); Assert.That(tryCatch.CatchBlockCount, Is.EqualTo(2)); From 90f8a00d7e6429cb64ff7860f19da3f62b534082 Mon Sep 17 00:00:00 2001 From: kolan72 Date: Fri, 29 Nov 2024 14:41:00 +0300 Subject: [PATCH 15/51] Introduce the `TryCatchBuilder.AddCatchBlock` method overload with the `NonEmptyCatchBlockFilter` parameter. --- src/TryCatch/TryCatchBuilder.cs | 11 +++++++++++ tests/TryCatchBuilderTests.cs | 28 +++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/TryCatch/TryCatchBuilder.cs b/src/TryCatch/TryCatchBuilder.cs index 58ab201..7ae5370 100644 --- a/src/TryCatch/TryCatchBuilder.cs +++ b/src/TryCatch/TryCatchBuilder.cs @@ -108,6 +108,17 @@ public TryCatchBuilder AddCatchBlock(NonEmptyCatchBlockFilter nonEmptyCatchBlock return AddCatchBlock(handler); } + /// + /// Adds a handler consisting of + /// + /// + /// + public TryCatchBuilder AddCatchBlock(NonEmptyCatchBlockFilter nonEmptyCatchBlockFilter) + { + var handler = new CatchBlockFilteredHandler(nonEmptyCatchBlockFilter); + return AddCatchBlock(handler); + } + public ITryCatch Build() => new TryCatch(_catchBlockHandlers, _hasCatchBlockForAll); } } diff --git a/tests/TryCatchBuilderTests.cs b/tests/TryCatchBuilderTests.cs index bdfb4a3..9e11c19 100644 --- a/tests/TryCatchBuilderTests.cs +++ b/tests/TryCatchBuilderTests.cs @@ -85,7 +85,7 @@ public void Should_CreateFrom_With_IBulkErrorProcessor_Param_Handle_Exception_Co } [Test] - public void Should_AddCatchBlock_With_IBulkErrorProcessor_Param_Really_Add() + public void Should_AddCatchBlock_With_NonEmptyCatchBlockFilter_And_IBulkErrorProcessor_Params_Really_Add() { int i = 0; void act(Exception _) @@ -107,6 +107,32 @@ void act(Exception _) Assert.That(i, Is.EqualTo(1)); } + [Test] + [TestCase(true)] + [TestCase(false)] + public void Should_AddCatchBlock_With_NonEmptyCatchBlockFilter_Param_Really_Add(bool canHandle) + { + var tryCatchBuilder = + TryCatchBuilder + .CreateFrom(CatchBlockHandlerFactory.FilterExceptionsBy(NonEmptyCatchBlockFilter.CreateByIncluding())) + .AddCatchBlock(NonEmptyCatchBlockFilter.CreateByIncluding()); + + var tryCatch = tryCatchBuilder.Build(); + Assert.That(tryCatch.CatchBlockCount, Is.EqualTo(2)); + + if (canHandle) + { + var res = tryCatch.Execute(() => throw new InvalidOperationException()); + Assert.That(res.IsError, Is.True); + } + else + { + var errorToThrow = new NotImplementedException("Test"); + var exc = Assert.Throws(() => tryCatch.Execute(() => throw errorToThrow)); + Assert.That(exc, Is.EqualTo(errorToThrow)); + } + } + [Test] public void Should_AddCatchBlock_With_IBulkErrorProcessor_Param_Really_Create() { From 19147db9c3e9a8ac4b59c1595eed50dc98686058 Mon Sep 17 00:00:00 2001 From: kolan72 Date: Sat, 30 Nov 2024 19:44:22 +0300 Subject: [PATCH 16/51] Introduce the `TryCatchBuilder.CreateFrom` method overload with the `NonEmptyCatchBlockFilter` parameter. --- src/TryCatch/TryCatchBuilder.cs | 20 ++++++++++++++++---- tests/TryCatchBuilderTests.cs | 16 +++++++++++++++- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/src/TryCatch/TryCatchBuilder.cs b/src/TryCatch/TryCatchBuilder.cs index 7ae5370..57c98cf 100644 --- a/src/TryCatch/TryCatchBuilder.cs +++ b/src/TryCatch/TryCatchBuilder.cs @@ -36,8 +36,20 @@ public static TryCatchBuilder CreateFrom(NonEmptyCatchBlockFilter nonEmptyCatchB { var builder = new TryCatchBuilder(); return builder.AddCatchBlock(nonEmptyCatchBlockFilter, bulkErrorProcessor); + } + + /// + /// Creates a class based on the . + /// The created contains a that mimics a try/catch block that swallows an exception. + /// + /// + /// + public static TryCatchBuilder CreateFrom(NonEmptyCatchBlockFilter nonEmptyCatchBlockFilter) + { + var builder = new TryCatchBuilder(); + return builder.AddCatchBlock(nonEmptyCatchBlockFilter); } - + /// /// Creates a class based on the . No other handlers may be added to the created object. /// @@ -106,10 +118,10 @@ public TryCatchBuilder AddCatchBlock(NonEmptyCatchBlockFilter nonEmptyCatchBlock var handler = new CatchBlockFilteredHandler(nonEmptyCatchBlockFilter); handler.SetBulkErrorProcessor(bulkErrorProcessor); return AddCatchBlock(handler); - } - + } + /// - /// Adds a handler consisting of + /// Adds a handler consisting of . Mimics a try/catch block that swallows an exception. /// /// /// diff --git a/tests/TryCatchBuilderTests.cs b/tests/TryCatchBuilderTests.cs index 9e11c19..08a2aa9 100644 --- a/tests/TryCatchBuilderTests.cs +++ b/tests/TryCatchBuilderTests.cs @@ -134,7 +134,7 @@ public void Should_AddCatchBlock_With_NonEmptyCatchBlockFilter_Param_Really_Add( } [Test] - public void Should_AddCatchBlock_With_IBulkErrorProcessor_Param_Really_Create() + public void Should_CreateFrom_With_NonEmptyCatchBlockFilter_And_With_IBulkErrorProcessor_Param_Really_Create() { int i = 0; void act(Exception _) @@ -153,5 +153,19 @@ void act(Exception _) Assert.That(res.IsError, Is.True); Assert.That(i, Is.EqualTo(1)); } + + [Test] + public void Should_CreateFrom_With_NonEmptyCatchBlockFilter_Param_Really_Create() + { + var tryCatchBuilder = + TryCatchBuilder + .CreateFrom(NonEmptyCatchBlockFilter.CreateByIncluding()); + + var tryCatch = tryCatchBuilder.Build(); + Assert.That(tryCatch.CatchBlockCount, Is.EqualTo(1)); + + var res = tryCatch.Execute(() => throw new InvalidOperationException()); + Assert.That(res.IsError, Is.True); + } } } From 4f69dbfa12cbce29c04813f8a792f23d50120bbd Mon Sep 17 00:00:00 2001 From: kolan72 Date: Sun, 1 Dec 2024 15:14:01 +0300 Subject: [PATCH 17/51] Introduce `TryCatchBuilder.CreateAndBuild` method. --- src/TryCatch/TryCatchBuilder.cs | 9 +++++++++ tests/TryCatchBuilderTests.cs | 11 +++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/TryCatch/TryCatchBuilder.cs b/src/TryCatch/TryCatchBuilder.cs index 57c98cf..5f13525 100644 --- a/src/TryCatch/TryCatchBuilder.cs +++ b/src/TryCatch/TryCatchBuilder.cs @@ -13,6 +13,15 @@ public sealed class TryCatchBuilder : ITryCatchBuilder private TryCatchBuilder() { _catchBlockHandlers = new List(); + } + + /// + /// Creates which mimics a try/catch block without a filter that will swallow any exception. + /// + /// + public static ITryCatch CreateAndBuild() + { + return CreateFrom(CatchBlockHandlerFactory.ForAllExceptions()).Build(); } /// diff --git a/tests/TryCatchBuilderTests.cs b/tests/TryCatchBuilderTests.cs index 08a2aa9..8aaf146 100644 --- a/tests/TryCatchBuilderTests.cs +++ b/tests/TryCatchBuilderTests.cs @@ -166,6 +166,17 @@ public void Should_CreateFrom_With_NonEmptyCatchBlockFilter_Param_Really_Create( var res = tryCatch.Execute(() => throw new InvalidOperationException()); Assert.That(res.IsError, Is.True); + } + + [Test] + public void Should_CreateAndBuild_Create_ITryCatch_That_Handles_Exception() + { + var tryCatch = TryCatchBuilder.CreateAndBuild(); + Assert.That(tryCatch.CatchBlockCount, Is.EqualTo(1)); + Assert.That(tryCatch.HasCatchBlockForAll, Is.True); + + var res = tryCatch.Execute(() => throw new Exception("Test")); + Assert.That(res.IsError, Is.True); } } } From 9cd6fce1c636cf61436131b0944a800660f55160 Mon Sep 17 00:00:00 2001 From: kolan72 Date: Mon, 2 Dec 2024 11:38:18 +0300 Subject: [PATCH 18/51] Introduce shorthand `TryCatchBuilder.CreateAndBuild` method with the `IBulkErrorProcessor` parameter (remove the `TryCatchBuilder.CreateFrom` method overload with the `IBulkErrorProcessor` parameter). --- src/TryCatch/TryCatchBuilder.cs | 22 +++++++++++----------- tests/TryCatchBuilderTests.cs | 30 +++++++++++++++++------------- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/src/TryCatch/TryCatchBuilder.cs b/src/TryCatch/TryCatchBuilder.cs index 5f13525..fb44caa 100644 --- a/src/TryCatch/TryCatchBuilder.cs +++ b/src/TryCatch/TryCatchBuilder.cs @@ -22,6 +22,17 @@ private TryCatchBuilder() public static ITryCatch CreateAndBuild() { return CreateFrom(CatchBlockHandlerFactory.ForAllExceptions()).Build(); + } + + /// + /// Creates which mimics a try/catch block without a filter that will process an exception by the . + /// + /// + /// + public static ITryCatch CreateAndBuild(IBulkErrorProcessor bulkErrorProcessor) + { + var builder = new TryCatchBuilder(); + return builder.AddCatchBlock(bulkErrorProcessor).Build(); } /// @@ -70,17 +81,6 @@ public static ITryCatchBuilder CreateFrom(CatchBlockForAllHandler filteredHandle return builder.AddCatchBlock(filteredHandler); } - /// - /// Creates a class based on the with the . No other handlers may be added to the created object. - /// - /// - /// - public static ITryCatchBuilder CreateFrom(IBulkErrorProcessor bulkErrorProcessor) - { - var builder = new TryCatchBuilder(); - return builder.AddCatchBlock(bulkErrorProcessor); - } - /// /// Adds handler to builder. Other handlers may be added to the created object. /// diff --git a/tests/TryCatchBuilderTests.cs b/tests/TryCatchBuilderTests.cs index 8aaf146..12a1af4 100644 --- a/tests/TryCatchBuilderTests.cs +++ b/tests/TryCatchBuilderTests.cs @@ -71,19 +71,6 @@ public void Should_AddCatchBlock_With_IBulkErrorProcessor_Param_Handle_Exception Assert.That(m, Is.EqualTo(1)); } - [Test] - public void Should_CreateFrom_With_IBulkErrorProcessor_Param_Handle_Exception_Correctly() - { - var bulkErrorProcessor = new BulkErrorProcessor(); - int i = 0; - bulkErrorProcessor.WithErrorProcessorOf((_) => i++); - int m = 0; - bulkErrorProcessor.WithErrorProcessorOf((_) => m++); - var builder = TryCatchBuilder.CreateFrom(bulkErrorProcessor); - var tryCatch = builder.Build(); - Assert.That(tryCatch.HasCatchBlockForAll, Is.True); - } - [Test] public void Should_AddCatchBlock_With_NonEmptyCatchBlockFilter_And_IBulkErrorProcessor_Params_Really_Add() { @@ -178,5 +165,22 @@ public void Should_CreateAndBuild_Create_ITryCatch_That_Handles_Exception() var res = tryCatch.Execute(() => throw new Exception("Test")); Assert.That(res.IsError, Is.True); } + + [Test] + public void Should_CreateAndBuild_With_IBulkErrorProcessor_Param_Create_ITryCatch_That_Handles_Exception() + { + var bulkErrorProcessor = new BulkErrorProcessor(); + int i = 0; + bulkErrorProcessor.WithErrorProcessorOf((_) => i++); + int m = 0; + bulkErrorProcessor.WithErrorProcessorOf((_) => m++); + var tryCatch = TryCatchBuilder.CreateAndBuild(bulkErrorProcessor); + Assert.That(tryCatch.HasCatchBlockForAll, Is.True); + + var res = tryCatch.Execute(() => throw new InvalidOperationException()); + Assert.That(res.IsError, Is.True); + Assert.That(i, Is.EqualTo(1)); + Assert.That(m, Is.EqualTo(1)); + } } } From a237f77ba97266cd713fdc135cdb62f631109806 Mon Sep 17 00:00:00 2001 From: kolan72 Date: Mon, 2 Dec 2024 14:30:05 +0300 Subject: [PATCH 19/51] Introduce new constructors for the `DefaultErrorProcessor` class with a `CancellationType` parameter. --- .../DefaultErrorProcessor.T.cs | 56 ++++++++++++++--- tests/ErrorProcessorTests.cs | 63 +++++++++++++++++++ 2 files changed, 109 insertions(+), 10 deletions(-) diff --git a/src/ErrorProcessors/DefaultErrorProcessor.T.cs b/src/ErrorProcessors/DefaultErrorProcessor.T.cs index b323477..b605d05 100644 --- a/src/ErrorProcessors/DefaultErrorProcessor.T.cs +++ b/src/ErrorProcessors/DefaultErrorProcessor.T.cs @@ -12,11 +12,21 @@ public DefaultErrorProcessor(Action> acti _errorProcessor = DefaultErrorProcessorT.Create(actionProcessor); } + public DefaultErrorProcessor(Action> actionProcessor, CancellationType cancellationType) + { + _errorProcessor = DefaultErrorProcessorT.Create(actionProcessor, cancellationType); + } + public DefaultErrorProcessor(Func, Task> funcProcessor) { _errorProcessor = DefaultErrorProcessorT.Create(funcProcessor); } + public DefaultErrorProcessor(Func, Task> funcProcessor, CancellationType cancellationType) + { + _errorProcessor = DefaultErrorProcessorT.Create(funcProcessor, cancellationType); + } + public Exception Process(Exception error, ProcessingErrorInfo catchBlockProcessErrorInfo = null, CancellationToken cancellationToken = default) { return _errorProcessor.Process(error, catchBlockProcessErrorInfo, cancellationToken); @@ -32,28 +42,54 @@ internal class DefaultErrorProcessorT : ErrorProcessorBase { public static DefaultErrorProcessorT Create(Action> actionProcessor) { - void action(Exception ex, ProcessingErrorInfo pi) - { - if (pi is ProcessingErrorInfo gpi) - actionProcessor(ex, gpi); - } + var action = ConvertToNonGenericAction(actionProcessor); var res = new DefaultErrorProcessorT(); res.SetSyncRunner(action); return res; } + public static DefaultErrorProcessorT Create(Action> actionProcessor, CancellationType cancellationType) + { + var action = ConvertToNonGenericAction(actionProcessor); + var res = new DefaultErrorProcessorT(); + res.SetSyncRunner(action, cancellationType); + return res; + } + public static DefaultErrorProcessorT Create(Func, Task> funcProcessor) { - Task func(Exception ex, ProcessingErrorInfo pi) + var func = ConvertToNonGenericFunc(funcProcessor); + var res = new DefaultErrorProcessorT(); + res.SetAsyncRunner(func); + return res; + } + + public static DefaultErrorProcessorT Create(Func, Task> funcProcessor, CancellationType cancellationType) + { + var func = ConvertToNonGenericFunc(funcProcessor); + var res = new DefaultErrorProcessorT(); + res.SetAsyncRunner(func, cancellationType); + return res; + } + + private static Action ConvertToNonGenericAction(Action> actionProcessor) + { + return (Exception ex, ProcessingErrorInfo pi) => + { + if (pi is ProcessingErrorInfo gpi) + actionProcessor(ex, gpi); + }; + } + + private static Func ConvertToNonGenericFunc(Func, Task> funcProcessor) + { + return (Exception ex, ProcessingErrorInfo pi) => { if (pi is ProcessingErrorInfo gpi) return funcProcessor(ex, gpi); else return Task.CompletedTask; - } - var res = new DefaultErrorProcessorT(); - res.SetAsyncRunner(func); - return res; + }; } protected override Func ParameterConverter => (_) => _; diff --git a/tests/ErrorProcessorTests.cs b/tests/ErrorProcessorTests.cs index b338606..56b74d5 100644 --- a/tests/ErrorProcessorTests.cs +++ b/tests/ErrorProcessorTests.cs @@ -105,5 +105,68 @@ public async Task Should_DefaultErrorProcessor_Sync_And_Async_Part_Work(bool syn ClassicAssert.AreEqual(2, infoCounter); } + + [Test] + [TestCase(true, false)] + [TestCase(false, false)] + [TestCase(true, true)] + [TestCase(false, true)] + public void Should_DefaultErrorProcessor_TParam_Process_Only_ProcessingErrorInfo_TParam(bool isGeneric, bool withCancelType) + { + int i = 0; + DefaultErrorProcessor errPr = null; + if (!withCancelType) + { + errPr = new DefaultErrorProcessor((_, __) => i++); + } + else + { + errPr = new DefaultErrorProcessor((_, __) => i++, CancellationType.Precancelable); + } + + ProcessingErrorInfo piToTest = null; + if (isGeneric) + { + piToTest = new ProcessingErrorInfo(new ProcessingErrorContext(PolicyAlias.NotSet, 1)); + } + else + { + piToTest = new ProcessingErrorInfo(PolicyAlias.NotSet); + } + errPr.Process(new Exception(), piToTest); + + Assert.That(i, Is.EqualTo(isGeneric ? 1 : 0)); + } + + [Test] + [TestCase(true, false)] + [TestCase(false, false)] + [TestCase(true, true)] + [TestCase(false, true)] + public async Task Should_DefaultErrorProcessor_TParam_ProcessAsync_Only_ProcessingErrorInfo_TParam(bool isGeneric, bool withCancelType) + { + int i = 0; + DefaultErrorProcessor errPr = null; + if (!withCancelType) + { + errPr = new DefaultErrorProcessor(async (_, __) => { await Task.Delay(1); i++; }); + } + else + { + errPr = new DefaultErrorProcessor(async (_, __) => { await Task.Delay(1); i++; }, CancellationType.Precancelable); + } + ProcessingErrorInfo piToTest = null; + if (isGeneric) + { + piToTest = new ProcessingErrorInfo(new ProcessingErrorContext(PolicyAlias.NotSet, 1)); + } + else + { + piToTest = new ProcessingErrorInfo(PolicyAlias.NotSet); + } + await errPr.ProcessAsync(new Exception(), piToTest); + + Assert.That(i, Is.EqualTo(isGeneric ? 1 : 0)); + } } } From 9169964bac859b82ce64b3f7baa1acf360d4437c Mon Sep 17 00:00:00 2001 From: kolan72 Date: Tue, 3 Dec 2024 13:54:52 +0300 Subject: [PATCH 20/51] Introduce shorthand `TryCatchBuilder.CreateAndBuild` method with the `Action` parameter. --- src/TryCatch/TryCatchBuilder.cs | 14 +++++++++++++- tests/TryCatchBuilderTests.cs | 15 +++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/TryCatch/TryCatchBuilder.cs b/src/TryCatch/TryCatchBuilder.cs index fb44caa..95d866e 100644 --- a/src/TryCatch/TryCatchBuilder.cs +++ b/src/TryCatch/TryCatchBuilder.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; namespace PoliNorError.TryCatch { @@ -33,6 +34,17 @@ public static ITryCatch CreateAndBuild(IBulkErrorProcessor bulkErrorProcessor) { var builder = new TryCatchBuilder(); return builder.AddCatchBlock(bulkErrorProcessor).Build(); + } + + /// + /// Creates which mimics a try/catch block without a filter that will process an exception by the . + /// + /// Delegate that will process an exception. + /// + public static ITryCatch CreateAndBuild(Action errorProcessorAction) + { + var bulkProcessor = new BulkErrorProcessor().WithErrorProcessorOf(errorProcessorAction); + return CreateAndBuild(bulkProcessor); } /// diff --git a/tests/TryCatchBuilderTests.cs b/tests/TryCatchBuilderTests.cs index 12a1af4..51e2982 100644 --- a/tests/TryCatchBuilderTests.cs +++ b/tests/TryCatchBuilderTests.cs @@ -182,5 +182,20 @@ public void Should_CreateAndBuild_With_IBulkErrorProcessor_Param_Create_ITryCatc Assert.That(i, Is.EqualTo(1)); Assert.That(m, Is.EqualTo(1)); } + + [Test] + public void Should_CreateAndBuild_With_Action_Exception_Param_Create_ITryCatch_That_Handles_Exception() + { + int i = 0; + void act(Exception _) + { + i++; + } + + var tryCatch = TryCatchBuilder.CreateAndBuild(act); + var res = tryCatch.Execute(() => throw new InvalidOperationException()); + Assert.That(res.IsError, Is.True); + Assert.That(i, Is.EqualTo(1)); + } } } From 292de0f334a06547e372fe15b965ecf2d3f0bae7 Mon Sep 17 00:00:00 2001 From: kolan72 Date: Tue, 3 Dec 2024 15:55:40 +0300 Subject: [PATCH 21/51] Add `EmptyErrorContext` internal class. --- src/CatchBlockHandlers/ErrorContext.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/CatchBlockHandlers/ErrorContext.cs b/src/CatchBlockHandlers/ErrorContext.cs index 4b74575..c70b565 100644 --- a/src/CatchBlockHandlers/ErrorContext.cs +++ b/src/CatchBlockHandlers/ErrorContext.cs @@ -12,7 +12,7 @@ protected ErrorContext(T t) public abstract ProcessingErrorContext ToProcessingErrorContext(); } - internal sealed class EmptyErrorContext : ErrorContext + internal class EmptyErrorContext : ErrorContext { public static EmptyErrorContext Default { get; } = new EmptyErrorContext(); @@ -20,10 +20,22 @@ internal sealed class EmptyErrorContext : ErrorContext public static EmptyErrorContext DefaultSimple { get; } = new EmptyErrorContext() { PolicyKind = PolicyAlias.Simple }; - private EmptyErrorContext() : base(Unit.Default){} + protected EmptyErrorContext() : base(Unit.Default){} public PolicyAlias PolicyKind { get; private set; } = PolicyAlias.NotSet; public override ProcessingErrorContext ToProcessingErrorContext() => new ProcessingErrorContext(PolicyKind); } + + internal class EmptyErrorContext : EmptyErrorContext + { + public EmptyErrorContext(TParam param) + { + Param = param; + } + + public TParam Param { get; private set; } + + public override ProcessingErrorContext ToProcessingErrorContext() => new ProcessingErrorContext(PolicyKind, Param); + } } From 97a552a26c6dec29ecfb79cb91f10cd84c7576a7 Mon Sep 17 00:00:00 2001 From: kolan72 Date: Tue, 3 Dec 2024 17:05:06 +0300 Subject: [PATCH 22/51] Introduce the`SimplePolicyProcessor.Execute(Action)` method and the `SimplePolicyProcessor.WithErrorProcessorOf` method overloads with the `Action>` parameter. --- src/Simple/SimplePolicyProcessor.cs | 29 ++++++++++++++++++- tests/BulkErrorProcessorTests.cs | 1 - tests/SimplePolicyProcessorTests.cs | 43 +++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 2 deletions(-) diff --git a/src/Simple/SimplePolicyProcessor.cs b/src/Simple/SimplePolicyProcessor.cs index ae5711a..51b4a6a 100644 --- a/src/Simple/SimplePolicyProcessor.cs +++ b/src/Simple/SimplePolicyProcessor.cs @@ -46,6 +46,17 @@ public SimplePolicyProcessor(IBulkErrorProcessor bulkErrorProcessor, bool rethro /// public PolicyResult Execute(Action action, CancellationToken token = default) + { + return Execute(action, _emptyErrorContext, token); + } + + public PolicyResult Execute(Action action, TParam param, CancellationToken token = default) + { + var emptyContext = new EmptyErrorContext(param); + return Execute(action, (EmptyErrorContext)emptyContext, token); + } + + private PolicyResult Execute(Action action, EmptyErrorContext emptyErrorContext, CancellationToken token = default) { if (action == null) return new PolicyResult().WithNoDelegateException(); @@ -90,7 +101,7 @@ public PolicyResult Execute(Action action, CancellationToken token = default) result.AddError(ex); result.ChangeByHandleCatchBlockResult(GetCatchBlockSyncHandler(result, token) - .Handle(ex, _emptyErrorContext)); + .Handle(ex, emptyErrorContext)); } return result; } @@ -242,6 +253,22 @@ public async Task> ExecuteAsync(Func(Action> actionProcessor) + { + return WithErrorProcessor(new DefaultErrorProcessor(actionProcessor)); + } + + public SimplePolicyProcessor WithErrorProcessorOf(Action> actionProcessor, CancellationType cancellationType) + { + return WithErrorProcessor(new DefaultErrorProcessor(actionProcessor, cancellationType)); + } + + public SimplePolicyProcessor WithErrorProcessor(DefaultErrorProcessor errorProcessor) + { + AddErrorProcessor(errorProcessor); + return this; + } + private (bool? FilterUnsatisfied, Exception exception) GetFilterUnsatisfiedOrFilterException(Exception ex) { try diff --git a/tests/BulkErrorProcessorTests.cs b/tests/BulkErrorProcessorTests.cs index 373521b..47216b5 100644 --- a/tests/BulkErrorProcessorTests.cs +++ b/tests/BulkErrorProcessorTests.cs @@ -371,6 +371,5 @@ public async Task Should_BulkErrorProcessor_Process_Generic_DefaultErrorProcesso Assert.That(errorProcessorWorksFlag, Is.True); Assert.That(errorProcessorThatShoulNotWorkFlag, Is.False); } - } } \ No newline at end of file diff --git a/tests/SimplePolicyProcessorTests.cs b/tests/SimplePolicyProcessorTests.cs index f7f5b86..33c0e15 100644 --- a/tests/SimplePolicyProcessorTests.cs +++ b/tests/SimplePolicyProcessorTests.cs @@ -622,6 +622,49 @@ public async Task Should_Rethrow_Or_Handle_If_ProcessorCreated_With_ThrowIfError } } + [Test] + [TestCase(true, true)] + [TestCase(false, true)] + [TestCase(true, false)] + [TestCase(false, false)] + public void Should_Execute_For_Action_With_Generic_Param_WithErrorProcessorOf_Action_Process_Correctly(bool shouldWork, bool withCancellationType) + { + int m = 0; + + void action(Exception _, ProcessingErrorInfo pi) + { + m = pi.Param; + } + + SimplePolicyProcessor processor; + + if (!withCancellationType) + { + processor = new SimplePolicyProcessor(true) + .WithErrorProcessorOf(action); + } + else + { + processor = new SimplePolicyProcessor(true) + .WithErrorProcessorOf(action, CancellationType.Precancelable); + } + + PolicyResult result = null; + + if (shouldWork) + { + result = processor.Execute(() => throw new InvalidOperationException(), 5); + Assert.That(m, Is.EqualTo(5)); + } + else + { + result = processor.Execute(() => throw new InvalidOperationException()); + Assert.That(m, Is.EqualTo(0)); + } + Assert.That(result.NoError, Is.False); + Assert.That(result.IsSuccess, Is.True); + } + private class TestErrorProcessor : IErrorProcessor { public Exception Process(Exception error, ProcessingErrorInfo catchBlockProcessErrorInfo = null, CancellationToken cancellationToken = default) From c7d9e30c30784a47f491894f4f791c7970611bc0 Mon Sep 17 00:00:00 2001 From: kolan72 Date: Wed, 4 Dec 2024 14:32:12 +0300 Subject: [PATCH 23/51] Introduce the `SimplePolicyProcessor.Execute(Action, TParam, token)` method. --- src/Simple/SimplePolicyProcessor.cs | 5 +++++ tests/SimplePolicyProcessorTests.cs | 32 +++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/src/Simple/SimplePolicyProcessor.cs b/src/Simple/SimplePolicyProcessor.cs index 51b4a6a..98cc8f8 100644 --- a/src/Simple/SimplePolicyProcessor.cs +++ b/src/Simple/SimplePolicyProcessor.cs @@ -50,6 +50,11 @@ public PolicyResult Execute(Action action, CancellationToken token = default) return Execute(action, _emptyErrorContext, token); } + public PolicyResult Execute(Action action, TParam param, CancellationToken token = default) + { + return Execute(action.Apply(param), param, token); + } + public PolicyResult Execute(Action action, TParam param, CancellationToken token = default) { var emptyContext = new EmptyErrorContext(param); diff --git a/tests/SimplePolicyProcessorTests.cs b/tests/SimplePolicyProcessorTests.cs index 33c0e15..7546d61 100644 --- a/tests/SimplePolicyProcessorTests.cs +++ b/tests/SimplePolicyProcessorTests.cs @@ -665,6 +665,38 @@ void action(Exception _, ProcessingErrorInfo pi) Assert.That(result.IsSuccess, Is.True); } + [Test] + [TestCase(true)] + [TestCase(false)] + public void Should_Execute_For_ActionTParam_With_Generic_Param_WithErrorProcessorOf_Action_Process_Correctly(bool throwEx) + { + int m = 0; + int addable = 1; + + void action(Exception _, ProcessingErrorInfo pi) + { + m = pi.Param; + } + + var processor = new SimplePolicyProcessor(true) + .WithErrorProcessorOf(action); + + PolicyResult result = null; + if (throwEx) + { + result = processor.Execute((_) => throw new InvalidOperationException(), 5); + Assert.That(m, Is.EqualTo(5)); + Assert.That(result.NoError, Is.False); + } + else + { + result = processor.Execute((v) => addable += v, 5); + Assert.That(addable, Is.EqualTo(6)); + Assert.That(result.NoError, Is.True); + } + Assert.That(result.IsSuccess, Is.True); + } + private class TestErrorProcessor : IErrorProcessor { public Exception Process(Exception error, ProcessingErrorInfo catchBlockProcessErrorInfo = null, CancellationToken cancellationToken = default) From 3ebf6ec18d8d7a64f3046af8ac9d3ddc2897c4ef Mon Sep 17 00:00:00 2001 From: kolan72 Date: Wed, 4 Dec 2024 16:00:19 +0300 Subject: [PATCH 24/51] Introduce `DefaultErrorProcessor(Action, CancellationToken>)` constructor. --- .../DefaultErrorProcessor.T.cs | 17 ++++++++++++++ tests/ErrorProcessorTests.cs | 22 +++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/src/ErrorProcessors/DefaultErrorProcessor.T.cs b/src/ErrorProcessors/DefaultErrorProcessor.T.cs index b605d05..534c6ed 100644 --- a/src/ErrorProcessors/DefaultErrorProcessor.T.cs +++ b/src/ErrorProcessors/DefaultErrorProcessor.T.cs @@ -12,6 +12,11 @@ public DefaultErrorProcessor(Action> acti _errorProcessor = DefaultErrorProcessorT.Create(actionProcessor); } + public DefaultErrorProcessor(Action, CancellationToken> actionProcessor) + { + _errorProcessor = DefaultErrorProcessorT.Create(actionProcessor); + } + public DefaultErrorProcessor(Action> actionProcessor, CancellationType cancellationType) { _errorProcessor = DefaultErrorProcessorT.Create(actionProcessor, cancellationType); @@ -48,6 +53,18 @@ public static DefaultErrorProcessorT Create(Action(Action, CancellationToken> actionProcessor) + { + void action(Exception ex, ProcessingErrorInfo pi, CancellationToken token) + { + if (pi is ProcessingErrorInfo gpi) + actionProcessor(ex, gpi, token); + } + var res = new DefaultErrorProcessorT(); + res.SetSyncRunner(action); + return res; + } + public static DefaultErrorProcessorT Create(Action> actionProcessor, CancellationType cancellationType) { var action = ConvertToNonGenericAction(actionProcessor); diff --git a/tests/ErrorProcessorTests.cs b/tests/ErrorProcessorTests.cs index 56b74d5..e268458 100644 --- a/tests/ErrorProcessorTests.cs +++ b/tests/ErrorProcessorTests.cs @@ -138,6 +138,28 @@ public void Should_DefaultErrorProcessor_TParam_Process_Only_ProcessingErrorInfo Assert.That(i, Is.EqualTo(isGeneric ? 1 : 0)); } + [Test] + [TestCase(true)] + [TestCase(false)] + public void Should_DefaultErrorProcessor_TParam_Of_Action_With_TokenPram_Process_Only_ProcessingErrorInfo_TParam(bool isGeneric) + { + int i = 0; + DefaultErrorProcessor errPr = new DefaultErrorProcessor((_, __, ___) => i++); + + ProcessingErrorInfo piToTest = null; + if (isGeneric) + { + piToTest = new ProcessingErrorInfo(new ProcessingErrorContext(PolicyAlias.NotSet, 1)); + } + else + { + piToTest = new ProcessingErrorInfo(PolicyAlias.NotSet); + } + errPr.Process(new Exception(), piToTest); + + Assert.That(i, Is.EqualTo(isGeneric ? 1 : 0)); + } + [Test] [TestCase(true, false)] [TestCase(false, false)] From f47f6eb97daffb39f6c1b419cc002b86efe9c7c2 Mon Sep 17 00:00:00 2001 From: kolan72 Date: Wed, 4 Dec 2024 18:27:55 +0300 Subject: [PATCH 25/51] Introduce `SimplePolicyProcessor.WithErrorProcessorOf(Action, CancellationToken>)` method. --- src/Simple/SimplePolicyProcessor.cs | 5 +++++ tests/SimplePolicyProcessorTests.cs | 32 +++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/src/Simple/SimplePolicyProcessor.cs b/src/Simple/SimplePolicyProcessor.cs index 98cc8f8..51742e5 100644 --- a/src/Simple/SimplePolicyProcessor.cs +++ b/src/Simple/SimplePolicyProcessor.cs @@ -263,6 +263,11 @@ public SimplePolicyProcessor WithErrorProcessorOf(Action(actionProcessor)); } + public SimplePolicyProcessor WithErrorProcessorOf(Action, CancellationToken> actionProcessor) + { + return WithErrorProcessor(new DefaultErrorProcessor(actionProcessor)); + } + public SimplePolicyProcessor WithErrorProcessorOf(Action> actionProcessor, CancellationType cancellationType) { return WithErrorProcessor(new DefaultErrorProcessor(actionProcessor, cancellationType)); diff --git a/tests/SimplePolicyProcessorTests.cs b/tests/SimplePolicyProcessorTests.cs index 7546d61..29f2c61 100644 --- a/tests/SimplePolicyProcessorTests.cs +++ b/tests/SimplePolicyProcessorTests.cs @@ -665,6 +665,38 @@ void action(Exception _, ProcessingErrorInfo pi) Assert.That(result.IsSuccess, Is.True); } + [Test] + [TestCase(true)] + [TestCase(false)] + public void Should_Execute_For_Action_With_Generic_Param_WithErrorProcessorOf_Action_With_Token_Param_Process_Correctly(bool shouldWork) + { + int m = 0; + + void action(Exception _, ProcessingErrorInfo pi, CancellationToken __) + { + m = pi.Param; + } + + var processor = new SimplePolicyProcessor(true) + .WithErrorProcessorOf(action); + + PolicyResult result; + + if (shouldWork) + { + result = processor.Execute(() => throw new InvalidOperationException(), 5); + Assert.That(m, Is.EqualTo(5)); + } + else + { + result = processor.Execute(() => throw new InvalidOperationException()); + Assert.That(m, Is.EqualTo(0)); + } + + Assert.That(result.NoError, Is.False); + Assert.That(result.IsSuccess, Is.True); + } + [Test] [TestCase(true)] [TestCase(false)] From 8fb49bcbc35bb5fa5a7108d86eeb4d977a522456 Mon Sep 17 00:00:00 2001 From: kolan72 Date: Thu, 5 Dec 2024 11:51:07 +0300 Subject: [PATCH 26/51] Introduce `DefaultErrorProcessor(Func, CancellationToken, Task>)` constructor. --- .../DefaultErrorProcessor.T.cs | 19 +++++++++++++++ tests/ErrorProcessorTests.cs | 23 ++++++++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/ErrorProcessors/DefaultErrorProcessor.T.cs b/src/ErrorProcessors/DefaultErrorProcessor.T.cs index 534c6ed..4c5c6d9 100644 --- a/src/ErrorProcessors/DefaultErrorProcessor.T.cs +++ b/src/ErrorProcessors/DefaultErrorProcessor.T.cs @@ -27,6 +27,11 @@ public DefaultErrorProcessor(Func, Task> _errorProcessor = DefaultErrorProcessorT.Create(funcProcessor); } + public DefaultErrorProcessor(Func, CancellationToken, Task> funcProcessor) + { + _errorProcessor = DefaultErrorProcessorT.Create(funcProcessor); + } + public DefaultErrorProcessor(Func, Task> funcProcessor, CancellationType cancellationType) { _errorProcessor = DefaultErrorProcessorT.Create(funcProcessor, cancellationType); @@ -81,6 +86,20 @@ public static DefaultErrorProcessorT Create(Func(Func, CancellationToken, Task> funcProcessor) + { + Task fn(Exception ex, ProcessingErrorInfo pi, CancellationToken token) + { + if (pi is ProcessingErrorInfo gpi) + return funcProcessor(ex, gpi, token); + else + return Task.CompletedTask; + } + var res = new DefaultErrorProcessorT(); + res.SetAsyncRunner(fn); + return res; + } + public static DefaultErrorProcessorT Create(Func, Task> funcProcessor, CancellationType cancellationType) { var func = ConvertToNonGenericFunc(funcProcessor); diff --git a/tests/ErrorProcessorTests.cs b/tests/ErrorProcessorTests.cs index e268458..21abf86 100644 --- a/tests/ErrorProcessorTests.cs +++ b/tests/ErrorProcessorTests.cs @@ -141,7 +141,7 @@ public void Should_DefaultErrorProcessor_TParam_Process_Only_ProcessingErrorInfo [Test] [TestCase(true)] [TestCase(false)] - public void Should_DefaultErrorProcessor_TParam_Of_Action_With_TokenPram_Process_Only_ProcessingErrorInfo_TParam(bool isGeneric) + public void Should_DefaultErrorProcessor_TParam_Of_Action_With_TokenParam_Process_Only_ProcessingErrorInfo_TParam(bool isGeneric) { int i = 0; DefaultErrorProcessor errPr = new DefaultErrorProcessor((_, __, ___) => i++); @@ -190,5 +190,26 @@ public async Task Should_DefaultErrorProcessor_TParam_ProcessAsync_Only_Processi Assert.That(i, Is.EqualTo(isGeneric ? 1 : 0)); } + + [Test] + [TestCase(true)] + [TestCase(false)] + public async Task Should_DefaultErrorProcessor_TParam_Of_Action_With_TokenParam_ProcessAsync_Only_ProcessingErrorInfo_TParam(bool isGeneric) + { + int i = 0; + DefaultErrorProcessor errPr = new DefaultErrorProcessor(async (_, __, ___) => { await Task.Delay(1); i++; }); + + ProcessingErrorInfo piToTest = null; + if (isGeneric) + { + piToTest = new ProcessingErrorInfo(new ProcessingErrorContext(PolicyAlias.NotSet, 1)); + } + else + { + piToTest = new ProcessingErrorInfo(PolicyAlias.NotSet); + } + await errPr.ProcessAsync(new Exception(), piToTest); + Assert.That(i, Is.EqualTo(isGeneric ? 1 : 0)); + } } } From e4f591355cfd922fc3c3f92144ce7487419be5ba Mon Sep 17 00:00:00 2001 From: kolan72 Date: Thu, 5 Dec 2024 12:14:49 +0300 Subject: [PATCH 27/51] Introduce shorthand `TryCatchBuilder.CreateAndBuild` method with the `Func` parameter. --- src/TryCatch/TryCatchBuilder.cs | 14 +++++++++++++- tests/TryCatchBuilderTests.cs | 33 +++++++++++++++++++++++++++++++-- 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/src/TryCatch/TryCatchBuilder.cs b/src/TryCatch/TryCatchBuilder.cs index 95d866e..146717a 100644 --- a/src/TryCatch/TryCatchBuilder.cs +++ b/src/TryCatch/TryCatchBuilder.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; - +using System.Threading.Tasks; + namespace PoliNorError.TryCatch { /// @@ -45,6 +46,17 @@ public static ITryCatch CreateAndBuild(Action errorProcessorAction) { var bulkProcessor = new BulkErrorProcessor().WithErrorProcessorOf(errorProcessorAction); return CreateAndBuild(bulkProcessor); + } + + /// + /// Creates which mimics a try/catch block without a filter that will process an exception by the . + /// + /// Delegate that will process an exception. + /// + public static ITryCatch CreateAndBuild(Func errorProcessorFunc) + { + var bulkProcessor = new BulkErrorProcessor().WithErrorProcessorOf(errorProcessorFunc); + return CreateAndBuild(bulkProcessor); } /// diff --git a/tests/TryCatchBuilderTests.cs b/tests/TryCatchBuilderTests.cs index 51e2982..8df4d65 100644 --- a/tests/TryCatchBuilderTests.cs +++ b/tests/TryCatchBuilderTests.cs @@ -1,7 +1,8 @@ using NUnit.Framework; using PoliNorError.TryCatch; using System; - +using System.Threading.Tasks; + namespace PoliNorError.Tests { internal class TryCatchBuilderTests @@ -184,7 +185,7 @@ public void Should_CreateAndBuild_With_IBulkErrorProcessor_Param_Create_ITryCatc } [Test] - public void Should_CreateAndBuild_With_Action_Exception_Param_Create_ITryCatch_That_Handles_Exception() + public void Should_CreateAndBuild_With_Action_With_Exception_Param_Create_ITryCatch_That_Handles_Exception() { int i = 0; void act(Exception _) @@ -197,5 +198,33 @@ void act(Exception _) Assert.That(res.IsError, Is.True); Assert.That(i, Is.EqualTo(1)); } + + [Test] + [TestCase(true)] + [TestCase(false)] + public async Task Should_CreateAndBuild_With_AsyncFunc_With_Exception_Param_Create_ITryCatch_That_Handles_Exception(bool isSync) + { + int i = 0; + async Task fn(Exception _) + { + await Task.Delay(1); + i++; + } + + var tryCatch = TryCatchBuilder.CreateAndBuild(fn); + + TryCatchResult res; + if (isSync) + { + res = tryCatch.Execute(() => throw new InvalidOperationException()); + } + else + { + res = await tryCatch.ExecuteAsync(async(_) => { await Task.Delay(1); throw new InvalidOperationException(); }); + } + + Assert.That(res.IsError, Is.True); + Assert.That(i, Is.EqualTo(1)); + } } } From 78902821107d75cc653f0080f12c922259274be5 Mon Sep 17 00:00:00 2001 From: kolan72 Date: Fri, 6 Dec 2024 14:44:56 +0300 Subject: [PATCH 28/51] Introduce `SimplePolicyProcessor.WithErrorProcessorOf(Func, Task>)` method overloads. --- src/Simple/SimplePolicyProcessor.cs | 10 +++++++ tests/SimplePolicyProcessorTests.cs | 45 +++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/src/Simple/SimplePolicyProcessor.cs b/src/Simple/SimplePolicyProcessor.cs index 51742e5..7b153dd 100644 --- a/src/Simple/SimplePolicyProcessor.cs +++ b/src/Simple/SimplePolicyProcessor.cs @@ -273,6 +273,16 @@ public SimplePolicyProcessor WithErrorProcessorOf(Action(actionProcessor, cancellationType)); } + public SimplePolicyProcessor WithErrorProcessorOf(Func, Task> funcProcessor) + { + return WithErrorProcessor(new DefaultErrorProcessor(funcProcessor)); + } + + public SimplePolicyProcessor WithErrorProcessorOf(Func, Task> funcProcessor, CancellationType cancellationType) + { + return WithErrorProcessor(new DefaultErrorProcessor(funcProcessor, cancellationType)); + } + public SimplePolicyProcessor WithErrorProcessor(DefaultErrorProcessor errorProcessor) { AddErrorProcessor(errorProcessor); diff --git a/tests/SimplePolicyProcessorTests.cs b/tests/SimplePolicyProcessorTests.cs index 29f2c61..3f7a309 100644 --- a/tests/SimplePolicyProcessorTests.cs +++ b/tests/SimplePolicyProcessorTests.cs @@ -665,6 +665,51 @@ void action(Exception _, ProcessingErrorInfo pi) Assert.That(result.IsSuccess, Is.True); } + [Test] + [TestCase(true, true)] + [TestCase(false, true)] + [TestCase(true, false)] + [TestCase(false, false)] + public void Should_Execute_For_Action_With_Generic_Param_WithErrorProcessorOf_AsyncFunc_Process_Correctly(bool shouldWork, bool withCancellationType) + { + int m = 0; + + async Task fn(Exception _, ProcessingErrorInfo pi) + { + await Task.Delay(1); + m = pi.Param; + } + + SimplePolicyProcessor processor; + + if (!withCancellationType) + { + processor = new SimplePolicyProcessor(true) + .WithErrorProcessorOf(fn); + } + else + { + processor = new SimplePolicyProcessor(true) + .WithErrorProcessorOf(fn, CancellationType.Precancelable); + } + + PolicyResult result = null; + + if (shouldWork) + { + result = processor.Execute(() => throw new InvalidOperationException(), 5); + Assert.That(m, Is.EqualTo(5)); + } + else + { + result = processor.Execute(() => throw new InvalidOperationException()); + Assert.That(m, Is.EqualTo(0)); + } + + Assert.That(result.NoError, Is.False); + Assert.That(result.IsSuccess, Is.True); + } + [Test] [TestCase(true)] [TestCase(false)] From cf411f6fa13c79b89d542a560cf743948f2fb1bc Mon Sep 17 00:00:00 2001 From: kolan72 Date: Sat, 7 Dec 2024 10:22:45 +0300 Subject: [PATCH 29/51] Introduce `SimplePolicyProcessor.WithErrorProcessorOf(Func, CancellationToken, Task>)` method. --- src/Simple/SimplePolicyProcessor.cs | 5 +++++ tests/SimplePolicyProcessorTests.cs | 33 +++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/src/Simple/SimplePolicyProcessor.cs b/src/Simple/SimplePolicyProcessor.cs index 7b153dd..bb9260b 100644 --- a/src/Simple/SimplePolicyProcessor.cs +++ b/src/Simple/SimplePolicyProcessor.cs @@ -283,6 +283,11 @@ public SimplePolicyProcessor WithErrorProcessorOf(Func(funcProcessor, cancellationType)); } + public SimplePolicyProcessor WithErrorProcessorOf(Func, CancellationToken, Task> funcProcessor) + { + return WithErrorProcessor(new DefaultErrorProcessor(funcProcessor)); + } + public SimplePolicyProcessor WithErrorProcessor(DefaultErrorProcessor errorProcessor) { AddErrorProcessor(errorProcessor); diff --git a/tests/SimplePolicyProcessorTests.cs b/tests/SimplePolicyProcessorTests.cs index 3f7a309..f596ec6 100644 --- a/tests/SimplePolicyProcessorTests.cs +++ b/tests/SimplePolicyProcessorTests.cs @@ -710,6 +710,39 @@ async Task fn(Exception _, ProcessingErrorInfo pi) Assert.That(result.IsSuccess, Is.True); } + [Test] + [TestCase(true)] + [TestCase(false)] + public void Should_Execute_For_Action_With_Generic_Param_WithErrorProcessorOf_AsyncFunc_With_Token_Param_Process_Correctly(bool shouldWork) + { + int m = 0; + + async Task fn(Exception _, ProcessingErrorInfo pi, CancellationToken __) + { + await Task.Delay(1); + m = pi.Param; + } + + var processor = new SimplePolicyProcessor(true) + .WithErrorProcessorOf(fn); + + PolicyResult result; + + if (shouldWork) + { + result = processor.Execute(() => throw new InvalidOperationException(), 5); + Assert.That(m, Is.EqualTo(5)); + } + else + { + result = processor.Execute(() => throw new InvalidOperationException()); + Assert.That(m, Is.EqualTo(0)); + } + + Assert.That(result.NoError, Is.False); + Assert.That(result.IsSuccess, Is.True); + } + [Test] [TestCase(true)] [TestCase(false)] From f4bb684d808991708982f38d80a5c603353dc8af Mon Sep 17 00:00:00 2001 From: kolan72 Date: Sun, 8 Dec 2024 19:34:01 +0300 Subject: [PATCH 30/51] Introduce `SimplePolicyProcessor.Execute(Func, TErrorContext, CancellationToken)` method. --- src/Simple/SimplePolicyProcessor.cs | 17 ++++++++++++--- tests/SimplePolicyProcessorTests.cs | 33 ++++++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/src/Simple/SimplePolicyProcessor.cs b/src/Simple/SimplePolicyProcessor.cs index bb9260b..4bb53ca 100644 --- a/src/Simple/SimplePolicyProcessor.cs +++ b/src/Simple/SimplePolicyProcessor.cs @@ -55,9 +55,9 @@ public PolicyResult Execute(Action action, TParam param, Cancell return Execute(action.Apply(param), param, token); } - public PolicyResult Execute(Action action, TParam param, CancellationToken token = default) + public PolicyResult Execute(Action action, TErrorContext param, CancellationToken token = default) { - var emptyContext = new EmptyErrorContext(param); + var emptyContext = new EmptyErrorContext(param); return Execute(action, (EmptyErrorContext)emptyContext, token); } @@ -111,8 +111,19 @@ private PolicyResult Execute(Action action, EmptyErrorContext emptyErrorContext, return result; } + public PolicyResult Execute(Func func, TErrorContext param, CancellationToken token = default) + { + var emptyContext = new EmptyErrorContext(param); + return Execute(func, (EmptyErrorContext)emptyContext, token); + } + /// public PolicyResult Execute(Func func, CancellationToken token = default) + { + return Execute(func, _emptyErrorContext, token); + } + + private PolicyResult Execute(Func func, EmptyErrorContext emptyErrorContext, CancellationToken token = default) { if (func == null) return new PolicyResult().WithNoDelegateException(); @@ -158,7 +169,7 @@ public PolicyResult Execute(Func func, CancellationToken token = defaul result.AddError(ex); result.ChangeByHandleCatchBlockResult(GetCatchBlockSyncHandler(result, token) - .Handle(ex, _emptyErrorContext)); + .Handle(ex, emptyErrorContext)); } return result; } diff --git a/tests/SimplePolicyProcessorTests.cs b/tests/SimplePolicyProcessorTests.cs index f596ec6..114e90b 100644 --- a/tests/SimplePolicyProcessorTests.cs +++ b/tests/SimplePolicyProcessorTests.cs @@ -778,7 +778,7 @@ void action(Exception _, ProcessingErrorInfo pi, CancellationToken __) [Test] [TestCase(true)] [TestCase(false)] - public void Should_Execute_For_ActionTParam_With_Generic_Param_WithErrorProcessorOf_Action_Process_Correctly(bool throwEx) + public void Should_Execute_With_TParam_For_Action_With_TParam_WithErrorProcessorOf_Action_Process_Correctly(bool throwEx) { int m = 0; int addable = 1; @@ -807,6 +807,37 @@ void action(Exception _, ProcessingErrorInfo pi) Assert.That(result.IsSuccess, Is.True); } + [Test] + [TestCase(true)] + [TestCase(false)] + public void Should_Execute_With_TParam_For_Func_With_TParam_WithErrorProcessorOf_Action_Process_Correctly(bool throwEx) + { + int m = 0; + + void action(Exception _, ProcessingErrorInfo pi) + { + m = pi.Param; + } + + var processor = new SimplePolicyProcessor(true) + .WithErrorProcessorOf(action); + + PolicyResult result = null; + if (throwEx) + { + result = processor.Execute(() => throw new InvalidOperationException(), 5); + Assert.That(m, Is.EqualTo(5)); + Assert.That(result.NoError, Is.False); + } + else + { + result = processor.Execute(() => 1, 5); + Assert.That(m, Is.EqualTo(0)); + Assert.That(result.NoError, Is.True); + } + Assert.That(result.IsSuccess, Is.True); + } + private class TestErrorProcessor : IErrorProcessor { public Exception Process(Exception error, ProcessingErrorInfo catchBlockProcessErrorInfo = null, CancellationToken cancellationToken = default) From 7473ed76300557c59b77d1a4c1f1e91a15c3c2cb Mon Sep 17 00:00:00 2001 From: kolan72 Date: Mon, 9 Dec 2024 12:59:23 +0300 Subject: [PATCH 31/51] Introduce `SimplePolicyProcessor.Execute(Func, TParam, CancellationToken)` method. --- src/Simple/SimplePolicyProcessor.cs | 5 ++++ tests/SimplePolicyProcessorTests.cs | 39 +++++++++++++++++++++++++++-- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/Simple/SimplePolicyProcessor.cs b/src/Simple/SimplePolicyProcessor.cs index 4bb53ca..48c9249 100644 --- a/src/Simple/SimplePolicyProcessor.cs +++ b/src/Simple/SimplePolicyProcessor.cs @@ -111,6 +111,11 @@ private PolicyResult Execute(Action action, EmptyErrorContext emptyErrorContext, return result; } + public PolicyResult Execute(Func func, TParam param, CancellationToken token = default) + { + return Execute(func.Apply(param), param, token); + } + public PolicyResult Execute(Func func, TErrorContext param, CancellationToken token = default) { var emptyContext = new EmptyErrorContext(param); diff --git a/tests/SimplePolicyProcessorTests.cs b/tests/SimplePolicyProcessorTests.cs index 114e90b..72626f9 100644 --- a/tests/SimplePolicyProcessorTests.cs +++ b/tests/SimplePolicyProcessorTests.cs @@ -800,7 +800,9 @@ void action(Exception _, ProcessingErrorInfo pi) } else { - result = processor.Execute((v) => addable += v, 5); +#pragma warning disable RCS1021 // Convert lambda expression body to expression-body. + result = processor.Execute((v) => { addable += v; }, 5); +#pragma warning restore RCS1021 // Convert lambda expression body to expression-body. Assert.That(addable, Is.EqualTo(6)); Assert.That(result.NoError, Is.True); } @@ -810,7 +812,7 @@ void action(Exception _, ProcessingErrorInfo pi) [Test] [TestCase(true)] [TestCase(false)] - public void Should_Execute_With_TParam_For_Func_With_TParam_WithErrorProcessorOf_Action_Process_Correctly(bool throwEx) + public void Should_Execute_With_TParam_For_Func_WithErrorProcessorOf_Action_Process_Correctly(bool throwEx) { int m = 0; @@ -838,6 +840,39 @@ void action(Exception _, ProcessingErrorInfo pi) Assert.That(result.IsSuccess, Is.True); } + [Test] + [TestCase(true)] + [TestCase(false)] + public void Should_Execute_With_TParam_For_Func_With_TParam_WithErrorProcessorOf_Action_Process_Correctly(bool throwEx) + { + int m = 0; + int addable = 1; + + void action(Exception _, ProcessingErrorInfo pi) + { + m = pi.Param; + } + + var processor = new SimplePolicyProcessor(true) + .WithErrorProcessorOf(action); + + PolicyResult result = null; + if (throwEx) + { + result = processor.Execute((_) => throw new InvalidOperationException(), 5); + Assert.That(m, Is.EqualTo(5)); + Assert.That(result.NoError, Is.False); + } + else + { + result = processor.Execute((v) => addable += v, 5); + Assert.That(m, Is.EqualTo(0)); + Assert.That(addable, Is.EqualTo(6)); + Assert.That(result.NoError, Is.True); + } + Assert.That(result.IsSuccess, Is.True); + } + private class TestErrorProcessor : IErrorProcessor { public Exception Process(Exception error, ProcessingErrorInfo catchBlockProcessErrorInfo = null, CancellationToken cancellationToken = default) From 430fa8c40c98050d48d225fc5d589b66e61b8a85 Mon Sep 17 00:00:00 2001 From: kolan72 Date: Tue, 10 Dec 2024 13:15:14 +0300 Subject: [PATCH 32/51] Introduce `SimplePolicyProcessor.ExecuteAsync(Func, TErrorContext, bool, CancellationToken)` method. --- src/Simple/SimplePolicyProcessor.cs | 25 ++++++++++++++++------- tests/SimplePolicyProcessorTests.cs | 31 +++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/src/Simple/SimplePolicyProcessor.cs b/src/Simple/SimplePolicyProcessor.cs index 48c9249..c9d5276 100644 --- a/src/Simple/SimplePolicyProcessor.cs +++ b/src/Simple/SimplePolicyProcessor.cs @@ -111,6 +111,12 @@ private PolicyResult Execute(Action action, EmptyErrorContext emptyErrorContext, return result; } + /// + public PolicyResult Execute(Func func, CancellationToken token = default) + { + return Execute(func, _emptyErrorContext, token); + } + public PolicyResult Execute(Func func, TParam param, CancellationToken token = default) { return Execute(func.Apply(param), param, token); @@ -122,12 +128,6 @@ public PolicyResult Execute(Func func, TErrorContext par return Execute(func, (EmptyErrorContext)emptyContext, token); } - /// - public PolicyResult Execute(Func func, CancellationToken token = default) - { - return Execute(func, _emptyErrorContext, token); - } - private PolicyResult Execute(Func func, EmptyErrorContext emptyErrorContext, CancellationToken token = default) { if (func == null) @@ -181,6 +181,17 @@ private PolicyResult Execute(Func func, EmptyErrorContext emptyErrorCon /// public async Task ExecuteAsync(Func func, bool configureAwait = false, CancellationToken token = default) + { + return await ExecuteAsync(func, _emptyErrorContext, configureAwait, token).ConfigureAwait(configureAwait); + } + + public async Task ExecuteAsync(Func func, TErrorContext param, bool configureAwait = false, CancellationToken token = default) + { + var emptyContext = new EmptyErrorContext(param); + return await ExecuteAsync(func, (EmptyErrorContext)emptyContext, configureAwait, token).ConfigureAwait(configureAwait); + } + + private async Task ExecuteAsync(Func func, EmptyErrorContext emptyErrorContext, bool configureAwait = false, CancellationToken token = default) { if (func == null) return new PolicyResult().WithNoDelegateException(); @@ -221,7 +232,7 @@ public async Task ExecuteAsync(Func func, result.AddError(ex); result.ChangeByHandleCatchBlockResult(await GetCatchBlockAsyncHandler(result, configureAwait, token) - .HandleAsync(ex, _emptyErrorContext).ConfigureAwait(configureAwait)); + .HandleAsync(ex, emptyErrorContext).ConfigureAwait(configureAwait)); } return result; } diff --git a/tests/SimplePolicyProcessorTests.cs b/tests/SimplePolicyProcessorTests.cs index 72626f9..615d8bd 100644 --- a/tests/SimplePolicyProcessorTests.cs +++ b/tests/SimplePolicyProcessorTests.cs @@ -873,6 +873,37 @@ void action(Exception _, ProcessingErrorInfo pi) Assert.That(result.IsSuccess, Is.True); } + [Test] + [TestCase(true)] + [TestCase(false)] + public async Task Should_ExecuteAsync_With_TParam_For_NonGeneric_AsyncFunc_WithErrorProcessorOf_Action_Process_Correctly(bool throwEx) + { + int m = 0; + + void action(Exception _, ProcessingErrorInfo pi) + { + m = pi.Param; + } + + var processor = new SimplePolicyProcessor(true) + .WithErrorProcessorOf(action); + + PolicyResult result = null; + if (throwEx) + { + result = await processor.ExecuteAsync(async(_) => { await Task.Delay(1); throw new InvalidOperationException(); }, 5); + Assert.That(m, Is.EqualTo(5)); + Assert.That(result.NoError, Is.False); + } + else + { + result = await processor.ExecuteAsync(async (_) => await Task.Delay(1), 5); + Assert.That(m, Is.EqualTo(0)); + Assert.That(result.NoError, Is.True); + } + Assert.That(result.IsSuccess, Is.True); + } + private class TestErrorProcessor : IErrorProcessor { public Exception Process(Exception error, ProcessingErrorInfo catchBlockProcessErrorInfo = null, CancellationToken cancellationToken = default) From 706dee2d48cf3f13dd7a3ba23fde2e0d2c3864c7 Mon Sep 17 00:00:00 2001 From: kolan72 Date: Wed, 11 Dec 2024 05:37:08 +0300 Subject: [PATCH 33/51] Rename the `WithErrorProcessorOf` methods of the `SimplePolicyProcessor` class to `WithErrorContextProcessorOf`. --- src/Simple/SimplePolicyProcessor.cs | 26 +++++++++++++------------- tests/SimplePolicyProcessorTests.cs | 20 ++++++++++---------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/Simple/SimplePolicyProcessor.cs b/src/Simple/SimplePolicyProcessor.cs index c9d5276..43247d7 100644 --- a/src/Simple/SimplePolicyProcessor.cs +++ b/src/Simple/SimplePolicyProcessor.cs @@ -285,37 +285,37 @@ public async Task> ExecuteAsync(Func(Action> actionProcessor) + public SimplePolicyProcessor WithErrorContextProcessorOf(Action> actionProcessor) { - return WithErrorProcessor(new DefaultErrorProcessor(actionProcessor)); + return WithErrorContextProcessor(new DefaultErrorProcessor(actionProcessor)); } - public SimplePolicyProcessor WithErrorProcessorOf(Action, CancellationToken> actionProcessor) + public SimplePolicyProcessor WithErrorContextProcessorOf(Action, CancellationToken> actionProcessor) { - return WithErrorProcessor(new DefaultErrorProcessor(actionProcessor)); + return WithErrorContextProcessor(new DefaultErrorProcessor(actionProcessor)); } - public SimplePolicyProcessor WithErrorProcessorOf(Action> actionProcessor, CancellationType cancellationType) + public SimplePolicyProcessor WithErrorContextProcessorOf(Action> actionProcessor, CancellationType cancellationType) { - return WithErrorProcessor(new DefaultErrorProcessor(actionProcessor, cancellationType)); + return WithErrorContextProcessor(new DefaultErrorProcessor(actionProcessor, cancellationType)); } - public SimplePolicyProcessor WithErrorProcessorOf(Func, Task> funcProcessor) + public SimplePolicyProcessor WithErrorContextProcessorOf(Func, Task> funcProcessor) { - return WithErrorProcessor(new DefaultErrorProcessor(funcProcessor)); + return WithErrorContextProcessor(new DefaultErrorProcessor(funcProcessor)); } - public SimplePolicyProcessor WithErrorProcessorOf(Func, Task> funcProcessor, CancellationType cancellationType) + public SimplePolicyProcessor WithErrorContextProcessorOf(Func, Task> funcProcessor, CancellationType cancellationType) { - return WithErrorProcessor(new DefaultErrorProcessor(funcProcessor, cancellationType)); + return WithErrorContextProcessor(new DefaultErrorProcessor(funcProcessor, cancellationType)); } - public SimplePolicyProcessor WithErrorProcessorOf(Func, CancellationToken, Task> funcProcessor) + public SimplePolicyProcessor WithErrorContextProcessorOf(Func, CancellationToken, Task> funcProcessor) { - return WithErrorProcessor(new DefaultErrorProcessor(funcProcessor)); + return WithErrorContextProcessor(new DefaultErrorProcessor(funcProcessor)); } - public SimplePolicyProcessor WithErrorProcessor(DefaultErrorProcessor errorProcessor) + public SimplePolicyProcessor WithErrorContextProcessor(DefaultErrorProcessor errorProcessor) { AddErrorProcessor(errorProcessor); return this; diff --git a/tests/SimplePolicyProcessorTests.cs b/tests/SimplePolicyProcessorTests.cs index 615d8bd..88b92e4 100644 --- a/tests/SimplePolicyProcessorTests.cs +++ b/tests/SimplePolicyProcessorTests.cs @@ -641,12 +641,12 @@ void action(Exception _, ProcessingErrorInfo pi) if (!withCancellationType) { processor = new SimplePolicyProcessor(true) - .WithErrorProcessorOf(action); + .WithErrorContextProcessorOf(action); } else { processor = new SimplePolicyProcessor(true) - .WithErrorProcessorOf(action, CancellationType.Precancelable); + .WithErrorContextProcessorOf(action, CancellationType.Precancelable); } PolicyResult result = null; @@ -685,12 +685,12 @@ async Task fn(Exception _, ProcessingErrorInfo pi) if (!withCancellationType) { processor = new SimplePolicyProcessor(true) - .WithErrorProcessorOf(fn); + .WithErrorContextProcessorOf(fn); } else { processor = new SimplePolicyProcessor(true) - .WithErrorProcessorOf(fn, CancellationType.Precancelable); + .WithErrorContextProcessorOf(fn, CancellationType.Precancelable); } PolicyResult result = null; @@ -724,7 +724,7 @@ async Task fn(Exception _, ProcessingErrorInfo pi, CancellationToken __) } var processor = new SimplePolicyProcessor(true) - .WithErrorProcessorOf(fn); + .WithErrorContextProcessorOf(fn); PolicyResult result; @@ -756,7 +756,7 @@ void action(Exception _, ProcessingErrorInfo pi, CancellationToken __) } var processor = new SimplePolicyProcessor(true) - .WithErrorProcessorOf(action); + .WithErrorContextProcessorOf(action); PolicyResult result; @@ -789,7 +789,7 @@ void action(Exception _, ProcessingErrorInfo pi) } var processor = new SimplePolicyProcessor(true) - .WithErrorProcessorOf(action); + .WithErrorContextProcessorOf(action); PolicyResult result = null; if (throwEx) @@ -822,7 +822,7 @@ void action(Exception _, ProcessingErrorInfo pi) } var processor = new SimplePolicyProcessor(true) - .WithErrorProcessorOf(action); + .WithErrorContextProcessorOf(action); PolicyResult result = null; if (throwEx) @@ -854,7 +854,7 @@ void action(Exception _, ProcessingErrorInfo pi) } var processor = new SimplePolicyProcessor(true) - .WithErrorProcessorOf(action); + .WithErrorContextProcessorOf(action); PolicyResult result = null; if (throwEx) @@ -886,7 +886,7 @@ void action(Exception _, ProcessingErrorInfo pi) } var processor = new SimplePolicyProcessor(true) - .WithErrorProcessorOf(action); + .WithErrorContextProcessorOf(action); PolicyResult result = null; if (throwEx) From 4417b4908c1497a630e04963aa0ae6ea6a3569cc Mon Sep 17 00:00:00 2001 From: kolan72 Date: Wed, 11 Dec 2024 18:29:32 +0300 Subject: [PATCH 34/51] Introduce `SimplePolicyProcessor.ExecuteAsync(Func, TParam, bool, CancellationToken)` method. --- src/Simple/SimplePolicyProcessor.cs | 5 +++++ tests/SimplePolicyProcessorTests.cs | 33 +++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/src/Simple/SimplePolicyProcessor.cs b/src/Simple/SimplePolicyProcessor.cs index 43247d7..0b7cb04 100644 --- a/src/Simple/SimplePolicyProcessor.cs +++ b/src/Simple/SimplePolicyProcessor.cs @@ -185,6 +185,11 @@ public async Task ExecuteAsync(Func func, return await ExecuteAsync(func, _emptyErrorContext, configureAwait, token).ConfigureAwait(configureAwait); } + public async Task ExecuteAsync(Func func, TParam param, bool configureAwait = false, CancellationToken token = default) + { + return await ExecuteAsync(func.Apply(param), param, configureAwait, token).ConfigureAwait(configureAwait); + } + public async Task ExecuteAsync(Func func, TErrorContext param, bool configureAwait = false, CancellationToken token = default) { var emptyContext = new EmptyErrorContext(param); diff --git a/tests/SimplePolicyProcessorTests.cs b/tests/SimplePolicyProcessorTests.cs index 88b92e4..68ca574 100644 --- a/tests/SimplePolicyProcessorTests.cs +++ b/tests/SimplePolicyProcessorTests.cs @@ -904,6 +904,39 @@ void action(Exception _, ProcessingErrorInfo pi) Assert.That(result.IsSuccess, Is.True); } + [Test] + [TestCase(true)] + [TestCase(false)] + public async Task Should_ExecuteAsync_With_TParam_For_NonGeneric_AsyncFunc__With_TParam_WithErrorProcessorOf_Action_Process_Correctly(bool throwEx) + { + int m = 0; + int addable = 1; + + void action(Exception _, ProcessingErrorInfo pi) + { + m = pi.Param; + } + + var processor = new SimplePolicyProcessor(true) + .WithErrorContextProcessorOf(action); + + PolicyResult result = null; + if (throwEx) + { + result = await processor.ExecuteAsync(async (_, __) => { await Task.Delay(1); throw new InvalidOperationException(); }, 5); + Assert.That(m, Is.EqualTo(5)); + Assert.That(result.NoError, Is.False); + } + else + { + result = await processor.ExecuteAsync(async (v,_) => {await Task.Delay(1); addable += v;}, 5); + Assert.That(m, Is.EqualTo(0)); + Assert.That(addable, Is.EqualTo(6)); + Assert.That(result.NoError, Is.True); + } + Assert.That(result.IsSuccess, Is.True); + } + private class TestErrorProcessor : IErrorProcessor { public Exception Process(Exception error, ProcessingErrorInfo catchBlockProcessErrorInfo = null, CancellationToken cancellationToken = default) From 69d9acee267ec7706849a2bd9486b238467c8756 Mon Sep 17 00:00:00 2001 From: kolan72 Date: Thu, 12 Dec 2024 14:29:50 +0300 Subject: [PATCH 35/51] Introduce `SimplePolicyProcessor.ExecuteAsync(Func>, TErrorContext, bool, CancellationToken)` method. --- src/Simple/SimplePolicyProcessor.cs | 13 +++++++++++- tests/SimplePolicyProcessorTests.cs | 33 ++++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/src/Simple/SimplePolicyProcessor.cs b/src/Simple/SimplePolicyProcessor.cs index 0b7cb04..147b830 100644 --- a/src/Simple/SimplePolicyProcessor.cs +++ b/src/Simple/SimplePolicyProcessor.cs @@ -244,6 +244,17 @@ private async Task ExecuteAsync(Func func /// public async Task> ExecuteAsync(Func> func, bool configureAwait = false, CancellationToken token = default) + { + return await ExecuteAsync(func, _emptyErrorContext, configureAwait, token).ConfigureAwait(configureAwait); + } + + public async Task> ExecuteAsync(Func> func, TErrorContext param, bool configureAwait = false, CancellationToken token = default) + { + var emptyContext = new EmptyErrorContext(param); + return await ExecuteAsync(func, (EmptyErrorContext)emptyContext, configureAwait, token).ConfigureAwait(configureAwait); + } + + private async Task> ExecuteAsync(Func> func, EmptyErrorContext emptyErrorContext, bool configureAwait = false, CancellationToken token = default) { if (func == null) return new PolicyResult().WithNoDelegateException(); @@ -285,7 +296,7 @@ public async Task> ExecuteAsync(Func(result, configureAwait, token) - .HandleAsync(ex, _emptyErrorContext).ConfigureAwait(configureAwait)); + .HandleAsync(ex, emptyErrorContext).ConfigureAwait(configureAwait)); } return result; } diff --git a/tests/SimplePolicyProcessorTests.cs b/tests/SimplePolicyProcessorTests.cs index 68ca574..5aec892 100644 --- a/tests/SimplePolicyProcessorTests.cs +++ b/tests/SimplePolicyProcessorTests.cs @@ -907,7 +907,7 @@ void action(Exception _, ProcessingErrorInfo pi) [Test] [TestCase(true)] [TestCase(false)] - public async Task Should_ExecuteAsync_With_TParam_For_NonGeneric_AsyncFunc__With_TParam_WithErrorProcessorOf_Action_Process_Correctly(bool throwEx) + public async Task Should_ExecuteAsync_With_TParam_For_NonGeneric_AsyncFunc_With_TParam_WithErrorProcessorOf_Action_Process_Correctly(bool throwEx) { int m = 0; int addable = 1; @@ -937,6 +937,37 @@ void action(Exception _, ProcessingErrorInfo pi) Assert.That(result.IsSuccess, Is.True); } + [Test] + [TestCase(true)] + [TestCase(false)] + public async Task Should_ExecuteAsync_With_TParam_For_Generic_AsyncFunc_WithErrorProcessorOf_Action_Process_Correctly(bool throwEx) + { + int m = 0; + + void action(Exception _, ProcessingErrorInfo pi) + { + m = pi.Param; + } + + var processor = new SimplePolicyProcessor(true) + .WithErrorContextProcessorOf(action); + + PolicyResult result = null; + if (throwEx) + { + result = await processor.ExecuteAsync(async (_) => { await Task.Delay(1); throw new InvalidOperationException(); }, 5); + Assert.That(m, Is.EqualTo(5)); + Assert.That(result.NoError, Is.False); + } + else + { + result = await processor.ExecuteAsync(async (_) => { await Task.Delay(1); return 1; }, 5); + Assert.That(m, Is.EqualTo(0)); + Assert.That(result.NoError, Is.True); + } + Assert.That(result.IsSuccess, Is.True); + } + private class TestErrorProcessor : IErrorProcessor { public Exception Process(Exception error, ProcessingErrorInfo catchBlockProcessErrorInfo = null, CancellationToken cancellationToken = default) From 3aa20e10adf683d05f62741e9eebcb923ec89540 Mon Sep 17 00:00:00 2001 From: kolan72 Date: Thu, 12 Dec 2024 17:05:04 +0300 Subject: [PATCH 36/51] Deprecate the `BulkErrorProcessor(PolicyAlias)` constructor. --- src/ErrorProcessors/BulkErrorProcessor.cs | 18 +++++++++--------- src/ErrorProcessors/ProcessingErrorContext.cs | 7 ++----- src/Fallback/DefaultFallbackProcessor.cs | 4 ++-- src/PolicyProcessor.cs | 5 ++--- src/Retry/DefaultRetryProcessor.cs | 2 +- src/Retry/RetryProcessingErrorContext.cs | 2 +- src/Retry/RetryProcessingErrorInfo.cs | 2 +- tests/BulkErrorProcessorTests.cs | 16 ++++++++-------- tests/CatchBlockHandlersTests.cs | 6 +++--- tests/DefaultRetryProcessorAsyncTests.cs | 4 ++-- tests/DefaultRetryProcessorTests.cs | 4 ++-- tests/IErrorProcessorRegistration.cs | 2 +- 12 files changed, 34 insertions(+), 38 deletions(-) diff --git a/src/ErrorProcessors/BulkErrorProcessor.cs b/src/ErrorProcessors/BulkErrorProcessor.cs index 076462b..664848c 100644 --- a/src/ErrorProcessors/BulkErrorProcessor.cs +++ b/src/ErrorProcessors/BulkErrorProcessor.cs @@ -11,14 +11,14 @@ public class BulkErrorProcessor : IBulkErrorProcessor { private readonly List _errorProcessors = new List(); - private readonly PolicyAlias _policyAlias; + public BulkErrorProcessor() {} - public BulkErrorProcessor(): this(PolicyAlias.NotSet) {} - - public BulkErrorProcessor(PolicyAlias policyAlias) - { - _policyAlias = policyAlias; - } +#pragma warning disable S1133 // Deprecated code should be removed + [Obsolete("This constructor is obsolete. Use parameterless constructor instead.")] +#pragma warning restore S1133 // Deprecated code should be removed +#pragma warning disable RCS1163 // Unused parameter. + public BulkErrorProcessor(PolicyAlias policyAlias){} +#pragma warning restore RCS1163 // Unused parameter. public void AddProcessor(IErrorProcessor errorProcessor) { @@ -33,7 +33,7 @@ public BulkProcessResult Process(Exception handlingError, ProcessingErrorContext return new BulkProcessResult(handlingError, errorProcessorExceptions); } - var catchBlockProcessErrorInfo = errorContext.ToProcessingErrorInfo(_policyAlias); + var catchBlockProcessErrorInfo = errorContext.ToProcessingErrorInfo(); var curError = handlingError; var isCanceledBetweenProcessOne = false; foreach (var errorProcessor in _errorProcessors) @@ -83,7 +83,7 @@ public async Task ProcessAsync(Exception handlingError, Proce return new BulkProcessResult(handlingError, errorProcessorExceptions); } - var catchBlockProcessErrorInfo = errorContext.ToProcessingErrorInfo(_policyAlias); + var catchBlockProcessErrorInfo = errorContext.ToProcessingErrorInfo(); var curError = handlingError; var isCanceledBetweenProcessOne = false; foreach (var errorProcessor in _errorProcessors) diff --git a/src/ErrorProcessors/ProcessingErrorContext.cs b/src/ErrorProcessors/ProcessingErrorContext.cs index aa1c6a1..6bfc1ec 100644 --- a/src/ErrorProcessors/ProcessingErrorContext.cs +++ b/src/ErrorProcessors/ProcessingErrorContext.cs @@ -15,12 +15,9 @@ public class ProcessingErrorContext internal PolicyAlias PolicyKind { get; set; } - internal virtual ProcessingErrorInfo ToProcessingErrorInfo(PolicyAlias policyAlias) + internal virtual ProcessingErrorInfo ToProcessingErrorInfo() { - if (policyAlias != PolicyAlias.NotSet) - return new ProcessingErrorInfo(policyAlias, this); - else - return new ProcessingErrorInfo(this); + return new ProcessingErrorInfo(this); } } } diff --git a/src/Fallback/DefaultFallbackProcessor.cs b/src/Fallback/DefaultFallbackProcessor.cs index 393bbfd..8171e5d 100644 --- a/src/Fallback/DefaultFallbackProcessor.cs +++ b/src/Fallback/DefaultFallbackProcessor.cs @@ -7,9 +7,9 @@ namespace PoliNorError public sealed class DefaultFallbackProcessor : PolicyProcessor, IFallbackProcessor { private readonly EmptyErrorContext _emptyErrorContext; - public DefaultFallbackProcessor(IBulkErrorProcessor bulkErrorProcessor = null) : base(PolicyAlias.Fallback, bulkErrorProcessor) + public DefaultFallbackProcessor(IBulkErrorProcessor bulkErrorProcessor = null) : base(bulkErrorProcessor) { - _emptyErrorContext = _isPolicyAliasSet ? EmptyErrorContext.Default : EmptyErrorContext.DefaultFallback; + _emptyErrorContext = EmptyErrorContext.DefaultFallback; } public PolicyResult Fallback(Action action, Action fallback, CancellationToken token = default) diff --git a/src/PolicyProcessor.cs b/src/PolicyProcessor.cs index 9f8dd69..2506de2 100644 --- a/src/PolicyProcessor.cs +++ b/src/PolicyProcessor.cs @@ -13,14 +13,13 @@ public abstract class PolicyProcessor : IPolicyProcessor protected bool _isPolicyAliasSet; - protected PolicyProcessor(PolicyAlias policyAlias, IBulkErrorProcessor bulkErrorProcessor = null) : this(policyAlias, new ExceptionFilter(), bulkErrorProcessor) + protected PolicyProcessor(IBulkErrorProcessor bulkErrorProcessor = null) : this(PolicyAlias.NotSet, new ExceptionFilter(), bulkErrorProcessor) { } protected PolicyProcessor(PolicyAlias policyAlias, ExceptionFilter exceptionFilter, IBulkErrorProcessor bulkErrorProcessor = null) { - _bulkErrorProcessor = bulkErrorProcessor ?? new BulkErrorProcessor(policyAlias); - _isPolicyAliasSet = bulkErrorProcessor == null; + _bulkErrorProcessor = bulkErrorProcessor ?? new BulkErrorProcessor(); ErrorFilter = exceptionFilter; } diff --git a/src/Retry/DefaultRetryProcessor.cs b/src/Retry/DefaultRetryProcessor.cs index f17aab1..38d98b5 100644 --- a/src/Retry/DefaultRetryProcessor.cs +++ b/src/Retry/DefaultRetryProcessor.cs @@ -23,7 +23,7 @@ public DefaultRetryProcessor(IBulkErrorProcessor bulkErrorProcessor, bool failed internal DefaultRetryProcessor(IDelayProvider delayProvider): this(null, false, delayProvider) {} internal DefaultRetryProcessor(IBulkErrorProcessor bulkErrorProcessor, bool failedIfSaveErrorThrows, IDelayProvider delayProvider = null) - : base(PolicyAlias.Retry, bulkErrorProcessor) + : base(bulkErrorProcessor) { _failedIfSaveErrorThrows = failedIfSaveErrorThrows; _retryErrorContextCreator = CreateRetryErrorContext; diff --git a/src/Retry/RetryProcessingErrorContext.cs b/src/Retry/RetryProcessingErrorContext.cs index 0c5e54c..59ab707 100644 --- a/src/Retry/RetryProcessingErrorContext.cs +++ b/src/Retry/RetryProcessingErrorContext.cs @@ -7,7 +7,7 @@ public RetryProcessingErrorContext(int retryCount) : base(PolicyAlias.Retry) RetryCount = retryCount; } - internal override ProcessingErrorInfo ToProcessingErrorInfo(PolicyAlias policyAlias) + internal override ProcessingErrorInfo ToProcessingErrorInfo() { return new RetryProcessingErrorInfo(RetryCount); } diff --git a/src/Retry/RetryProcessingErrorInfo.cs b/src/Retry/RetryProcessingErrorInfo.cs index 031d603..6c5f76a 100644 --- a/src/Retry/RetryProcessingErrorInfo.cs +++ b/src/Retry/RetryProcessingErrorInfo.cs @@ -2,7 +2,7 @@ { public class RetryProcessingErrorInfo : ProcessingErrorInfo { - internal RetryProcessingErrorInfo(int retryAttempt) : base(PolicyAlias.Retry, new RetryProcessingErrorContext(retryAttempt)) + internal RetryProcessingErrorInfo(int retryAttempt) : base(new RetryProcessingErrorContext(retryAttempt)) { #pragma warning disable CS0618 // Type or member is obsolete CurrentRetryCount = retryAttempt; diff --git a/tests/BulkErrorProcessorTests.cs b/tests/BulkErrorProcessorTests.cs index 3d60821..57e6aa5 100644 --- a/tests/BulkErrorProcessorTests.cs +++ b/tests/BulkErrorProcessorTests.cs @@ -15,7 +15,7 @@ internal class BulkErrorProcessorTests [Test] public async Task Should_ProcessAsync_Return_Status_None_When_No_Processors() { - var bulkProcessor = new BulkErrorProcessor(PolicyAlias.Retry); + var bulkProcessor = new BulkErrorProcessor(); var res = await bulkProcessor.ProcessAsync(new Exception(), new RetryProcessingErrorContext(1), CancellationToken.None); ClassicAssert.IsTrue(!res.ProcessErrors.Any()); } @@ -23,7 +23,7 @@ public async Task Should_ProcessAsync_Return_Status_None_When_No_Processors() [Test] public async Task Should_ProcessAsync_Return_Status_ProcessorException_When_ProcessorWithError() { - var bulkProcessor = new BulkErrorProcessor(PolicyAlias.Retry); + var bulkProcessor = new BulkErrorProcessor(); var mockedErrorProcessor = Substitute.For(); @@ -40,7 +40,7 @@ public async Task Should_ProcessAsync_Return_Status_ProcessorException_When_Proc [Test] public async Task Should_ProcessAsync_Return_Status_Success_And_CorrectProcessor() { - var bulkProcessor = new BulkErrorProcessor(PolicyAlias.Retry); + var bulkProcessor = new BulkErrorProcessor(); var exc = new Exception(); @@ -55,7 +55,7 @@ public async Task Should_ProcessAsync_Return_Status_Success_And_CorrectProcessor [Test] public void Should_Process_Return_Status_None_When_No_Processors() { - var bulkProcessor = new BulkErrorProcessor(PolicyAlias.Retry); + var bulkProcessor = new BulkErrorProcessor(); var res = bulkProcessor.Process(new Exception(), new RetryProcessingErrorContext(1), CancellationToken.None); ClassicAssert.IsTrue(!res.ProcessErrors.Any()); } @@ -63,7 +63,7 @@ public void Should_Process_Return_Status_None_When_No_Processors() [Test] public void Should_Process_Return_Status_ProcessorException_When_ProcessorWithError2() { - var bulkProcessor = new BulkErrorProcessor(PolicyAlias.Retry); + var bulkProcessor = new BulkErrorProcessor(); var exc = new Exception(); @@ -79,7 +79,7 @@ public void Should_Process_Return_Status_ProcessorException_When_ProcessorWithEr [Test] public void Should_Process_Return_Status_Success_When_CorrectProcessor() { - var bulkProcessor = new BulkErrorProcessor(PolicyAlias.Retry); + var bulkProcessor = new BulkErrorProcessor(); var exc = new Exception(); @@ -96,7 +96,7 @@ public void Should_Process_NotCallOtherProcessor_If_Canceled() { using (var cancelTokenSource = new CancellationTokenSource()) { - var bulkProcessor = new BulkErrorProcessor(PolicyAlias.Retry); + var bulkProcessor = new BulkErrorProcessor(); cancelTokenSource.CancelAfter(500); var delayProcessor = new DelayErrorProcessor(TimeSpan.FromMilliseconds(1000)); bulkProcessor.AddProcessor(delayProcessor); @@ -114,7 +114,7 @@ public async Task Should_ProcessAsync_NotCallOtherProcessor_If_Canceled() { using (var cancelTokenSource = new CancellationTokenSource()) { - var bulkProcessor = new BulkErrorProcessor(PolicyAlias.Retry); + var bulkProcessor = new BulkErrorProcessor(); cancelTokenSource.CancelAfter(500); var delayProcessor = new DelayErrorProcessor(TimeSpan.FromMilliseconds(1000)); bulkProcessor.AddProcessor(delayProcessor); diff --git a/tests/CatchBlockHandlersTests.cs b/tests/CatchBlockHandlersTests.cs index ee1c247..6435e26 100644 --- a/tests/CatchBlockHandlersTests.cs +++ b/tests/CatchBlockHandlersTests.cs @@ -14,7 +14,7 @@ internal class CatchBlockHandlersTests [Test] public void Should_PolicyProcessorCatchBlockSyncHandler_Be_Cancelable() { - var bulkProcessor = new BulkErrorProcessor(PolicyAlias.Simple); + var bulkProcessor = new BulkErrorProcessor(); bulkProcessor.AddProcessor(new BasicErrorProcessor()); var filter = new PolicyProcessor.ExceptionFilter(); using (var cancelTokenSource = new CancellationTokenSource()) @@ -36,7 +36,7 @@ public void Should_PolicyProcessorCatchBlockSyncHandler_Be_Cancelable() [Test] public async Task Should_PolicyProcessorCatchBlockAsyncHandler_Be_Cancelable() { - var bulkProcessor = new BulkErrorProcessor(PolicyAlias.Simple); + var bulkProcessor = new BulkErrorProcessor(); bulkProcessor.AddProcessor(new BasicErrorProcessor()); var filter = new PolicyProcessor.ExceptionFilter(); using (var cancelTokenSource = new CancellationTokenSource()) @@ -63,7 +63,7 @@ public async Task Should_PolicyProcessorCatchBlockAsyncHandler_Be_Cancelable() [TestCase(1, HandleCatchBlockResult.Success, false)] public async Task Should_PolicyProcessorCatchBlockHandler_When_ErrorContext_CanNotBeHandled_Returns_FailedByPolicyRules(int retryCount, HandleCatchBlockResult result, bool sync) { - var bulkProcessor = new BulkErrorProcessor(PolicyAlias.Simple); + var bulkProcessor = new BulkErrorProcessor(); bulkProcessor.AddProcessor(new BasicErrorProcessor()); var filter = new PolicyProcessor.ExceptionFilter(); diff --git a/tests/DefaultRetryProcessorAsyncTests.cs b/tests/DefaultRetryProcessorAsyncTests.cs index 19d602f..a4fae3d 100644 --- a/tests/DefaultRetryProcessorAsyncTests.cs +++ b/tests/DefaultRetryProcessorAsyncTests.cs @@ -50,7 +50,7 @@ public async Task Should_RetryAsyncT_BeCancelable() public async Task Should_RetryAsync_NotBreak_When_ErrorProcessing_Faulted(bool isGeneric) { var throwingExc = new ApplicationException(); - var bulkProcessor = new BulkErrorProcessor(PolicyAlias.Retry); + var bulkProcessor = new BulkErrorProcessor(); bulkProcessor.AddProcessor(new BasicErrorProcessor((_, __) => throw new Exception("Test"))); var processor = RetryProcessor.CreateDefault(bulkProcessor); @@ -79,7 +79,7 @@ public async Task Should_RetryAsync_Break_When_ErrorProcessing_Canceled(bool isG { var cancelSource = new CancellationTokenSource(); - var bulkProcessor = new BulkErrorProcessor(PolicyAlias.Retry); + var bulkProcessor = new BulkErrorProcessor(); bulkProcessor.AddProcessor(new BasicErrorProcessor((_, __) => cancelSource.Cancel())); bulkProcessor.AddProcessor(new BasicErrorProcessor((_, __) => { })); diff --git a/tests/DefaultRetryProcessorTests.cs b/tests/DefaultRetryProcessorTests.cs index ff5230c..230a3d7 100644 --- a/tests/DefaultRetryProcessorTests.cs +++ b/tests/DefaultRetryProcessorTests.cs @@ -275,7 +275,7 @@ public void Should_Retry_Throw_When_Task_IsReturnType() public void Should_Retry_NotBreak_When_ErrorProcessing_Faulted(bool isGeneric) { var throwingExc = new ApplicationException(); - var bulkProcessor = new BulkErrorProcessor(PolicyAlias.Retry); + var bulkProcessor = new BulkErrorProcessor(); bulkProcessor.AddProcessor(new BasicErrorProcessor((_, __) => throw new Exception("Test"))); var processor = RetryProcessor.CreateDefault(bulkProcessor); @@ -304,7 +304,7 @@ public void Should_Retry_Break_When_ErrorProcessing_Canceled(bool isGeneric) { var cancelSource = new CancellationTokenSource(); - var bulkProcessor = new BulkErrorProcessor(PolicyAlias.Retry); + var bulkProcessor = new BulkErrorProcessor(); bulkProcessor.AddProcessor(new BasicErrorProcessor((_, __) => cancelSource.Cancel())); bulkProcessor.AddProcessor(new BasicErrorProcessor((_, __) => { })); diff --git a/tests/IErrorProcessorRegistration.cs b/tests/IErrorProcessorRegistration.cs index 14b6514..148a40a 100644 --- a/tests/IErrorProcessorRegistration.cs +++ b/tests/IErrorProcessorRegistration.cs @@ -145,7 +145,7 @@ public void WithErrorProcessorOf(Func funcProcessor, Cancellati public class BulkErrorProcessorErrorProcessorRegistration : IErrorProcessorRegistration { - private readonly BulkErrorProcessor _processor = new BulkErrorProcessor(PolicyAlias.Simple); + private readonly BulkErrorProcessor _processor = new BulkErrorProcessor(); public int Count => _processor.Count(); public void WithErrorProcessor(IErrorProcessor errorProcessor) => _processor.WithErrorProcessor(new DefaultErrorProcessor()); From 9a341bfb922acf3d2518a1ca4d1c379f33c5f118 Mon Sep 17 00:00:00 2001 From: kolan72 Date: Fri, 13 Dec 2024 15:35:56 +0300 Subject: [PATCH 37/51] Introduce `SimplePolicyProcessor.ExecuteAsync(Func>, TParam, bool, CancellationToken)` method. --- src/Simple/SimplePolicyProcessor.cs | 5 +++++ tests/SimplePolicyProcessorTests.cs | 34 +++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/src/Simple/SimplePolicyProcessor.cs b/src/Simple/SimplePolicyProcessor.cs index 147b830..c6180a7 100644 --- a/src/Simple/SimplePolicyProcessor.cs +++ b/src/Simple/SimplePolicyProcessor.cs @@ -248,6 +248,11 @@ public async Task> ExecuteAsync(Func> ExecuteAsync(Func> func, TParam param, bool configureAwait = false, CancellationToken token = default) + { + return await ExecuteAsync(func.Apply(param), param, configureAwait, token).ConfigureAwait(configureAwait); + } + public async Task> ExecuteAsync(Func> func, TErrorContext param, bool configureAwait = false, CancellationToken token = default) { var emptyContext = new EmptyErrorContext(param); diff --git a/tests/SimplePolicyProcessorTests.cs b/tests/SimplePolicyProcessorTests.cs index 5aec892..f7def7e 100644 --- a/tests/SimplePolicyProcessorTests.cs +++ b/tests/SimplePolicyProcessorTests.cs @@ -968,6 +968,40 @@ void action(Exception _, ProcessingErrorInfo pi) Assert.That(result.IsSuccess, Is.True); } + [Test] + [TestCase(true)] + [TestCase(false)] + public async Task Should_ExecuteAsync_With_TParam_For_Generic_AsyncFunc_With_TParam_WithErrorProcessorOf_Action_Process_Correctly(bool throwEx) + { + int m = 0; + int addable = 1; + + void action(Exception _, ProcessingErrorInfo pi) + { + m = pi.Param; + } + + var processor = new SimplePolicyProcessor(true) + .WithErrorContextProcessorOf(action); + + PolicyResult result = null; + if (throwEx) + { + result = await processor.ExecuteAsync(async (_, __) => { await Task.Delay(1); throw new InvalidOperationException(); }, 5); + Assert.That(m, Is.EqualTo(5)); + Assert.That(result.NoError, Is.False); + } + else + { + result = await processor.ExecuteAsync(async (v, _) => { await Task.Delay(1); addable += v; return addable; }, 5); + Assert.That(m, Is.EqualTo(0)); + Assert.That(addable, Is.EqualTo(6)); + Assert.That(result.Result, Is.EqualTo(6)); + Assert.That(result.NoError, Is.True); + } + Assert.That(result.IsSuccess, Is.True); + } + private class TestErrorProcessor : IErrorProcessor { public Exception Process(Exception error, ProcessingErrorInfo catchBlockProcessErrorInfo = null, CancellationToken cancellationToken = default) From 5786798a7479b6c9163db6ac9353c1056e25d39f Mon Sep 17 00:00:00 2001 From: kolan72 Date: Fri, 13 Dec 2024 18:37:38 +0300 Subject: [PATCH 38/51] Introduce `SimplePolicy.WithErrorContextProcessor(DefaultErrorProcessor)` method. --- src/Simple/SimplePolicy.cs | 11 +++++++++++ tests/SimplePolicyTests.cs | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/src/Simple/SimplePolicy.cs b/src/Simple/SimplePolicy.cs index 3fbe7ef..d9873d0 100644 --- a/src/Simple/SimplePolicy.cs +++ b/src/Simple/SimplePolicy.cs @@ -204,5 +204,16 @@ public SimplePolicy SetPolicyResultFailedIf(Func, bool> predi { return this.SetPolicyResultFailedWithHandlerIfInner(predicate, onSetPolicyResultFailed); } + + public SimplePolicy WithErrorContextProcessor(DefaultErrorProcessor errorProcessor) + { + if (!(_simpleProcessor is SimplePolicyProcessor processor)) + { + throw new NotImplementedException("This method is only supported for the SimplePolicyProcessor implementation of the ISimplePolicyProcessor interface."); + } + + processor.WithErrorContextProcessor(errorProcessor); + return this; + } } } diff --git a/tests/SimplePolicyTests.cs b/tests/SimplePolicyTests.cs index 3a65d5a..7786af2 100644 --- a/tests/SimplePolicyTests.cs +++ b/tests/SimplePolicyTests.cs @@ -2,6 +2,8 @@ using NUnit.Framework; using NUnit.Framework.Legacy; using System; +using System.Collections; +using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Threading; @@ -734,5 +736,36 @@ public async Task Should_Rethrow_Or_Handle_If_PolicyCreated_With_ThrowIfErrorFil Assert.That(exception.DataContainsKeyStringWithValue(PolinorErrorConsts.EXCEPTION_DATA_ERRORFILTERUNSATISFIED_KEY, true), Is.True); } } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Should_WithErrorContextProcessor_Throws_Only_For_Not_SimplePolicyProcessor(bool throwEx) + { + SimplePolicy simplePolicy; + if (throwEx) + { + simplePolicy = new SimplePolicy(new TestSimplePolicyProcessor()); + Assert.Throws(() => simplePolicy.WithErrorContextProcessor(new DefaultErrorProcessor((_, __) => { }))); + } + else + { + simplePolicy = new SimplePolicy(); + Assert.DoesNotThrow(() => simplePolicy.WithErrorContextProcessor(new DefaultErrorProcessor((_, __) => { }))); + } + } + + public class TestSimplePolicyProcessor : ISimplePolicyProcessor + { + public PolicyProcessor.ExceptionFilter ErrorFilter => throw new NotImplementedException(); + + public void AddErrorProcessor(IErrorProcessor newErrorProcessor) => Expression.Empty(); + public PolicyResult Execute(Action action, CancellationToken token = default) => throw new NotImplementedException(); + public PolicyResult Execute(Func func, CancellationToken token = default) => throw new NotImplementedException(); + public Task ExecuteAsync(Func func, bool configureAwait = false, CancellationToken token = default) => throw new NotImplementedException(); + public Task> ExecuteAsync(Func> func, bool configureAwait = false, CancellationToken token = default) => throw new NotImplementedException(); + public IEnumerator GetEnumerator() => throw new NotImplementedException(); + IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); + } } } From 7bd1653523a77d17c7ea9c3322b826b619b7c8a6 Mon Sep 17 00:00:00 2001 From: kolan72 Date: Sat, 14 Dec 2024 14:43:00 +0300 Subject: [PATCH 39/51] Introduce `SimplePolicy.Handle(Action, TErrorContext, CancellationToken)` method and `WithErrorContextProcessorOf(Action>)` method with overload. --- src/Simple/SimplePolicy.cs | 30 +++++++++++++++++++ tests/SimplePolicyTests.cs | 59 +++++++++++++++++++++++++++++++++++++- 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/src/Simple/SimplePolicy.cs b/src/Simple/SimplePolicy.cs index d9873d0..626a7cd 100644 --- a/src/Simple/SimplePolicy.cs +++ b/src/Simple/SimplePolicy.cs @@ -47,6 +47,26 @@ public PolicyResult Handle(Action action, CancellationToken token = default) return retryResult; } + public PolicyResult Handle(Action action, TErrorContext param, CancellationToken token = default) + { + var (Act, Wrapper) = WrapDelegateIfNeed(action, token); + if (Act == null && Wrapper != null) + { + return new PolicyResult().WithNoDelegateExceptionAndPolicyNameFrom(this); + } + + if (!(_simpleProcessor is SimplePolicyProcessor processor)) + { + throw new NotImplementedException("This method is only supported for the SimplePolicyProcessor implementation of the ISimplePolicyProcessor interface."); + } + var retryResult = processor.Execute(Act, param, token) + .SetWrappedPolicyResults(Wrapper) + .SetPolicyName(PolicyName); + + HandlePolicyResult(retryResult, token); + return retryResult; + } + public PolicyResult Handle(Func func, CancellationToken token = default) { var (Fn, Wrapper) = WrapDelegateIfNeed(func, token); @@ -205,6 +225,16 @@ public SimplePolicy SetPolicyResultFailedIf(Func, bool> predi return this.SetPolicyResultFailedWithHandlerIfInner(predicate, onSetPolicyResultFailed); } + public SimplePolicy WithErrorContextProcessorOf(Action> actionProcessor) + { + return WithErrorContextProcessor(new DefaultErrorProcessor(actionProcessor)); + } + + public SimplePolicy WithErrorContextProcessorOf(Action> actionProcessor, CancellationType cancellationType) + { + return WithErrorContextProcessor(new DefaultErrorProcessor(actionProcessor, cancellationType)); + } + public SimplePolicy WithErrorContextProcessor(DefaultErrorProcessor errorProcessor) { if (!(_simpleProcessor is SimplePolicyProcessor processor)) diff --git a/tests/SimplePolicyTests.cs b/tests/SimplePolicyTests.cs index 7786af2..dcb9a0d 100644 --- a/tests/SimplePolicyTests.cs +++ b/tests/SimplePolicyTests.cs @@ -750,9 +750,66 @@ public void Should_WithErrorContextProcessor_Throws_Only_For_Not_SimplePolicyPro } else { + int m = 0; + + void action(Exception _, ProcessingErrorInfo pi) + { + m = pi.Param; + } simplePolicy = new SimplePolicy(); - Assert.DoesNotThrow(() => simplePolicy.WithErrorContextProcessor(new DefaultErrorProcessor((_, __) => { }))); + + var result = simplePolicy + .WithErrorContextProcessor(new DefaultErrorProcessor(action)) + .Handle(() => throw new InvalidOperationException(), 5); + + Assert.That(result.NoError, Is.False); + Assert.That(result.IsSuccess, Is.True); + + Assert.That(m, Is.EqualTo(5)); + } + } + + [Test] + [TestCase(true, true)] + [TestCase(false, true)] + [TestCase(true, false)] + [TestCase(false, false)] + public void Should_Execute_For_Action_With_Generic_Param_WithErrorProcessorOf_Action_Process_Correctly(bool shouldWork, bool withCancellationType) + { + int m = 0; + + void action(Exception _, ProcessingErrorInfo pi) + { + m = pi.Param; + } + + SimplePolicy policy; + + if (!withCancellationType) + { + policy = new SimplePolicy(true) + .WithErrorContextProcessorOf(action); + } + else + { + policy = new SimplePolicy(true) + .WithErrorContextProcessorOf(action, CancellationType.Precancelable); + } + + PolicyResult result = null; + + if (shouldWork) + { + result = policy.Handle(() => throw new InvalidOperationException(), 5); + Assert.That(m, Is.EqualTo(5)); + } + else + { + result = policy.Handle(() => throw new InvalidOperationException()); + Assert.That(m, Is.EqualTo(0)); } + Assert.That(result.NoError, Is.False); + Assert.That(result.IsSuccess, Is.True); } public class TestSimplePolicyProcessor : ISimplePolicyProcessor From 2f5fd9a60921915b78764dcf368c3badba80f1e4 Mon Sep 17 00:00:00 2001 From: kolan72 Date: Sun, 15 Dec 2024 19:28:30 +0300 Subject: [PATCH 40/51] Introduce `SimplePolicy.WithErrorContextProcessorOf(Action, CancellationToken>)` method. --- src/Simple/SimplePolicy.cs | 5 +++++ tests/SimplePolicyTests.cs | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/src/Simple/SimplePolicy.cs b/src/Simple/SimplePolicy.cs index 626a7cd..61ccaab 100644 --- a/src/Simple/SimplePolicy.cs +++ b/src/Simple/SimplePolicy.cs @@ -235,6 +235,11 @@ public SimplePolicy WithErrorContextProcessorOf(Action(actionProcessor, cancellationType)); } + public SimplePolicy WithErrorContextProcessorOf(Action, CancellationToken> actionProcessor) + { + return WithErrorContextProcessor(new DefaultErrorProcessor(actionProcessor)); + } + public SimplePolicy WithErrorContextProcessor(DefaultErrorProcessor errorProcessor) { if (!(_simpleProcessor is SimplePolicyProcessor processor)) diff --git a/tests/SimplePolicyTests.cs b/tests/SimplePolicyTests.cs index dcb9a0d..a53ad4a 100644 --- a/tests/SimplePolicyTests.cs +++ b/tests/SimplePolicyTests.cs @@ -812,6 +812,38 @@ void action(Exception _, ProcessingErrorInfo pi) Assert.That(result.IsSuccess, Is.True); } + [Test] + [TestCase(true)] + [TestCase(false)] + public void Should_Execute_For_Action_With_Generic_Param_WithErrorProcessorOf_Action_With_Token_Param_Process_Correctly(bool shouldWork) + { + int m = 0; + + void action(Exception _, ProcessingErrorInfo pi, CancellationToken __) + { + m = pi.Param; + } + + var policy = new SimplePolicy(true) + .WithErrorContextProcessorOf(action); + + PolicyResult result; + + if (shouldWork) + { + result = policy.Handle(() => throw new InvalidOperationException(), 5); + Assert.That(m, Is.EqualTo(5)); + } + else + { + result = policy.Handle(() => throw new InvalidOperationException()); + Assert.That(m, Is.EqualTo(0)); + } + + Assert.That(result.NoError, Is.False); + Assert.That(result.IsSuccess, Is.True); + } + public class TestSimplePolicyProcessor : ISimplePolicyProcessor { public PolicyProcessor.ExceptionFilter ErrorFilter => throw new NotImplementedException(); From bc13192b6bf626f281909378ac28e04293f03060 Mon Sep 17 00:00:00 2001 From: kolan72 Date: Mon, 16 Dec 2024 17:40:53 +0300 Subject: [PATCH 41/51] Introduces the `SimplePolicy.WithErrorContextProcessorOf(Func, Task>)` method with an overload with the `CancellationType` parameter. --- src/Simple/SimplePolicy.cs | 10 +++++++++ tests/SimplePolicyTests.cs | 45 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/src/Simple/SimplePolicy.cs b/src/Simple/SimplePolicy.cs index 61ccaab..5c7f610 100644 --- a/src/Simple/SimplePolicy.cs +++ b/src/Simple/SimplePolicy.cs @@ -240,6 +240,16 @@ public SimplePolicy WithErrorContextProcessorOf(Action(actionProcessor)); } + public SimplePolicy WithErrorContextProcessorOf(Func, Task> funcProcessor) + { + return WithErrorContextProcessor(new DefaultErrorProcessor(funcProcessor)); + } + + public SimplePolicy WithErrorContextProcessorOf(Func, Task> funcProcessor, CancellationType cancellationType) + { + return WithErrorContextProcessor(new DefaultErrorProcessor(funcProcessor, cancellationType)); + } + public SimplePolicy WithErrorContextProcessor(DefaultErrorProcessor errorProcessor) { if (!(_simpleProcessor is SimplePolicyProcessor processor)) diff --git a/tests/SimplePolicyTests.cs b/tests/SimplePolicyTests.cs index a53ad4a..3686d54 100644 --- a/tests/SimplePolicyTests.cs +++ b/tests/SimplePolicyTests.cs @@ -844,6 +844,51 @@ void action(Exception _, ProcessingErrorInfo pi, CancellationToken __) Assert.That(result.IsSuccess, Is.True); } + [Test] + [TestCase(true, true)] + [TestCase(false, true)] + [TestCase(true, false)] + [TestCase(false, false)] + public void Should_Execute_For_Action_With_Generic_Param_WithErrorProcessorOf_AsyncFunc_Process_Correctly(bool shouldWork, bool withCancellationType) + { + int m = 0; + + async Task fn(Exception _, ProcessingErrorInfo pi) + { + await Task.Delay(1); + m = pi.Param; + } + + SimplePolicy policy; + + if (!withCancellationType) + { + policy = new SimplePolicy(true) + .WithErrorContextProcessorOf(fn); + } + else + { + policy = new SimplePolicy(true) + .WithErrorContextProcessorOf(fn, CancellationType.Precancelable); + } + + PolicyResult result = null; + + if (shouldWork) + { + result = policy.Handle(() => throw new InvalidOperationException(), 5); + Assert.That(m, Is.EqualTo(5)); + } + else + { + result = policy.Handle(() => throw new InvalidOperationException()); + Assert.That(m, Is.EqualTo(0)); + } + + Assert.That(result.NoError, Is.False); + Assert.That(result.IsSuccess, Is.True); + } + public class TestSimplePolicyProcessor : ISimplePolicyProcessor { public PolicyProcessor.ExceptionFilter ErrorFilter => throw new NotImplementedException(); From fee560d1e8fd74f598cd6d9c2d540b53681ccaa1 Mon Sep 17 00:00:00 2001 From: kolan72 Date: Tue, 17 Dec 2024 12:53:44 +0300 Subject: [PATCH 42/51] Introduce `SimplePolicy.WithErrorContextProcessorOf(Func, CancellationToken, Task>` method. --- src/Simple/SimplePolicy.cs | 5 +++++ tests/SimplePolicyTests.cs | 39 +++++++++++++++++++++++++++++++++++--- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/src/Simple/SimplePolicy.cs b/src/Simple/SimplePolicy.cs index 5c7f610..51bbddd 100644 --- a/src/Simple/SimplePolicy.cs +++ b/src/Simple/SimplePolicy.cs @@ -250,6 +250,11 @@ public SimplePolicy WithErrorContextProcessorOf(Func(funcProcessor, cancellationType)); } + public SimplePolicy WithErrorContextProcessorOf(Func, CancellationToken, Task> funcProcessor) + { + return WithErrorContextProcessor(new DefaultErrorProcessor(funcProcessor)); + } + public SimplePolicy WithErrorContextProcessor(DefaultErrorProcessor errorProcessor) { if (!(_simpleProcessor is SimplePolicyProcessor processor)) diff --git a/tests/SimplePolicyTests.cs b/tests/SimplePolicyTests.cs index 3686d54..d083dcd 100644 --- a/tests/SimplePolicyTests.cs +++ b/tests/SimplePolicyTests.cs @@ -774,7 +774,7 @@ void action(Exception _, ProcessingErrorInfo pi) [TestCase(false, true)] [TestCase(true, false)] [TestCase(false, false)] - public void Should_Execute_For_Action_With_Generic_Param_WithErrorProcessorOf_Action_Process_Correctly(bool shouldWork, bool withCancellationType) + public void Should_Handle_For_Action_With_Generic_Param_WithErrorProcessorOf_Action_Process_Correctly(bool shouldWork, bool withCancellationType) { int m = 0; @@ -815,7 +815,7 @@ void action(Exception _, ProcessingErrorInfo pi) [Test] [TestCase(true)] [TestCase(false)] - public void Should_Execute_For_Action_With_Generic_Param_WithErrorProcessorOf_Action_With_Token_Param_Process_Correctly(bool shouldWork) + public void Should_Handle_For_Action_With_Generic_Param_WithErrorProcessorOf_Action_With_Token_Param_Process_Correctly(bool shouldWork) { int m = 0; @@ -849,7 +849,7 @@ void action(Exception _, ProcessingErrorInfo pi, CancellationToken __) [TestCase(false, true)] [TestCase(true, false)] [TestCase(false, false)] - public void Should_Execute_For_Action_With_Generic_Param_WithErrorProcessorOf_AsyncFunc_Process_Correctly(bool shouldWork, bool withCancellationType) + public void Should_Handle_For_Action_With_Generic_Param_WithErrorProcessorOf_AsyncFunc_Process_Correctly(bool shouldWork, bool withCancellationType) { int m = 0; @@ -889,6 +889,39 @@ async Task fn(Exception _, ProcessingErrorInfo pi) Assert.That(result.IsSuccess, Is.True); } + [Test] + [TestCase(true)] + [TestCase(false)] + public void Should_Handle_For_Action_With_Generic_Param_WithErrorProcessorOf_AsyncFunc_With_Token_Param_Process_Correctly(bool shouldWork) + { + int m = 0; + + async Task fn(Exception _, ProcessingErrorInfo pi, CancellationToken __) + { + await Task.Delay(1); + m = pi.Param; + } + + var policy = new SimplePolicy(true) + .WithErrorContextProcessorOf(fn); + + PolicyResult result; + + if (shouldWork) + { + result = policy.Handle(() => throw new InvalidOperationException(), 5); + Assert.That(m, Is.EqualTo(5)); + } + else + { + result = policy.Handle(() => throw new InvalidOperationException()); + Assert.That(m, Is.EqualTo(0)); + } + + Assert.That(result.NoError, Is.False); + Assert.That(result.IsSuccess, Is.True); + } + public class TestSimplePolicyProcessor : ISimplePolicyProcessor { public PolicyProcessor.ExceptionFilter ErrorFilter => throw new NotImplementedException(); From 00516b224050b94bbceef7eea2e32fdd6d425677 Mon Sep 17 00:00:00 2001 From: kolan72 Date: Tue, 17 Dec 2024 18:37:04 +0300 Subject: [PATCH 43/51] Add `Policy.HasPolicyWrapperFactory` internal property. --- src/Policy.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Policy.cs b/src/Policy.cs index 60f770a..6ef9934 100644 --- a/src/Policy.cs +++ b/src/Policy.cs @@ -111,7 +111,7 @@ protected async Task HandlePolicyResultAsync(PolicyResult policyRetryResul internal void SetWrap(IPolicyBase policyToWrap) { - if (_policyWrapperFactory != null) + if (HasPolicyWrapperFactory) { throw new NotImplementedException("More than one wrapped policy is not supported."); } @@ -120,7 +120,7 @@ internal void SetWrap(IPolicyBase policyToWrap) internal void SetWrap(IEnumerable policies, ThrowOnWrappedCollectionFailed throwOnWrappedCollectionFailed) { - if (_policyWrapperFactory != null) + if (HasPolicyWrapperFactory) { throw new ArgumentException("More than one wrapped PolicyCollection is not supported."); } @@ -133,7 +133,7 @@ internal void SetWrap(IEnumerable policies, ThrowOnWrappedCollectio internal (Action Act, PolicyWrapper Wrapper) WrapDelegateIfNeed(Action action, CancellationToken token) { - if (_policyWrapperFactory == null) + if (!HasPolicyWrapperFactory) { return (action, null); } @@ -149,7 +149,7 @@ internal void SetWrap(IEnumerable policies, ThrowOnWrappedCollectio internal (Func Fn, PolicyWrapper Wrapper) WrapDelegateIfNeed(Func fn, CancellationToken token) { - if (_policyWrapperFactory == null) + if (!HasPolicyWrapperFactory) { return (fn, null); } @@ -165,7 +165,7 @@ internal void SetWrap(IEnumerable policies, ThrowOnWrappedCollectio internal (Func Fn, PolicyWrapper Wrapper) WrapDelegateIfNeed(Func fn, CancellationToken token, bool configureAwait) { - if (_policyWrapperFactory == null) + if (!HasPolicyWrapperFactory) { return (fn, null); } @@ -181,7 +181,7 @@ internal void SetWrap(IEnumerable policies, ThrowOnWrappedCollectio internal (Func> Fn, PolicyWrapper Wrapper) WrapDelegateIfNeed(Func> fn, CancellationToken token, bool configureAwait) { - if (_policyWrapperFactory == null) + if (!HasPolicyWrapperFactory) { return (fn, null); } @@ -195,6 +195,8 @@ internal void SetWrap(IEnumerable policies, ThrowOnWrappedCollectio } } + protected bool HasPolicyWrapperFactory => _policyWrapperFactory != null; + public void ResetWrap() { _policyWrapperFactory = null; From 0ac55d6460b0f724003187b588663e9bacad07de Mon Sep 17 00:00:00 2001 From: kolan72 Date: Wed, 18 Dec 2024 17:41:31 +0300 Subject: [PATCH 44/51] Introduce `SimplePolicy.Handle(Action, TParam, CancellationToken)` --- src/Simple/SimplePolicy.cs | 19 ++++++++++++ tests/SimplePolicyTests.cs | 59 ++++++++++++++++++++++++++++++++++---- 2 files changed, 72 insertions(+), 6 deletions(-) diff --git a/src/Simple/SimplePolicy.cs b/src/Simple/SimplePolicy.cs index 51bbddd..bde3dc4 100644 --- a/src/Simple/SimplePolicy.cs +++ b/src/Simple/SimplePolicy.cs @@ -67,6 +67,25 @@ public PolicyResult Handle(Action action, TErrorContext param, Ca return retryResult; } + public PolicyResult Handle(Action action, TParam param, CancellationToken token = default) + { + if (HasPolicyWrapperFactory) + { + return Handle(action.Apply(param), token); + } + else + { + if (!(_simpleProcessor is SimplePolicyProcessor processor)) + { + throw new NotImplementedException("This method is only supported for the SimplePolicyProcessor implementation of the ISimplePolicyProcessor interface."); + } + var result = processor.Execute(action, param, token) + .SetPolicyName(PolicyName); + HandlePolicyResult(result, token); + return result; + } + } + public PolicyResult Handle(Func func, CancellationToken token = default) { var (Fn, Wrapper) = WrapDelegateIfNeed(func, token); diff --git a/tests/SimplePolicyTests.cs b/tests/SimplePolicyTests.cs index d083dcd..396225b 100644 --- a/tests/SimplePolicyTests.cs +++ b/tests/SimplePolicyTests.cs @@ -738,9 +738,10 @@ public async Task Should_Rethrow_Or_Handle_If_PolicyCreated_With_ThrowIfErrorFil } [Test] - [TestCase(true)] - [TestCase(false)] - public void Should_WithErrorContextProcessor_Throws_Only_For_Not_SimplePolicyProcessor(bool throwEx) + [TestCase(true, null)] + [TestCase(false, true)] + [TestCase(false, false)] + public void Should_WithErrorContextProcessor_Throws_Only_For_Not_SimplePolicyProcessor(bool throwEx, bool? wrap) { SimplePolicy simplePolicy; if (throwEx) @@ -756,11 +757,17 @@ void action(Exception _, ProcessingErrorInfo pi) { m = pi.Param; } - simplePolicy = new SimplePolicy(); + + simplePolicy = new SimplePolicy() + .WithErrorContextProcessor(new DefaultErrorProcessor(action)); + + if (wrap == true) + { + simplePolicy = simplePolicy.WrapPolicy(new RetryPolicy(1)); + } var result = simplePolicy - .WithErrorContextProcessor(new DefaultErrorProcessor(action)) - .Handle(() => throw new InvalidOperationException(), 5); + .Handle(() => throw new InvalidOperationException(), 5); Assert.That(result.NoError, Is.False); Assert.That(result.IsSuccess, Is.True); @@ -922,6 +929,46 @@ async Task fn(Exception _, ProcessingErrorInfo pi, CancellationToken __) Assert.That(result.IsSuccess, Is.True); } + [Test] + [TestCase(true, true)] + [TestCase(true, false)] + [TestCase(false, null)] + public void Should_Handle_With_TParam_For_Action_With_TParam_WithErrorProcessorOf_Action_Process_Correctly(bool throwEx, bool? useWrap) + { + int m = 0; + int addable = 1; + + void action(Exception _, ProcessingErrorInfo pi) + { + m = pi.Param; + } + + var policyToTest = new SimplePolicy(true).WithErrorContextProcessorOf(action); + + if (useWrap == true) + { + policyToTest = policyToTest.WrapPolicy(new RetryPolicy(1)); + } + + PolicyResult result = null; + if (throwEx) + { + result = policyToTest.Handle((_) => throw new InvalidOperationException(), 5); + //With wrapping, we fallback to no-param handling + Assert.That(m, useWrap == true ? Is.EqualTo(0) : Is.EqualTo(5)); + Assert.That(result.NoError, Is.False); + } + else + { +#pragma warning disable RCS1021 // Convert lambda expression body to expression-body. + result = policyToTest.Handle((v) => { addable += v; }, 5); +#pragma warning restore RCS1021 // Convert lambda expression body to expression-body. + Assert.That(addable, Is.EqualTo(6)); + Assert.That(result.NoError, Is.True); + } + Assert.That(result.IsSuccess, Is.True); + } + public class TestSimplePolicyProcessor : ISimplePolicyProcessor { public PolicyProcessor.ExceptionFilter ErrorFilter => throw new NotImplementedException(); From 5de3fd9ae150bd012f349150b86b2a3735ddab47 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Dec 2024 09:38:37 +0000 Subject: [PATCH 45/51] Bump the nunit group with 7 updates Bumps the nunit group with 7 updates: | Package | From | To | | --- | --- | --- | | [NUnit](https://github.com/nunit/nunit) | `4.2.2` | `4.3.0` | | [System.Buffers](https://github.com/dotnet/maintenance-packages) | `4.6.0` | `4.6.0` | | [System.Memory](https://github.com/dotnet/maintenance-packages) | `4.6.0` | `4.6.0` | | [System.Numerics.Vectors](https://github.com/dotnet/maintenance-packages) | `4.6.0` | `4.6.0` | | [System.Runtime.CompilerServices.Unsafe](https://github.com/dotnet/maintenance-packages) | `6.1.0` | `6.1.0` | | System.Threading.Tasks.Extensions | `4.6.0` | `4.5.4` | | System.ValueTuple | `4.5.0` | `4.5.0` | Updates `NUnit` from 4.2.2 to 4.3.0 - [Release notes](https://github.com/nunit/nunit/releases) - [Changelog](https://github.com/nunit/nunit/blob/main/CHANGES.md) - [Commits](https://github.com/nunit/nunit/compare/4.2.2...4.3.0) Updates `System.Buffers` from 4.6.0 to 4.6.0 - [Release notes](https://github.com/dotnet/maintenance-packages/releases) - [Commits](https://github.com/dotnet/maintenance-packages/commits) Updates `System.Memory` from 4.6.0 to 4.6.0 - [Release notes](https://github.com/dotnet/maintenance-packages/releases) - [Commits](https://github.com/dotnet/maintenance-packages/commits) Updates `System.Numerics.Vectors` from 4.6.0 to 4.6.0 - [Release notes](https://github.com/dotnet/maintenance-packages/releases) - [Commits](https://github.com/dotnet/maintenance-packages/commits) Updates `System.Runtime.CompilerServices.Unsafe` from 6.1.0 to 6.1.0 - [Release notes](https://github.com/dotnet/maintenance-packages/releases) - [Commits](https://github.com/dotnet/maintenance-packages/commits) Updates `System.Threading.Tasks.Extensions` from 4.6.0 to 4.5.4 Updates `System.ValueTuple` from 4.5.0 to 4.5.0 --- updated-dependencies: - dependency-name: NUnit dependency-type: direct:production update-type: version-update:semver-minor dependency-group: nunit - dependency-name: System.Buffers dependency-type: direct:production update-type: version-update:semver-patch dependency-group: nunit - dependency-name: System.Memory dependency-type: direct:production update-type: version-update:semver-patch dependency-group: nunit - dependency-name: System.Numerics.Vectors dependency-type: direct:production update-type: version-update:semver-patch dependency-group: nunit - dependency-name: System.Runtime.CompilerServices.Unsafe dependency-type: direct:production update-type: version-update:semver-patch dependency-group: nunit - dependency-name: System.Threading.Tasks.Extensions dependency-type: direct:production update-type: version-update:semver-minor dependency-group: nunit - dependency-name: System.ValueTuple dependency-type: direct:production update-type: version-update:semver-patch dependency-group: nunit ... Signed-off-by: dependabot[bot] --- tests/App.config | 4 ++-- tests/PoliNorError.Tests.csproj | 10 ++++++---- tests/packages.config | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/tests/App.config b/tests/App.config index 1371cf3..93e58be 100644 --- a/tests/App.config +++ b/tests/App.config @@ -17,11 +17,11 @@ - + - + diff --git a/tests/PoliNorError.Tests.csproj b/tests/PoliNorError.Tests.csproj index da016c9..53599f5 100644 --- a/tests/PoliNorError.Tests.csproj +++ b/tests/PoliNorError.Tests.csproj @@ -1,5 +1,6 @@  + @@ -43,12 +44,12 @@ ..\packages\NSubstitute.5.3.0\lib\net462\NSubstitute.dll True - - ..\packages\NUnit.4.2.2\lib\net462\nunit.framework.dll + + ..\packages\NUnit.4.3.0\lib\net462\nunit.framework.dll True - - ..\packages\NUnit.4.2.2\lib\net462\nunit.framework.legacy.dll + + ..\packages\NUnit.4.3.0\lib\net462\nunit.framework.legacy.dll True @@ -166,5 +167,6 @@ + \ No newline at end of file diff --git a/tests/packages.config b/tests/packages.config index 5d43376..25000a9 100644 --- a/tests/packages.config +++ b/tests/packages.config @@ -2,7 +2,7 @@ - + From 6f54147b788802643c1690f4ded20894b15eb833 Mon Sep 17 00:00:00 2001 From: kolan72 Date: Thu, 19 Dec 2024 14:32:48 +0300 Subject: [PATCH 46/51] Introduce `SimplePolicy`'s `Handle(Func, TParam, CancellationToken)` and `Handle(Func func, TErrorContext param, CancellationToken)` --- src/Simple/SimplePolicy.cs | 40 ++++++++++++++++++++ tests/SimplePolicyTests.cs | 77 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+) diff --git a/src/Simple/SimplePolicy.cs b/src/Simple/SimplePolicy.cs index bde3dc4..0462d30 100644 --- a/src/Simple/SimplePolicy.cs +++ b/src/Simple/SimplePolicy.cs @@ -102,6 +102,46 @@ public PolicyResult Handle(Func func, CancellationToken token = default return retryResult; } + public PolicyResult Handle(Func func, TParam param, CancellationToken token = default) + { + if (HasPolicyWrapperFactory) + { + return Handle(func.Apply(param), token); + } + else + { + if (!(_simpleProcessor is SimplePolicyProcessor processor)) + { + throw new NotImplementedException("This method is only supported for the SimplePolicyProcessor implementation of the ISimplePolicyProcessor interface."); + } + var result = processor.Execute(func, param, token) + .SetPolicyName(PolicyName); + HandlePolicyResult(result, token); + return result; + } + } + + public PolicyResult Handle(Func func, TErrorContext param, CancellationToken token = default) + { + var (Fn, Wrapper) = WrapDelegateIfNeed(func, token); + if (Fn == null && Wrapper != null) + { + return new PolicyResult().WithNoDelegateExceptionAndPolicyNameFrom(this); + } + + if (!(_simpleProcessor is SimplePolicyProcessor processor)) + { + throw new NotImplementedException("This method is only supported for the SimplePolicyProcessor implementation of the ISimplePolicyProcessor interface."); + } + + var retryResult = processor.Execute(Fn, param, token) + .SetWrappedPolicyResults(Wrapper) + .SetPolicyName(PolicyName); + + HandlePolicyResult(retryResult, token); + return retryResult; + } + public async Task HandleAsync(Func func, bool configureAwait = false, CancellationToken token = default) { var (Fn, Wrapper) = WrapDelegateIfNeed(func, token, configureAwait); diff --git a/tests/SimplePolicyTests.cs b/tests/SimplePolicyTests.cs index 396225b..a8d6ac8 100644 --- a/tests/SimplePolicyTests.cs +++ b/tests/SimplePolicyTests.cs @@ -969,6 +969,83 @@ void action(Exception _, ProcessingErrorInfo pi) Assert.That(result.IsSuccess, Is.True); } + [Test] + [TestCase(true, true)] + [TestCase(false, true)] + [TestCase(true, false)] + [TestCase(false, false)] + public void Should_Handle_With_TParam_For_Func_WithErrorProcessorOf_Action_Process_Correctly(bool throwEx, bool wrap) + { + int m = 0; + + void action(Exception _, ProcessingErrorInfo pi) + { + m = pi.Param; + } + + SimplePolicy policy = new SimplePolicy(true) + .WithErrorContextProcessorOf(action); + if (wrap) + { + policy = policy.WrapPolicy(new RetryPolicy(1)); + } + + PolicyResult result = null; + if (throwEx) + { + result = policy.Handle(() => throw new InvalidOperationException(), 5); + Assert.That(m, Is.EqualTo(5)); + Assert.That(result.NoError, Is.False); + } + else + { + result = policy.Handle(() => 1, 5); + Assert.That(m, Is.EqualTo(0)); + Assert.That(result.NoError, Is.True); + } + Assert.That(result.IsSuccess, Is.True); + } + + [Test] + [TestCase(true, true)] + [TestCase(true, false)] + [TestCase(false, null)] + public void Should_Handle_With_TParam_For_Func_With_TParam_WithErrorProcessorOf_Action_Process_Correctly(bool throwEx, bool? useWrap) + { + int m = 0; + int addable = 1; + + void action(Exception _, ProcessingErrorInfo pi) + { + m = pi.Param; + } + + var policyToTest = new SimplePolicy(true).WithErrorContextProcessorOf(action); + + if (useWrap == true) + { + policyToTest = policyToTest.WrapPolicy(new RetryPolicy(1)); + } + + PolicyResult result = null; + if (throwEx) + { + result = policyToTest.Handle((_) => throw new InvalidOperationException(), 5); + //With wrapping, we fallback to no-param handling + Assert.That(m, useWrap == true ? Is.EqualTo(0) : Is.EqualTo(5)); + Assert.That(result.NoError, Is.False); + } + else + { +#pragma warning disable RCS1021 // Convert lambda expression body to expression-body. + result = policyToTest.Handle((v) => { addable += v; return addable; }, 5); +#pragma warning restore RCS1021 // Convert lambda expression body to expression-body. + Assert.That(addable, Is.EqualTo(6)); + Assert.That(result.NoError, Is.True); + } + Assert.That(result.IsSuccess, Is.True); + } + public class TestSimplePolicyProcessor : ISimplePolicyProcessor { public PolicyProcessor.ExceptionFilter ErrorFilter => throw new NotImplementedException(); From a1e2c4829344ec3bb1f551abaab2ecb5c128f6e7 Mon Sep 17 00:00:00 2001 From: kolan72 Date: Fri, 20 Dec 2024 16:08:36 +0300 Subject: [PATCH 47/51] Introduce the `SimplePolicy.HandleAsync` and `SimplePolicy.HandleAsync` methods overloads. --- src/Simple/SimplePolicy.cs | 50 +++++++++++++++++++++++ tests/SimplePolicyTests.cs | 82 +++++++++++++++++++++++++++++++++++++- 2 files changed, 130 insertions(+), 2 deletions(-) diff --git a/src/Simple/SimplePolicy.cs b/src/Simple/SimplePolicy.cs index 0462d30..149aab4 100644 --- a/src/Simple/SimplePolicy.cs +++ b/src/Simple/SimplePolicy.cs @@ -158,6 +158,56 @@ public async Task HandleAsync(Func func, return retryResult; } + public Task HandleAsync(Func func, TErrorContext param, CancellationToken token) + { + return HandleAsync(func, param, false, token); + } + + public async Task HandleAsync(Func func, TErrorContext param, bool configureAwait, CancellationToken token) + { + var (Fn, Wrapper) = WrapDelegateIfNeed(func, token, configureAwait); + if (Fn == null && Wrapper != null) + { + return new PolicyResult().WithNoDelegateExceptionAndPolicyNameFrom(this); + } + + if (!(_simpleProcessor is SimplePolicyProcessor processor)) + { + throw new NotImplementedException("This method is only supported for the SimplePolicyProcessor implementation of the ISimplePolicyProcessor interface."); + } + + var retryResult = (await processor.ExecuteAsync(Fn, param, configureAwait, token).ConfigureAwait(configureAwait)) + .SetWrappedPolicyResults(Wrapper) + .SetPolicyName(PolicyName); + + await HandlePolicyResultAsync(retryResult, configureAwait, token).ConfigureAwait(configureAwait); + return retryResult; + } + + public Task HandleAsync(Func func, TParam param, CancellationToken token) + { + return HandleAsync(func, param, false, token); + } + + public async Task HandleAsync(Func func, TParam param, bool configureAwait, CancellationToken token) + { + if (HasPolicyWrapperFactory) + { + return await HandleAsync(func.Apply(param), configureAwait, token).ConfigureAwait(configureAwait); + } + else + { + if (!(_simpleProcessor is SimplePolicyProcessor processor)) + { + throw new NotImplementedException("This method is only supported for the SimplePolicyProcessor implementation of the ISimplePolicyProcessor interface."); + } + var result = (await processor.ExecuteAsync(func, param, configureAwait, token).ConfigureAwait(configureAwait)) + .SetPolicyName(PolicyName); + HandlePolicyResult(result, token); + return result; + } + } + public async Task> HandleAsync(Func> func, bool configureAwait = false, CancellationToken token = default) { var (Fn, Wrapper) = WrapDelegateIfNeed(func, token, configureAwait); diff --git a/tests/SimplePolicyTests.cs b/tests/SimplePolicyTests.cs index a8d6ac8..0aec7d3 100644 --- a/tests/SimplePolicyTests.cs +++ b/tests/SimplePolicyTests.cs @@ -1037,9 +1037,87 @@ void action(Exception _, ProcessingErrorInfo pi) } else { -#pragma warning disable RCS1021 // Convert lambda expression body to expression-body. result = policyToTest.Handle((v) => { addable += v; return addable; }, 5); -#pragma warning restore RCS1021 // Convert lambda expression body to expression-body. + Assert.That(addable, Is.EqualTo(6)); + Assert.That(result.NoError, Is.True); + } + Assert.That(result.IsSuccess, Is.True); + } + + [Test] + [TestCase(true, false)] + [TestCase(false, false)] + [TestCase(null, true)] + public async Task Should_HandleAsync_With_TParam_For_NonGeneric_AsyncFunc_WithErrorProcessorOf_Action_Process_Correctly(bool? throwEx, bool notDefaultImpl) + { + int m = 0; + + void action(Exception _, ProcessingErrorInfo pi) + { + m = pi.Param; + } + + SimplePolicy policy; + + if (notDefaultImpl) + { + policy = new SimplePolicy(new TestSimplePolicyProcessor()); + Assert.ThrowsAsync(async() => await policy.HandleAsync(async (_) => await Task.Delay(1), 5, false, default)); + } + else + { + policy = new SimplePolicy(true) + .WithErrorContextProcessorOf(action); + + PolicyResult result = null; + if (throwEx == true) + { + result = await policy.HandleAsync(async (_) => { await Task.Delay(1); throw new InvalidOperationException(); }, 5, false, default); + Assert.That(m, Is.EqualTo(5)); + Assert.That(result.NoError, Is.False); + } + else + { + result = await policy.HandleAsync(async (_) => await Task.Delay(1), 5, false, default); + Assert.That(m, Is.EqualTo(0)); + Assert.That(result.NoError, Is.True); + } + Assert.That(result.IsSuccess, Is.True); + } + } + + [Test] + [TestCase(true, true)] + [TestCase(true, false)] + [TestCase(false, null)] + public async Task Should_HandleAsync_With_TParam_For_AsyncFunc_With_TParam_WithErrorProcessorOf_Action_Process_Correctly(bool throwEx, bool? useWrap) + { + int m = 0; + int addable = 1; + + void action(Exception _, ProcessingErrorInfo pi) + { + m = pi.Param; + } + + var policyToTest = new SimplePolicy(true).WithErrorContextProcessorOf(action); + + if (useWrap == true) + { + policyToTest = policyToTest.WrapPolicy(new RetryPolicy(1)); + } + + PolicyResult result = null; + if (throwEx) + { + result = await policyToTest.HandleAsync(async (_, __) => { await Task.Delay(1); throw new InvalidOperationException(); }, 5, false, default); + //With wrapping, we fallback to no-param handling + Assert.That(m, useWrap == true ? Is.EqualTo(0) : Is.EqualTo(5)); + Assert.That(result.NoError, Is.False); + } + else + { + result = await policyToTest.HandleAsync(async (v, _) => { await Task.Delay(1); addable += v; }, 5, false, default); Assert.That(addable, Is.EqualTo(6)); Assert.That(result.NoError, Is.True); } From 62d9771393bd1fb359634c80621f7a2f8711901b Mon Sep 17 00:00:00 2001 From: kolan72 Date: Sat, 21 Dec 2024 20:18:59 +0300 Subject: [PATCH 48/51] Introduce the `SimplePolicy.HandleAsync` and `SimplePolicy.HandleAsync` methods overloads. --- src/Simple/SimplePolicy.cs | 52 ++++++++++++++++++++++++- tests/SimplePolicyTests.cs | 80 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+), 1 deletion(-) diff --git a/src/Simple/SimplePolicy.cs b/src/Simple/SimplePolicy.cs index 149aab4..13cb33c 100644 --- a/src/Simple/SimplePolicy.cs +++ b/src/Simple/SimplePolicy.cs @@ -121,7 +121,7 @@ public PolicyResult Handle(Func func, TParam param, Can } } - public PolicyResult Handle(Func func, TErrorContext param, CancellationToken token = default) + public PolicyResult Handle(Func func, TErrorContext param, CancellationToken token = default) { var (Fn, Wrapper) = WrapDelegateIfNeed(func, token); if (Fn == null && Wrapper != null) @@ -224,6 +224,56 @@ public async Task> HandleAsync(Func> HandleAsync(Func> func, TParam param, CancellationToken token) + { + return HandleAsync(func, param, false, token); + } + + public async Task> HandleAsync(Func> func, TParam param, bool configureAwait, CancellationToken token) + { + if (HasPolicyWrapperFactory) + { + return await HandleAsync(func.Apply(param), configureAwait, token).ConfigureAwait(configureAwait); + } + else + { + if (!(_simpleProcessor is SimplePolicyProcessor processor)) + { + throw new NotImplementedException("This method is only supported for the SimplePolicyProcessor implementation of the ISimplePolicyProcessor interface."); + } + var result = (await processor.ExecuteAsync(func, param, configureAwait, token).ConfigureAwait(configureAwait)) + .SetPolicyName(PolicyName); + HandlePolicyResult(result, token); + return result; + } + } + + public Task> HandleAsync(Func> func, TErrorContext param, CancellationToken token) + { + return HandleAsync(func, param, false, token); + } + + public async Task> HandleAsync(Func> func, TErrorContext param, bool configureAwait, CancellationToken token) + { + var (Fn, Wrapper) = WrapDelegateIfNeed(func, token, configureAwait); + if (Fn == null && Wrapper != null) + { + return new PolicyResult().WithNoDelegateExceptionAndPolicyNameFrom(this); + } + + if (!(_simpleProcessor is SimplePolicyProcessor processor)) + { + throw new NotImplementedException("This method is only supported for the SimplePolicyProcessor implementation of the ISimplePolicyProcessor interface."); + } + + var retryResult = (await processor.ExecuteAsync(Fn, param, configureAwait, token).ConfigureAwait(configureAwait)) + .SetWrappedPolicyResults(Wrapper) + .SetPolicyName(PolicyName); + + await HandlePolicyResultAsync(retryResult, configureAwait, token).ConfigureAwait(configureAwait); + return retryResult; + } + public SimplePolicy IncludeError(Func func = null) where TException : Exception => this.IncludeError(func); public SimplePolicy IncludeError(Expression> expression) => this.IncludeError(expression); diff --git a/tests/SimplePolicyTests.cs b/tests/SimplePolicyTests.cs index 0aec7d3..b408986 100644 --- a/tests/SimplePolicyTests.cs +++ b/tests/SimplePolicyTests.cs @@ -1124,6 +1124,86 @@ void action(Exception _, ProcessingErrorInfo pi) Assert.That(result.IsSuccess, Is.True); } + [Test] + [TestCase(true, false)] + [TestCase(false, false)] + [TestCase(null, true)] + public async Task Should_HandleAsync_With_TParam_For_Generic_AsyncFunc_WithErrorProcessorOf_Action_Process_Correctly(bool? throwEx, bool notDefaultImpl) + { + int m = 0; + + void action(Exception _, ProcessingErrorInfo pi) + { + m = pi.Param; + } + + SimplePolicy policy; + + if (notDefaultImpl) + { + policy = new SimplePolicy(new TestSimplePolicyProcessor()); + Assert.ThrowsAsync(async () => await policy.HandleAsync(async (_) => { await Task.Delay(1); return 1; }, 5, false, default)); + } + else + { + policy = new SimplePolicy(true) + .WithErrorContextProcessorOf(action); + + PolicyResult result = null; + if (throwEx == true) + { + result = await policy.HandleAsync(async (_) => { await Task.Delay(1); throw new InvalidOperationException(); }, 5, false, default); + Assert.That(m, Is.EqualTo(5)); + Assert.That(result.NoError, Is.False); + } + else + { + result = await policy.HandleAsync(async (_) => { await Task.Delay(1); return 1; }, 5, false, default); + Assert.That(m, Is.EqualTo(0)); + Assert.That(result.NoError, Is.True); + } + Assert.That(result.IsSuccess, Is.True); + } + } + + [Test] + [TestCase(true, true)] + [TestCase(true, false)] + [TestCase(false, null)] + public async Task Should_HandleAsync_With_TParam_For_GenericAsyncFunc_With_TParam_WithErrorProcessorOf_Action_Process_Correctly(bool throwEx, bool? useWrap) + { + int m = 0; + int addable = 1; + + void action(Exception _, ProcessingErrorInfo pi) + { + m = pi.Param; + } + + var policyToTest = new SimplePolicy(true).WithErrorContextProcessorOf(action); + + if (useWrap == true) + { + policyToTest = policyToTest.WrapPolicy(new RetryPolicy(1)); + } + + PolicyResult result = null; + if (throwEx) + { + result = await policyToTest.HandleAsync(async (_, __) => { await Task.Delay(1); throw new InvalidOperationException(); }, 5, false, default); + //With wrapping, we fallback to no-param handling + Assert.That(m, useWrap == true ? Is.EqualTo(0) : Is.EqualTo(5)); + Assert.That(result.NoError, Is.False); + } + else + { + result = await policyToTest.HandleAsync(async (v, _) => { await Task.Delay(1); addable += v; return addable; }, 5, false, default); + Assert.That(addable, Is.EqualTo(6)); + Assert.That(result.NoError, Is.True); + } + Assert.That(result.IsSuccess, Is.True); + } + public class TestSimplePolicyProcessor : ISimplePolicyProcessor { public PolicyProcessor.ExceptionFilter ErrorFilter => throw new NotImplementedException(); From 389da572285fbee89031b89fe9dd5a791ef1452b Mon Sep 17 00:00:00 2001 From: kolan72 Date: Sun, 22 Dec 2024 21:02:36 +0300 Subject: [PATCH 49/51] Simplify `ProcessingErrorContext.ToProcessingErrorInfo` method. --- src/ErrorProcessors/ProcessingErrorContext.T.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/ErrorProcessors/ProcessingErrorContext.T.cs b/src/ErrorProcessors/ProcessingErrorContext.T.cs index 58328e5..d751a3c 100644 --- a/src/ErrorProcessors/ProcessingErrorContext.T.cs +++ b/src/ErrorProcessors/ProcessingErrorContext.T.cs @@ -8,12 +8,9 @@ public ProcessingErrorContext(PolicyAlias policyKind, TParam param) : base(polic } public TParam Param { get; set; } - internal override ProcessingErrorInfo ToProcessingErrorInfo(PolicyAlias policyAlias) + internal override ProcessingErrorInfo ToProcessingErrorInfo() { - if (policyAlias != PolicyAlias.NotSet) - return new ProcessingErrorInfo(policyAlias, this); - else - return new ProcessingErrorInfo(this); + return new ProcessingErrorInfo(this); } } } From 348d5bc78a77e8d1974cfec6da475266db3b5103 Mon Sep 17 00:00:00 2001 From: kolan72 Date: Mon, 23 Dec 2024 12:59:37 +0300 Subject: [PATCH 50/51] Made `PolicyProcessor` constructors that use the `PolicyAlias` parameter and the `_isPolicyAliasSet` field obsolete. --- src/PolicyProcessor.cs | 23 +++++++++++++++++++++-- src/Simple/SimplePolicyProcessor.cs | 4 ++-- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/PolicyProcessor.cs b/src/PolicyProcessor.cs index 2506de2..16437d1 100644 --- a/src/PolicyProcessor.cs +++ b/src/PolicyProcessor.cs @@ -1,7 +1,6 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Linq; using System.Linq.Expressions; using System.Threading; @@ -11,15 +10,35 @@ public abstract class PolicyProcessor : IPolicyProcessor { protected IBulkErrorProcessor _bulkErrorProcessor; +#pragma warning disable S1133 // Deprecated code should be removed + [Obsolete("This field is obsolete.")] +#pragma warning restore S1133 // Deprecated code should be removed protected bool _isPolicyAliasSet; - protected PolicyProcessor(IBulkErrorProcessor bulkErrorProcessor = null) : this(PolicyAlias.NotSet, new ExceptionFilter(), bulkErrorProcessor) + protected PolicyProcessor(IBulkErrorProcessor bulkErrorProcessor = null): this(new ExceptionFilter(), bulkErrorProcessor) + {} + +#pragma warning disable S1133 // Deprecated code should be removed + [Obsolete("This constructor is obsolete. Use constructors without the PolicyAlias parameter instead.")] +#pragma warning restore S1133 // Deprecated code should be removed + protected PolicyProcessor(PolicyAlias policyAlias, IBulkErrorProcessor bulkErrorProcessor = null) : this(policyAlias, new ExceptionFilter(), bulkErrorProcessor) + {} + + protected PolicyProcessor(ExceptionFilter exceptionFilter, IBulkErrorProcessor bulkErrorProcessor = null) { + _bulkErrorProcessor = bulkErrorProcessor ?? new BulkErrorProcessor(); + ErrorFilter = exceptionFilter; } +#pragma warning disable S1133 // Deprecated code should be removed + [Obsolete("This constructor is obsolete. Use constructors without the PolicyAlias parameter instead.")] +#pragma warning restore S1133 // Deprecated code should be removed +#pragma warning disable RCS1163 // Unused parameter. protected PolicyProcessor(PolicyAlias policyAlias, ExceptionFilter exceptionFilter, IBulkErrorProcessor bulkErrorProcessor = null) +#pragma warning restore RCS1163 // Unused parameter. { _bulkErrorProcessor = bulkErrorProcessor ?? new BulkErrorProcessor(); + _isPolicyAliasSet = bulkErrorProcessor == null; ErrorFilter = exceptionFilter; } diff --git a/src/Simple/SimplePolicyProcessor.cs b/src/Simple/SimplePolicyProcessor.cs index c6180a7..384158e 100644 --- a/src/Simple/SimplePolicyProcessor.cs +++ b/src/Simple/SimplePolicyProcessor.cs @@ -27,9 +27,9 @@ public SimplePolicyProcessor(IBulkErrorProcessor bulkErrorProcessor, bool rethro internal SimplePolicyProcessor(CatchBlockFilter catchBlockFilter, IBulkErrorProcessor bulkErrorProcessor = null, bool rethrowIfErrorFilterUnsatisfied = false) : this(bulkErrorProcessor, (catchBlockFilter ?? new CatchBlockFilter()).ErrorFilter, rethrowIfErrorFilterUnsatisfied) {} - private SimplePolicyProcessor(IBulkErrorProcessor bulkErrorProcessor, ExceptionFilter exceptionFilter, bool rethrowIfErrorFilterUnsatisfied) : base(PolicyAlias.Simple, exceptionFilter ?? new ExceptionFilter(), bulkErrorProcessor) + private SimplePolicyProcessor(IBulkErrorProcessor bulkErrorProcessor, ExceptionFilter exceptionFilter, bool rethrowIfErrorFilterUnsatisfied) : base(exceptionFilter ?? new ExceptionFilter(), bulkErrorProcessor) { - _emptyErrorContext = _isPolicyAliasSet ? EmptyErrorContext.Default : EmptyErrorContext.DefaultSimple; + _emptyErrorContext = EmptyErrorContext.DefaultSimple; _rethrowIfErrorFilterUnsatisfied = rethrowIfErrorFilterUnsatisfied; } From 0d1f66c82eee43fbf33bcac32c4c517096112b6f Mon Sep 17 00:00:00 2001 From: kolan72 Date: Wed, 25 Dec 2024 15:24:34 +0300 Subject: [PATCH 51/51] Apply Rider recommendations to the `SimplePolicyProcessor` class. --- src/Simple/SimplePolicyProcessor.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Simple/SimplePolicyProcessor.cs b/src/Simple/SimplePolicyProcessor.cs index 384158e..65da780 100644 --- a/src/Simple/SimplePolicyProcessor.cs +++ b/src/Simple/SimplePolicyProcessor.cs @@ -66,7 +66,7 @@ private PolicyResult Execute(Action action, EmptyErrorContext emptyErrorContext, if (action == null) return new PolicyResult().WithNoDelegateException(); - PolicyResult result = PolicyResult.ForSync(); + var result = PolicyResult.ForSync(); if (token.IsCancellationRequested) { @@ -91,7 +91,7 @@ private PolicyResult Execute(Action action, EmptyErrorContext emptyErrorContext, { if (_rethrowIfErrorFilterUnsatisfied) { - (bool? filterUnsatisfied, Exception filterException) = GetFilterUnsatisfiedOrFilterException(ex); + var (filterUnsatisfied, filterException) = GetFilterUnsatisfiedOrFilterException(ex); if (filterUnsatisfied == true) { ex.Data[PolinorErrorConsts.EXCEPTION_DATA_ERRORFILTERUNSATISFIED_KEY] = true; @@ -159,7 +159,7 @@ private PolicyResult Execute(Func func, EmptyErrorContext emptyErrorCon { if (_rethrowIfErrorFilterUnsatisfied) { - (bool? filterUnsatisfied, Exception filterException) = GetFilterUnsatisfiedOrFilterException(ex); + var (filterUnsatisfied, filterException) = GetFilterUnsatisfiedOrFilterException(ex); if (filterUnsatisfied == true) { ex.Data[PolinorErrorConsts.EXCEPTION_DATA_ERRORFILTERUNSATISFIED_KEY] = true; @@ -201,7 +201,7 @@ private async Task ExecuteAsync(Func func if (func == null) return new PolicyResult().WithNoDelegateException(); - PolicyResult result = PolicyResult.InitByConfigureAwait(configureAwait); + var result = PolicyResult.InitByConfigureAwait(configureAwait); if (token.IsCancellationRequested) { @@ -222,7 +222,7 @@ private async Task ExecuteAsync(Func func { if (_rethrowIfErrorFilterUnsatisfied) { - (bool? filterUnsatisfied, Exception filterException) = GetFilterUnsatisfiedOrFilterException(ex); + var (filterUnsatisfied, filterException) = GetFilterUnsatisfiedOrFilterException(ex); if (filterUnsatisfied == true) { ex.Data[PolinorErrorConsts.EXCEPTION_DATA_ERRORFILTERUNSATISFIED_KEY] = true; @@ -286,7 +286,7 @@ private async Task> ExecuteAsync(Func