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 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/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); + } } 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/DefaultErrorProcessor.T.cs b/src/ErrorProcessors/DefaultErrorProcessor.T.cs new file mode 100644 index 0000000..4c5c6d9 --- /dev/null +++ b/src/ErrorProcessors/DefaultErrorProcessor.T.cs @@ -0,0 +1,133 @@ +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(Action, CancellationToken> actionProcessor) + { + _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, CancellationToken, 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); + } + + 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) + { + var action = ConvertToNonGenericAction(actionProcessor); + var res = new DefaultErrorProcessorT(); + res.SetSyncRunner(action); + return res; + } + + public static DefaultErrorProcessorT Create(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); + var res = new DefaultErrorProcessorT(); + res.SetSyncRunner(action, cancellationType); + return res; + } + + public static DefaultErrorProcessorT Create(Func, Task> funcProcessor) + { + var func = ConvertToNonGenericFunc(funcProcessor); + var res = new DefaultErrorProcessorT(); + res.SetAsyncRunner(func); + return res; + } + + public static DefaultErrorProcessorT Create(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); + 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; + }; + } + + protected override Func ParameterConverter => (_) => _; + } +} 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; } diff --git a/src/ErrorProcessors/ProcessingErrorContext.T.cs b/src/ErrorProcessors/ProcessingErrorContext.T.cs new file mode 100644 index 0000000..d751a3c --- /dev/null +++ b/src/ErrorProcessors/ProcessingErrorContext.T.cs @@ -0,0 +1,16 @@ +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() + { + return new ProcessingErrorInfo(this); + } + } +} 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/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/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/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; diff --git a/src/PolicyProcessor.cs b/src/PolicyProcessor.cs index 9f8dd69..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,34 @@ 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(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(policyAlias); + _bulkErrorProcessor = bulkErrorProcessor ?? new BulkErrorProcessor(); _isPolicyAliasSet = bulkErrorProcessor == null; 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/src/Simple/SimplePolicy.cs b/src/Simple/SimplePolicy.cs index 3fbe7ef..13cb33c 100644 --- a/src/Simple/SimplePolicy.cs +++ b/src/Simple/SimplePolicy.cs @@ -47,6 +47,45 @@ 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(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); @@ -63,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); @@ -79,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); @@ -95,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); @@ -204,5 +383,46 @@ 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 WithErrorContextProcessorOf(Action, CancellationToken> actionProcessor) + { + return WithErrorContextProcessor(new DefaultErrorProcessor(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 WithErrorContextProcessorOf(Func, CancellationToken, Task> funcProcessor) + { + return WithErrorContextProcessor(new DefaultErrorProcessor(funcProcessor)); + } + + 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/src/Simple/SimplePolicyProcessor.cs b/src/Simple/SimplePolicyProcessor.cs index ae5711a..65da780 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; } @@ -46,11 +46,27 @@ 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) + { + return Execute(action.Apply(param), param, token); + } + + public PolicyResult Execute(Action action, TErrorContext 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(); - PolicyResult result = PolicyResult.ForSync(); + var result = PolicyResult.ForSync(); if (token.IsCancellationRequested) { @@ -75,7 +91,7 @@ public PolicyResult Execute(Action action, CancellationToken token = default) { if (_rethrowIfErrorFilterUnsatisfied) { - (bool? filterUnsatisfied, Exception filterException) = GetFilterUnsatisfiedOrFilterException(ex); + var (filterUnsatisfied, filterException) = GetFilterUnsatisfiedOrFilterException(ex); if (filterUnsatisfied == true) { ex.Data[PolinorErrorConsts.EXCEPTION_DATA_ERRORFILTERUNSATISFIED_KEY] = true; @@ -90,13 +106,29 @@ public PolicyResult Execute(Action action, CancellationToken token = default) result.AddError(ex); result.ChangeByHandleCatchBlockResult(GetCatchBlockSyncHandler(result, token) - .Handle(ex, _emptyErrorContext)); + .Handle(ex, 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); + } + + public PolicyResult Execute(Func func, TErrorContext param, CancellationToken token = default) + { + var emptyContext = new EmptyErrorContext(param); + return Execute(func, (EmptyErrorContext)emptyContext, token); + } + + private PolicyResult Execute(Func func, EmptyErrorContext emptyErrorContext, CancellationToken token = default) { if (func == null) return new PolicyResult().WithNoDelegateException(); @@ -127,7 +159,7 @@ public PolicyResult Execute(Func func, CancellationToken token = defaul { if (_rethrowIfErrorFilterUnsatisfied) { - (bool? filterUnsatisfied, Exception filterException) = GetFilterUnsatisfiedOrFilterException(ex); + var (filterUnsatisfied, filterException) = GetFilterUnsatisfiedOrFilterException(ex); if (filterUnsatisfied == true) { ex.Data[PolinorErrorConsts.EXCEPTION_DATA_ERRORFILTERUNSATISFIED_KEY] = true; @@ -142,18 +174,34 @@ public PolicyResult Execute(Func func, CancellationToken token = defaul result.AddError(ex); result.ChangeByHandleCatchBlockResult(GetCatchBlockSyncHandler(result, token) - .Handle(ex, _emptyErrorContext)); + .Handle(ex, emptyErrorContext)); } return result; } /// 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, 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); + 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(); - PolicyResult result = PolicyResult.InitByConfigureAwait(configureAwait); + var result = PolicyResult.InitByConfigureAwait(configureAwait); if (token.IsCancellationRequested) { @@ -174,7 +222,7 @@ public 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; @@ -189,13 +237,29 @@ 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; } /// 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, 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); + 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(); @@ -222,7 +286,7 @@ public async Task> ExecuteAsync(Func> ExecuteAsync(Func(result, configureAwait, token) - .HandleAsync(ex, _emptyErrorContext).ConfigureAwait(configureAwait)); + .HandleAsync(ex, emptyErrorContext).ConfigureAwait(configureAwait)); } return result; } + public SimplePolicyProcessor WithErrorContextProcessorOf(Action> actionProcessor) + { + return WithErrorContextProcessor(new DefaultErrorProcessor(actionProcessor)); + } + + public SimplePolicyProcessor WithErrorContextProcessorOf(Action, CancellationToken> actionProcessor) + { + return WithErrorContextProcessor(new DefaultErrorProcessor(actionProcessor)); + } + + public SimplePolicyProcessor WithErrorContextProcessorOf(Action> actionProcessor, CancellationType cancellationType) + { + return WithErrorContextProcessor(new DefaultErrorProcessor(actionProcessor, cancellationType)); + } + + public SimplePolicyProcessor WithErrorContextProcessorOf(Func, Task> funcProcessor) + { + return WithErrorContextProcessor(new DefaultErrorProcessor(funcProcessor)); + } + + public SimplePolicyProcessor WithErrorContextProcessorOf(Func, Task> funcProcessor, CancellationType cancellationType) + { + return WithErrorContextProcessor(new DefaultErrorProcessor(funcProcessor, cancellationType)); + } + + public SimplePolicyProcessor WithErrorContextProcessorOf(Func, CancellationToken, Task> funcProcessor) + { + return WithErrorContextProcessor(new DefaultErrorProcessor(funcProcessor)); + } + + public SimplePolicyProcessor WithErrorContextProcessor(DefaultErrorProcessor errorProcessor) + { + AddErrorProcessor(errorProcessor); + return this; + } + private (bool? FilterUnsatisfied, Exception exception) GetFilterUnsatisfiedOrFilterException(Exception ex) { try diff --git a/src/TryCatch/TryCatchBuilder.cs b/src/TryCatch/TryCatchBuilder.cs index 7d3642b..146717a 100644 --- a/src/TryCatch/TryCatchBuilder.cs +++ b/src/TryCatch/TryCatchBuilder.cs @@ -1,5 +1,7 @@ -using System.Collections.Generic; - +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + namespace PoliNorError.TryCatch { /// @@ -13,6 +15,48 @@ 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(); + } + + /// + /// 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(); + } + + /// + /// 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); + } + + /// + /// 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); } /// @@ -26,6 +70,30 @@ 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 . + /// 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. /// @@ -60,6 +128,42 @@ public ITryCatchBuilder AddCatchBlock(CatchBlockForAllHandler filteredHandler) return this; } + /// + /// Adds handler with the . No other handlers may be added to the created object. + /// + /// + /// + public ITryCatchBuilder AddCatchBlock(IBulkErrorProcessor bulkErrorProcessor) + { + var handlerToAdd = CatchBlockHandlerFactory.ForAllExceptions(); + handlerToAdd.SetBulkErrorProcessor(bulkErrorProcessor); + return AddCatchBlock(handlerToAdd); + } + + /// + /// 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); + } + + /// + /// Adds a handler consisting of . Mimics a try/catch block that swallows an exception. + /// + /// + /// + public TryCatchBuilder AddCatchBlock(NonEmptyCatchBlockFilter nonEmptyCatchBlockFilter) + { + var handler = new CatchBlockFilteredHandler(nonEmptyCatchBlockFilter); + return AddCatchBlock(handler); + } + public ITryCatch Build() => new TryCatch(_catchBlockHandlers, _hasCatchBlockForAll); } } diff --git a/src/TryCatch/TryCatchDelegateInvoking.cs b/src/TryCatch/TryCatchDelegateInvoking.cs new file mode 100644 index 0000000..ba3f0bc --- /dev/null +++ b/src/TryCatch/TryCatchDelegateInvoking.cs @@ -0,0 +1,90 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +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); + } + + /// + /// 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); + } + + /// + /// 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 . + /// + /// 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); + } + + /// + /// 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. + /// + /// 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/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/BulkErrorProcessorTests.cs b/tests/BulkErrorProcessorTests.cs index 3d60821..c5e9905 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); @@ -292,5 +292,84 @@ 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 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/ErrorProcessorTests.cs b/tests/ErrorProcessorTests.cs index b338606..21abf86 100644 --- a/tests/ErrorProcessorTests.cs +++ b/tests/ErrorProcessorTests.cs @@ -105,5 +105,111 @@ 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)] + [TestCase(false)] + public void Should_DefaultErrorProcessor_TParam_Of_Action_With_TokenParam_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)] + [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)); + } + + [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)); + } } } 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()); diff --git a/tests/PoliNorError.Tests.csproj b/tests/PoliNorError.Tests.csproj index 142d336..53599f5 100644 --- a/tests/PoliNorError.Tests.csproj +++ b/tests/PoliNorError.Tests.csproj @@ -1,11 +1,8 @@  + - - - - Debug @@ -47,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 @@ -168,11 +165,8 @@ 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 diff --git a/tests/SimplePolicyProcessorTests.cs b/tests/SimplePolicyProcessorTests.cs index f7f5b86..f7def7e 100644 --- a/tests/SimplePolicyProcessorTests.cs +++ b/tests/SimplePolicyProcessorTests.cs @@ -622,6 +622,386 @@ 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) + .WithErrorContextProcessorOf(action); + } + else + { + processor = new SimplePolicyProcessor(true) + .WithErrorContextProcessorOf(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); + } + + [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) + .WithErrorContextProcessorOf(fn); + } + else + { + processor = new SimplePolicyProcessor(true) + .WithErrorContextProcessorOf(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)] + 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) + .WithErrorContextProcessorOf(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)] + 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) + .WithErrorContextProcessorOf(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)] + public void Should_Execute_With_TParam_For_Action_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 = processor.Execute((_) => throw new InvalidOperationException(), 5); + Assert.That(m, Is.EqualTo(5)); + Assert.That(result.NoError, Is.False); + } + else + { +#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); + } + Assert.That(result.IsSuccess, Is.True); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Should_Execute_With_TParam_For_Func_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 = 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); + } + + [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) + .WithErrorContextProcessorOf(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); + } + + [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) + .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), 5); + Assert.That(m, Is.EqualTo(0)); + Assert.That(result.NoError, Is.True); + } + 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); + } + + [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); + } + + [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) diff --git a/tests/SimplePolicyTests.cs b/tests/SimplePolicyTests.cs index 3a65d5a..b408986 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,485 @@ 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, null)] + [TestCase(false, true)] + [TestCase(false, false)] + public void Should_WithErrorContextProcessor_Throws_Only_For_Not_SimplePolicyProcessor(bool throwEx, bool? wrap) + { + SimplePolicy simplePolicy; + if (throwEx) + { + simplePolicy = new SimplePolicy(new TestSimplePolicyProcessor()); + Assert.Throws(() => simplePolicy.WithErrorContextProcessor(new DefaultErrorProcessor((_, __) => { }))); + } + else + { + int m = 0; + + void action(Exception _, ProcessingErrorInfo pi) + { + m = pi.Param; + } + + simplePolicy = new SimplePolicy() + .WithErrorContextProcessor(new DefaultErrorProcessor(action)); + + if (wrap == true) + { + simplePolicy = simplePolicy.WrapPolicy(new RetryPolicy(1)); + } + + var result = simplePolicy + .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_Handle_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); + } + + [Test] + [TestCase(true)] + [TestCase(false)] + public void Should_Handle_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); + } + + [Test] + [TestCase(true, true)] + [TestCase(false, true)] + [TestCase(true, false)] + [TestCase(false, false)] + public void Should_Handle_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); + } + + [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); + } + + [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); + } + + [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 + { + result = policyToTest.Handle((v) => { addable += v; return addable; }, 5); + 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); + } + 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(); + + 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(); + } } } diff --git a/tests/TryCatchBuilderTests.cs b/tests/TryCatchBuilderTests.cs index 15e5936..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 @@ -48,5 +49,182 @@ 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_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(); + 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_AddCatchBlock_With_NonEmptyCatchBlockFilter_And_IBulkErrorProcessor_Params_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)); + } + + [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_CreateFrom_With_NonEmptyCatchBlockFilter_And_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)); + } + + [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); + } + + [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); + } + + [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)); + } + + [Test] + public void Should_CreateAndBuild_With_Action_With_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)); + } + + [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)); + } } } diff --git a/tests/TryCatchTests.cs b/tests/TryCatchTests.cs index 97d89e8..fafba28 100644 --- a/tests/TryCatchTests.cs +++ b/tests/TryCatchTests.cs @@ -627,6 +627,218 @@ 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_For_Action_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); + } + + [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)); + } + } + + [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); + Assert.That(result.Result, Is.EqualTo(1)); + } + + [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); + } + + [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; + + 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; 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 @@ - +