From e8a14d1d1be4d760328be62f91179afb786fabfb Mon Sep 17 00:00:00 2001 From: doomchild Date: Tue, 4 Mar 2025 18:45:02 -0600 Subject: [PATCH] Issue-111: Fix tapping functions swallowing exceptions closes #111 bug SemVer:Patch --- README.md | 2 + src/TaskExtensionsIfFaulted.cs | 15 +- src/TaskExtensionsIfFulfilled.cs | 27 +- src/TaskExtensionsTap.cs | 109 ++++++-- tests/unit/IfFaulted/WithAction.cs | 114 +++++++++ tests/unit/IfFaulted/WithFullTaskFunc.cs | 157 ++++++++++++ tests/unit/IfFaulted/WithRawTaskFunc.cs | 158 ++++++++++++ tests/unit/IfFulfilled/WithAction.cs | 114 +++++++++ tests/unit/IfFulfilled/WithFullTaskFunc.cs | 144 +++++++++++ tests/unit/IfFulfilled/WithRawTaskFunc.cs | 144 +++++++++++ ...thActionOnFulfilledAndFullTaskOnFaulted.cs | 162 ++++++++++++ ...ithActionOnFulfilledAndRawTaskOnFaulted.cs | 159 ++++++++++++ tests/unit/Tap/WithBothActions.cs | 147 +++++++++++ tests/unit/Tap/WithBothFullTasks.cs | 179 +++++++++++++ tests/unit/Tap/WithBothRawTasks.cs | 177 +++++++++++++ ...thFullTaskOnFulfilledAndActionOnFaulted.cs | 133 ++++++++++ ...ithRawTaskOnFulfilledAndActionOnFaulted.cs | 158 ++++++++++++ ...hRawTaskOnFulfilledAndFullTaskOnFaulted.cs | 177 +++++++++++++ tests/unit/TaskChainingIfFaultedTests.cs | 177 ------------- tests/unit/TaskChainingIfFulfilledTests.cs | 239 ------------------ tests/unit/TaskChainingTapTests.cs | 196 -------------- 21 files changed, 2232 insertions(+), 656 deletions(-) create mode 100644 tests/unit/IfFaulted/WithAction.cs create mode 100644 tests/unit/IfFaulted/WithFullTaskFunc.cs create mode 100644 tests/unit/IfFaulted/WithRawTaskFunc.cs create mode 100644 tests/unit/IfFulfilled/WithAction.cs create mode 100644 tests/unit/IfFulfilled/WithFullTaskFunc.cs create mode 100644 tests/unit/IfFulfilled/WithRawTaskFunc.cs create mode 100644 tests/unit/Tap/WithActionOnFulfilledAndFullTaskOnFaulted.cs create mode 100644 tests/unit/Tap/WithActionOnFulfilledAndRawTaskOnFaulted.cs create mode 100644 tests/unit/Tap/WithBothActions.cs create mode 100644 tests/unit/Tap/WithBothFullTasks.cs create mode 100644 tests/unit/Tap/WithBothRawTasks.cs create mode 100644 tests/unit/Tap/WithFullTaskOnFulfilledAndActionOnFaulted.cs create mode 100644 tests/unit/Tap/WithRawTaskOnFulfilledAndActionOnFaulted.cs create mode 100644 tests/unit/Tap/WithRawTaskOnFulfilledAndFullTaskOnFaulted.cs delete mode 100644 tests/unit/TaskChainingIfFaultedTests.cs delete mode 100644 tests/unit/TaskChainingIfFulfilledTests.cs delete mode 100644 tests/unit/TaskChainingTapTests.cs diff --git a/README.md b/README.md index 8d4cda6..d943eed 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,8 @@ Task.FromResult("not-a-url") // The `IfFulfilled` and `IfFaulted` methods can be used to perform side effects such as logging when the `Task` is in the fulfilled or faulted state, respectively. +**NOTE**: These functions do _not_ trap errors. If the function passed to them throws an exception, the result is a faulted task with that exception. + ```c# HttpClient client; // Assuming this is coming from an HttpClientFactory or injected or whatever diff --git a/src/TaskExtensionsIfFaulted.cs b/src/TaskExtensionsIfFaulted.cs index 6fc2d0a..25f45c4 100644 --- a/src/TaskExtensionsIfFaulted.cs +++ b/src/TaskExtensionsIfFaulted.cs @@ -1,11 +1,8 @@ using System; -using System.Threading; using System.Threading.Tasks; namespace RLC.TaskChaining; -using static TaskStatics; - public static partial class TaskExtensions { /// @@ -39,13 +36,11 @@ public static Task IfFaulted(this Task task, Func Exception taskException = PotentiallyUnwindException(continuationTask.Exception!); return Task.FromException(PotentiallyUnwindException(continuationTask.Exception!)) - .Catch(ex => onFaulted(ex)) - .Then( - _ => Task.FromException(taskException), - _ => Task.FromException(taskException) - ); + .Catch(onFaulted) + .Then(_ => Task.FromException(taskException)); } - else if (continuationTask.IsCanceled) + + if (continuationTask.IsCanceled) { try { @@ -70,5 +65,5 @@ public static Task IfFaulted(this Task task, Func /// The function to execute if the task is faulted. /// The task. public static Task IfFaulted(this Task task, Func onFaulted) - => task.IfFaulted(exception => onFaulted(exception).ContinueWith(continuationTask => Task.FromException(exception)).Unwrap()); + => task.IfFaulted(exception => onFaulted(exception).ContinueWith(_ => Task.FromException(exception)).Unwrap()); } diff --git a/src/TaskExtensionsIfFulfilled.cs b/src/TaskExtensionsIfFulfilled.cs index fd9e81c..91426c0 100644 --- a/src/TaskExtensionsIfFulfilled.cs +++ b/src/TaskExtensionsIfFulfilled.cs @@ -24,25 +24,24 @@ public static Task IfFulfilled(this Task task, Action consumer) /// The function to execute if the task is fulfilled. /// The task. public static Task IfFulfilled(this Task task, Func> func) - => task.ContinueWith(async continuationTask => + => task.ContinueWith(continuationTask => { if (continuationTask.IsFaulted || continuationTask.IsCanceled) { return continuationTask; } - else - { - T value = await continuationTask; - return Task.FromResult(value).Then(func).Then(_ => value, _ => value); - } - }).Unwrap().Unwrap(); + return continuationTask.Then(value => + { + func(value); + return value; + }); + }).Unwrap(); /// /// Executes a function and throws away the result if the is in a fulfilled state. /// /// The task's underlying type. - /// The type of the discarded result of . /// The task. /// The function to execute if the task is fulfilled. /// The task. @@ -53,13 +52,11 @@ public static Task IfFulfilled(this Task task, Func func) { return continuationTask; } - else + + return continuationTask.Then(async value => { - return continuationTask.Then(async value => - { - await func(value); - return value; - }); - } + await func(value); + return value; + }); }).Unwrap(); } diff --git a/src/TaskExtensionsTap.cs b/src/TaskExtensionsTap.cs index b5cf8e5..414120a 100644 --- a/src/TaskExtensionsTap.cs +++ b/src/TaskExtensionsTap.cs @@ -1,17 +1,14 @@ using System; -using System.Threading; using System.Threading.Tasks; namespace RLC.TaskChaining; -using static TaskStatics; - public static partial class TaskExtensions { /// /// Executes a function and discards the result on a whether it is in a fulfilled or faulted state. /// - /// This method is useful if you need to perform a side effect without altering the 's + /// This method is useful if you need to perform a side effect without altering the 's /// value, such as logging. /// The task's underlying type. /// The task. @@ -19,12 +16,22 @@ public static partial class TaskExtensions /// The function to execute if the task is faulted. /// The task. public static Task Tap(this Task task, Action onFulfilled, Action onFaulted) - => task.IfFulfilled(onFulfilled).IfFaulted(onFaulted); + => task.ContinueWith(continuationTask => + { + if (continuationTask.IsCanceled) + { + return continuationTask; + } + + return continuationTask.IsFaulted + ? continuationTask.IfFaulted(onFaulted) + : continuationTask.IfFulfilled(onFulfilled); + }).Unwrap(); /// /// Executes a function and discards the result on a whether it is in a fulfilled or faulted state. /// - /// This method is useful if you need to perform a side effect without altering the 's + /// This method is useful if you need to perform a side effect without altering the 's /// value, such as logging. /// The task's underlying type. /// The output type of the function. @@ -33,12 +40,19 @@ public static Task Tap(this Task task, Action onFulfilled, ActionThe function to execute if the task is faulted. /// The task. public static Task Tap(this Task task, Action onFulfilled, Func> onFaulted) - => task.IfFulfilled(onFulfilled).IfFaulted(onFaulted); + => task.Tap( + value => + { + onFulfilled(value); + return Task.FromResult(value); + }, + onFaulted + ); /// /// Executes a function and discards the result on a whether it is in a fulfilled or faulted state. /// - /// This method is useful if you need to perform a side effect without altering the 's + /// This method is useful if you need to perform a side effect without altering the 's /// value, such as logging. /// The task's underlying type. /// The task. @@ -46,12 +60,22 @@ public static Task Tap(this Task task, Action onFulfilled, FuncThe function to execute if the task is faulted. /// The task. public static Task Tap(this Task task, Action onFulfilled, Func onFaulted) - => task.IfFulfilled(onFulfilled).IfFaulted(onFaulted); + => task.ContinueWith(continuationTask => + { + if (continuationTask.IsCanceled) + { + return continuationTask; + } + + return continuationTask.IsFaulted + ? continuationTask.IfFaulted(onFaulted) + : continuationTask.IfFulfilled(onFulfilled); + }).Unwrap(); /// /// Executes a function and discards the result on a whether it is in a fulfilled or faulted state. /// - /// This method is useful if you need to perform a side effect without altering the 's + /// This method is useful if you need to perform a side effect without altering the 's /// value, such as logging. /// The task's underlying type. /// The output type of the function. @@ -60,12 +84,22 @@ public static Task Tap(this Task task, Action onFulfilled, FuncThe function to execute if the task is faulted. /// The task. public static Task Tap(this Task task, Func> onFulfilled, Action onFaulted) - => task.IfFulfilled(onFulfilled).IfFaulted(onFaulted); + => task.ContinueWith(continuationTask => + { + if (continuationTask.IsCanceled) + { + return continuationTask; + } + + return continuationTask.IsFaulted + ? continuationTask.IfFaulted(onFaulted) + : continuationTask.IfFulfilled(onFulfilled); + }).Unwrap(); /// /// Executes a function and discards the result on a whether it is in a fulfilled or faulted state. /// - /// This method is useful if you need to perform a side effect without altering the 's + /// This method is useful if you need to perform a side effect without altering the 's /// value, such as logging. /// The task's underlying type. /// The task. @@ -73,12 +107,22 @@ public static Task Tap(this Task task, Func> onFulfilled, /// The function to execute if the task is faulted. /// The task. public static Task Tap(this Task task, Func onFulfilled, Action onFaulted) - => task.IfFulfilled(onFulfilled).IfFaulted(onFaulted); + => task.ContinueWith(continuationTask => + { + if (continuationTask.IsCanceled) + { + return continuationTask; + } + + return continuationTask.IsFaulted + ? continuationTask.IfFaulted(onFaulted) + : continuationTask.IfFulfilled(onFulfilled); + }).Unwrap(); /// /// Executes a function and discards the result on a whether it is in a fulfilled or faulted state. /// - /// This method is useful if you need to perform a side effect without altering the 's + /// This method is useful if you need to perform a side effect without altering the 's /// value, such as logging. /// The task's underlying type. /// The output type of the function. @@ -87,12 +131,22 @@ public static Task Tap(this Task task, Func onFulfilled, Actio /// The function to execute if the task is faulted. /// The task. public static Task Tap(this Task task, Func> onFulfilled, Func> onFaulted) - => task.IfFulfilled(onFulfilled).IfFaulted(onFaulted); + => task.ContinueWith(continuationTask => + { + if (continuationTask.IsCanceled) + { + return continuationTask; + } + + return continuationTask.IsFaulted + ? continuationTask.IfFaulted(onFaulted) + : continuationTask.IfFulfilled(onFulfilled); + }).Unwrap(); /// /// Executes a function and discards the result on a whether it is in a fulfilled or faulted state. /// - /// This method is useful if you need to perform a side effect without altering the 's + /// This method is useful if you need to perform a side effect without altering the 's /// value, such as logging. /// The task's underlying type. /// The task. @@ -100,12 +154,22 @@ public static Task Tap(this Task task, Func> onFulfill /// The function to execute if the task is faulted. /// The task. public static Task Tap(this Task task, Func onFulfilled, Func onFaulted) - => task.IfFulfilled(onFulfilled).IfFaulted(onFaulted); + => task.ContinueWith(continuationTask => + { + if (continuationTask.IsCanceled) + { + return continuationTask; + } + + return continuationTask.IsFaulted + ? continuationTask.IfFaulted(onFaulted) + : continuationTask.IfFulfilled(onFulfilled); + }).Unwrap(); /// /// Executes a function and discards the result on a whether it is in a fulfilled or faulted state. /// - /// This method is useful if you need to perform a side effect without altering the 's + /// This method is useful if you need to perform a side effect without altering the 's /// value, such as logging. /// The task's underlying type. /// The task. @@ -113,5 +177,12 @@ public static Task Tap(this Task task, Func onFulfilled, Func< /// The function to execute if the task is faulted. /// The task. public static Task Tap(this Task task, Func onFulfilled, Func> onFaulted) - => task.IfFulfilled(onFulfilled).IfFaulted(onFaulted); + => task.Tap( + value => + { + onFulfilled(value); + return Task.FromResult(value); + }, + onFaulted + ); } diff --git a/tests/unit/IfFaulted/WithAction.cs b/tests/unit/IfFaulted/WithAction.cs new file mode 100644 index 0000000..6b736f5 --- /dev/null +++ b/tests/unit/IfFaulted/WithAction.cs @@ -0,0 +1,114 @@ +using System; +using System.Threading.Tasks; +using RLC.TaskChaining; +using Xunit; + +namespace RLC.TaskChainingTests.IfFaulted; + +public class WithAction +{ + [Fact] + public async Task ItShouldPerformASideEffectWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 5; + Action onFaulted = _ => { actualValue = 5; }; + + _ = Task.FromException(new ArgumentNullException()) + .IfFaulted(onFaulted); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldNotPerformASideEffectForAResolution() + { + int actualValue = 0; + int expectedValue = 0; + Action onFaulted = _ => { actualValue = 5; }; + + await Task.FromResult(5) + .IfFaulted(onFaulted); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldNotPerformASideEffectForAResolutionWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 0; + Action onFaulted = _ => { actualValue = 5; }; + + _ = Task.FromResult(5) + .IfFaulted(onFaulted); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectForACancellation() + { + int actualValue = 0; + int expectedValue = 5; + Func func = _ => throw new TaskCanceledException(); + Action onFaulted = _ => { actualValue = 5; }; + + try + { + await Task.FromResult(0) + .Then(func) + .IfFaulted(onFaulted); + } + catch (OperationCanceledException) + { + } + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectForACancellationWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 5; + Func func = _ => throw new TaskCanceledException(); + Action onFaulted = _ => { actualValue = 5; }; + + _ = Task.FromResult(0) + .Then(func) + .IfFaulted(onFaulted); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldChangeExceptionTypesForExceptionThrowingFunc() + { + Func func = _ => throw new ArgumentException(); + Action onFaulted = _ => throw new NullReferenceException(); + + Task actualValue = Task.FromResult(5) + .Then(func) + .IfFaulted(onFaulted); + + await Assert.ThrowsAsync(() => actualValue); + } + + [Fact] + public async Task ItShouldResultInAFaultedTaskWithAnException() + { + Action onFaulted = _ => throw new ArgumentNullException(); + + Task testTask = Task.FromException(new NullReferenceException()) + .IfFaulted(onFaulted); + + await Assert.ThrowsAsync(() => testTask); + } +} \ No newline at end of file diff --git a/tests/unit/IfFaulted/WithFullTaskFunc.cs b/tests/unit/IfFaulted/WithFullTaskFunc.cs new file mode 100644 index 0000000..87c625f --- /dev/null +++ b/tests/unit/IfFaulted/WithFullTaskFunc.cs @@ -0,0 +1,157 @@ +using System; +using System.Threading.Tasks; +using RLC.TaskChaining; +using Xunit; + +namespace RLC.TaskChainingTests.IfFaulted; + +public class WithFullTaskFunc +{ + [Fact] + public async Task ItShouldPerformASideEffect() + { + int actualValue = 0; + int expectedValue = 5; + Func> onFaulted = _ => + { + actualValue = 5; + return Task.FromResult(5); + }; + + try + { + await Task.FromException(new ArgumentNullException()) + .IfFaulted(onFaulted); + } + catch (ArgumentNullException exception) + { + } + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 5; + Func> onFaulted = _ => + { + actualValue = 5; + return Task.FromResult(5); + }; + + _ = Task.FromException(new ArgumentNullException()) + .IfFaulted(onFaulted); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldNotPerformASideEffectForAResolution() + { + int actualValue = 0; + int expectedValue = 0; + Func> onFaulted = _ => + { + actualValue = 5; + return Task.FromResult(5); + }; + + await Task.FromResult(5) + .IfFaulted(onFaulted); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldNotPerformASideEffectForAResolutionWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 0; + Func> onFaulted = _ => + { + actualValue = 5; + return Task.FromResult(5); + }; + + _ = Task.FromResult(5) + .IfFaulted(onFaulted); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectForACancellation() + { + int actualValue = 0; + int expectedValue = 5; + Func func = _ => throw new TaskCanceledException(); + Func> onFaulted = _ => + { + actualValue = 5; + return Task.FromResult(5); + }; + + try + { + await Task.FromResult(0) + .Then(func) + .IfFaulted(onFaulted); + } + catch (OperationCanceledException) + { + } + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectForACancellationWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 5; + Func func = _ => throw new TaskCanceledException(); + Func> onFaulted = _ => + { + actualValue = 5; + return Task.FromResult(5); + }; + + _ = Task.FromResult(0) + .Then(func) + .IfFaulted(onFaulted); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldChangeExceptionTypesForExceptionThrowingFunc() + { + Func func = _ => throw new ArgumentException(); + Func> onFaulted = _ => throw new NullReferenceException(); + + Task actualValue = Task.FromResult(5) + .Then(func) + .IfFaulted(onFaulted); + + await Assert.ThrowsAsync(() => actualValue); + } + + [Fact] + public async Task ItShouldResultInAFaultedTaskWithAnException() + { + Func> func = _ => throw new ArgumentNullException(); + + Task testTask = Task.FromException(new NullReferenceException()) + .IfFaulted(func); + + await Assert.ThrowsAsync(() => testTask); + } +} \ No newline at end of file diff --git a/tests/unit/IfFaulted/WithRawTaskFunc.cs b/tests/unit/IfFaulted/WithRawTaskFunc.cs new file mode 100644 index 0000000..e2d643a --- /dev/null +++ b/tests/unit/IfFaulted/WithRawTaskFunc.cs @@ -0,0 +1,158 @@ +using System; +using System.Threading.Tasks; +using RLC.TaskChaining; +using Xunit; +using Xunit.Sdk; + +namespace RLC.TaskChainingTests.IfFaulted; + +public class WithRawTaskFunc +{ + [Fact] + public async Task ItShouldPerformASideEffect() + { + int actualValue = 0; + int expectedValue = 5; + Func onFaulted = _ => + { + actualValue = 5; + return Task.CompletedTask; + }; + + try + { + await Task.FromException(new ArgumentNullException()) + .IfFaulted(onFaulted); + } + catch (ArgumentNullException exception) + { + } + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 5; + Func onFaulted = _ => + { + actualValue = 5; + return Task.CompletedTask; + }; + + _ = Task.FromException(new ArgumentNullException()) + .IfFaulted(onFaulted); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldNotPerformASideEffectForAResolution() + { + int actualValue = 0; + int expectedValue = 0; + Func onFaulted = _ => + { + actualValue = 5; + return Task.CompletedTask; + }; + + await Task.FromResult(5) + .IfFaulted(onFaulted); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldNotPerformASideEffectForAResolutionWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 0; + Func> onFaulted = _ => + { + actualValue = 5; + return Task.FromResult(5); + }; + + _ = Task.FromResult(5) + .IfFaulted(onFaulted); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectForACancellation() + { + int actualValue = 0; + int expectedValue = 5; + Func func = _ => throw new TaskCanceledException(); + Func onFaulted = _ => + { + actualValue = 5; + return Task.CompletedTask; + }; + + try + { + await Task.FromResult(0) + .Then(func) + .IfFaulted(onFaulted); + } + catch (OperationCanceledException) + { + } + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectForACancellationWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 5; + Func func = _ => throw new TaskCanceledException(); + Func onFaulted = _ => + { + actualValue = 5; + return Task.CompletedTask; + }; + + _ = Task.FromResult(0) + .Then(func) + .IfFaulted(onFaulted); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldChangeExceptionTypesForExceptionThrowingFunc() + { + Func func = _ => throw new ArgumentException(); + Func onFaulted = _ => throw new NullReferenceException(); + + Task actualValue = Task.FromResult(5) + .Then(func) + .IfFaulted(onFaulted); + + await Assert.ThrowsAsync(() => actualValue); + } + + [Fact] + public async Task ItShouldResultInAFaultedTaskWithAnException() + { + Func func = _ => throw new ArgumentNullException(); + + Task testTask = Task.FromException(new NullReferenceException()) + .IfFaulted(func); + + await Assert.ThrowsAsync(() => testTask); + } +} \ No newline at end of file diff --git a/tests/unit/IfFulfilled/WithAction.cs b/tests/unit/IfFulfilled/WithAction.cs new file mode 100644 index 0000000..d12f265 --- /dev/null +++ b/tests/unit/IfFulfilled/WithAction.cs @@ -0,0 +1,114 @@ +using System; +using System.Threading.Tasks; +using RLC.TaskChaining; +using Xunit; + +namespace RLC.TaskChainingTests.IfFulfilled; + +public class WithAction +{ + [Fact] + public async Task ItShouldPerformASideEffect() + { + int actualValue = 0; + int expectedValue = 5; + + await Task.FromResult(5) + .IfFulfilled(value => { actualValue = value; }); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 5; + + _ = Task.FromResult(5) + .IfFulfilled(value => { actualValue = value; }); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldNotPerformASideEffectForAFault() + { + int actualValue = 0; + int expectedValue = 0; + + try + { + await Task.FromException(new ArgumentNullException()) + .IfFulfilled(_ => { actualValue = 5; }); + } + catch (ArgumentNullException) + { + } + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldNotPerformASideEffectForAFaultWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 0; + + _ = Task.FromException(new ArgumentNullException()) + .IfFulfilled(_ => { actualValue = 5; }); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldNotPerformASideEffectForACancellation() + { + int actualValue = 0; + int expectedValue = 0; + Func func = _ => throw new TaskCanceledException(); + + try + { + await Task.FromResult(0) + .Then(func) + .IfFulfilled(_ => { actualValue = 5; }); + } + catch (OperationCanceledException) + { + } + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldNotPerformASideEffectForACancellationWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 0; + Func func = _ => throw new TaskCanceledException(); + + _ = Task.FromResult(0) + .Then(func) + .IfFulfilled(_ => { actualValue = 5; }); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldResultInAFaultedTaskWithAnException() + { + Action func = _ => throw new ArgumentNullException(); + + Task testTask = Task.FromResult(0) + .IfFulfilled(func); + + await Assert.ThrowsAsync(() => testTask); + } +} \ No newline at end of file diff --git a/tests/unit/IfFulfilled/WithFullTaskFunc.cs b/tests/unit/IfFulfilled/WithFullTaskFunc.cs new file mode 100644 index 0000000..e2acbf7 --- /dev/null +++ b/tests/unit/IfFulfilled/WithFullTaskFunc.cs @@ -0,0 +1,144 @@ +using System; +using System.Threading.Tasks; +using RLC.TaskChaining; +using Xunit; + +namespace RLC.TaskChainingTests.IfFulfilled; + +public class WithFullTaskFunc +{ + [Fact] + public async Task ItShouldPerformASideEffect() + { + int actualValue = 0; + int expectedValue = 5; + Func> onFulfilled = _ => + { + actualValue = 5; + return Task.FromResult(5); + }; + + await Task.FromResult(5) + .IfFulfilled(onFulfilled); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 5; + Func> onFulfilled = _ => + { + actualValue = 5; + return Task.FromResult(5); + }; + + _ = Task.FromResult(5) + .IfFulfilled(onFulfilled); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldNotPerformASideEffectForAFault() + { + int actualValue = 0; + int expectedValue = 0; + Func> onFulfilled = _ => + { + actualValue = 5; + return Task.FromResult(5); + }; + + try + { + await Task.FromException(new ArgumentNullException()) + .IfFulfilled(onFulfilled); + } + catch (ArgumentNullException) + { + } + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldNotPerformASideEffectForAFaultWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 0; + Func> onFulfilled = _ => + { + actualValue = 5; + return Task.FromResult(5); + }; + + _ = Task.FromException(new ArgumentNullException()) + .IfFulfilled(onFulfilled); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldNotPerformASideEffectForACancellation() + { + int actualValue = 0; + int expectedValue = 0; + Func func = _ => throw new TaskCanceledException(); + Func> onFulfilled = _ => + { + actualValue = 5; + return Task.FromResult(5); + }; + + try + { + await Task.FromResult(0) + .Then(func) + .IfFulfilled(onFulfilled); + } + catch (OperationCanceledException) + { + } + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldNotPerformASideEffectForACancellationWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 0; + Func func = _ => throw new TaskCanceledException(); + Func> onFulfilled = _ => + { + actualValue = 5; + return Task.FromResult(5); + }; + + _ = Task.FromResult(0) + .Then(func) + .IfFulfilled(onFulfilled); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldResultInAFaultedTaskWithAnException() + { + Func> onFulfilled = _ => throw new ArgumentNullException(); + + Task testTask = Task.FromResult(0) + .IfFulfilled(onFulfilled); + + await Assert.ThrowsAsync(() => testTask); + } +} \ No newline at end of file diff --git a/tests/unit/IfFulfilled/WithRawTaskFunc.cs b/tests/unit/IfFulfilled/WithRawTaskFunc.cs new file mode 100644 index 0000000..c3d1e38 --- /dev/null +++ b/tests/unit/IfFulfilled/WithRawTaskFunc.cs @@ -0,0 +1,144 @@ +using System; +using System.Threading.Tasks; +using RLC.TaskChaining; +using Xunit; + +namespace RLC.TaskChainingTests.IfFulfilled; + +public class WithRawTaskFunc +{ + [Fact] + public async Task ItShouldPerformASideEffect() + { + int actualValue = 0; + int expectedValue = 5; + Func onFulfilled = _ => + { + actualValue = 5; + return Task.CompletedTask; + }; + + await Task.FromResult(5) + .IfFulfilled(onFulfilled); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 5; + Func onFulfilled = _ => + { + actualValue = 5; + return Task.CompletedTask; + }; + + _ = Task.FromResult(5) + .IfFulfilled(onFulfilled); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldNotPerformASideEffectForAFault() + { + int actualValue = 0; + int expectedValue = 0; + Func onFulfilled = _ => + { + actualValue = 5; + return Task.CompletedTask; + }; + + try + { + await Task.FromException(new ArgumentNullException()) + .IfFulfilled(onFulfilled); + } + catch (ArgumentNullException) + { + } + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldNotPerformASideEffectForAFaultWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 0; + Func onFulfilled = _ => + { + actualValue = 5; + return Task.CompletedTask; + }; + + _ = Task.FromException(new ArgumentNullException()) + .IfFulfilled(onFulfilled); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldNotPerformASideEffectForACancellation() + { + int actualValue = 0; + int expectedValue = 0; + Func func = _ => throw new TaskCanceledException(); + Func onFulfilled = _ => + { + actualValue = 5; + return Task.CompletedTask; + }; + + try + { + await Task.FromResult(0) + .Then(func) + .IfFulfilled(onFulfilled); + } + catch (OperationCanceledException) + { + } + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldNotPerformASideEffectForACancellationWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 0; + Func func = _ => throw new TaskCanceledException(); + Func onFulfilled = _ => + { + actualValue = 5; + return Task.CompletedTask; + }; + + _ = Task.FromResult(0) + .Then(func) + .IfFulfilled(onFulfilled); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldResultInAFaultedTaskWithAnException() + { + Func onFulfilled = _ => throw new ArgumentNullException(); + + Task testTask = Task.FromResult(0) + .IfFulfilled(onFulfilled); + + await Assert.ThrowsAsync(() => testTask); + } +} \ No newline at end of file diff --git a/tests/unit/Tap/WithActionOnFulfilledAndFullTaskOnFaulted.cs b/tests/unit/Tap/WithActionOnFulfilledAndFullTaskOnFaulted.cs new file mode 100644 index 0000000..9712593 --- /dev/null +++ b/tests/unit/Tap/WithActionOnFulfilledAndFullTaskOnFaulted.cs @@ -0,0 +1,162 @@ +using System; +using System.Threading.Tasks; +using RLC.TaskChaining; +using Xunit; + +namespace RLC.TaskChainingTests.Tap; + +public class WithActionOnFulfilledAndFullTaskOnFaulted +{ + [Fact] + public async Task ItShouldPerformASideEffectOnAResolution() + { + int actualValue = 0; + int expectedValue = 5; + Action onFulfilled = i => { actualValue = i; }; + Func> onFaulted = _ => Task.FromResult(1); + + await Task.FromResult(5) + .Tap(onFulfilled, onFaulted); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectOnAResolutionWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 5; + Action onFulfilled = value => { actualValue = value; }; + Func> onFaulted = _ => + { + return Task.FromResult(1); + }; + + _ = Task.FromResult(5) + .Tap(onFulfilled, onFaulted); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectOnAFault() + { + int actualValue = 0; + int expectedValue = 5; + Action onFulfilled = _ => { }; + Func> onFaulted = _ => + { + actualValue = 5; + return Task.FromResult(1); + }; + + try + { + await Task.FromException(new ArgumentNullException()) + .Tap(onFulfilled, onFaulted); + } + catch (ArgumentNullException) + { + } + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectOnAFaultWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 5; + Action onFulfilled = _ => { }; + Func> onFaulted = _ => + { + actualValue = 5; + return Task.FromResult(1); + }; + + _ = Task.FromException(new ArgumentNullException()) + .Tap(onFulfilled, onFaulted); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectOnACancellation() + { + int actualValue = 0; + int expectedValue = 5; + Func> func = _ => throw new TaskCanceledException(); + Action onFulfilled = _ => { actualValue = 5; }; + Func> onFaulted = _ => + { + actualValue = 5; + return Task.FromResult(1); + }; + + _ = Task.FromResult(0) + .Then(func) + .Tap(onFulfilled, onFaulted); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectOnACancellationWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 5; + Func func = _ => throw new TaskCanceledException(); + Action onFulfilled = _ => { actualValue = 5; }; + Func> onFaulted = _ => + { + actualValue = 5; + return Task.FromResult(1); + }; + + _ = Task.FromResult(0) + .Then(func) + .Tap(onFulfilled, onFaulted); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldNotCallOnFaultedIfOnFulfilledThrows() + { + Action onFulfilled = _ => throw new ArgumentException(); + Func> onFaulted = _ => Task.FromResult(1); + + Task testTask = Task.FromResult(0) + .Tap(onFulfilled, onFaulted); + + await Assert.ThrowsAsync(() => testTask); + } + + [Fact] + public async Task ItShouldNotCallOnFaultedIfOnFulfilledThrowsWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 5; + Action onFulfilled = _ => throw new ArgumentException(); + Func> onFaulted = _ => + { + actualValue = 5; + return Task.FromResult(1); + }; + + _ = Task.FromResult(0) + .Tap(onFulfilled, onFaulted); + + await Task.Delay(10); + + Assert.NotEqual(expectedValue, actualValue); + } +} \ No newline at end of file diff --git a/tests/unit/Tap/WithActionOnFulfilledAndRawTaskOnFaulted.cs b/tests/unit/Tap/WithActionOnFulfilledAndRawTaskOnFaulted.cs new file mode 100644 index 0000000..532871d --- /dev/null +++ b/tests/unit/Tap/WithActionOnFulfilledAndRawTaskOnFaulted.cs @@ -0,0 +1,159 @@ +using System; +using System.Threading.Tasks; +using RLC.TaskChaining; +using Xunit; + +namespace RLC.TaskChainingTests.Tap; + +public class WithActionOnFulfilledAndRawTaskOnFaulted +{ + [Fact] + public async Task ItShouldPerformASideEffectOnAResolution() + { + int actualValue = 0; + int expectedValue = 5; + Action onFulfilled = i => { actualValue = i; }; + Func onFaulted = _ => Task.CompletedTask; + + await Task.FromResult(5) + .Tap(onFulfilled, onFaulted); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectOnAResolutionWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 5; + Action onFulfilled = i => { actualValue = i; }; + Func onFaulted = _ => Task.CompletedTask; + + _ = Task.FromResult(5) + .Tap(onFulfilled, onFaulted); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectOnAFault() + { + int actualValue = 0; + int expectedValue = 5; + Action onFulfilled = _ => { }; + Func onFaulted = _ => + { + actualValue = 5; + return Task.CompletedTask; + }; + + try + { + await Task.FromException(new ArgumentNullException()) + .Tap(onFulfilled, onFaulted); + } + catch (ArgumentNullException) + { + } + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectOnAFaultWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 5; + Action onFulfilled = _ => { }; + Func onFaulted = _ => + { + actualValue = 5; + return Task.CompletedTask; + }; + + _ = Task.FromException(new ArgumentNullException()) + .Tap(onFulfilled, onFaulted); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectOnACancellation() + { + int actualValue = 0; + int expectedValue = 5; + Func> func = _ => throw new TaskCanceledException(); + Action onFulfilled = _ => { actualValue = 0; }; + Func onFaulted = _ => + { + actualValue = 5; + return Task.CompletedTask; + }; + + _ = Task.FromResult(0) + .Then(func) + .Tap(onFulfilled, onFaulted); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectOnACancellationWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 5; + Func func = _ => throw new TaskCanceledException(); + Action onFulfilled = _ => { actualValue = 0; }; + Func onFaulted = _ => + { + actualValue = 5; + return Task.CompletedTask; + }; + + _ = Task.FromResult(0) + .Then(func) + .Tap(onFulfilled, onFaulted); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldNotCallOnFaultedIfOnFulfilledThrows() + { + Action onFulfilled = _ => throw new ArgumentException(); + Func onFaulted = _ => Task.CompletedTask; + + Task testTask = Task.FromResult(0) + .Tap(onFulfilled, onFaulted); + + await Assert.ThrowsAsync(() => testTask); + } + + [Fact] + public async Task ItShouldNotCallOnFaultedIfOnFulfilledThrowsWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 5; + Action onFulfilled = _ => throw new ArgumentException(); + Func onFaulted = _ => + { + actualValue = 5; + return Task.CompletedTask; + }; + + _ = Task.FromResult(0) + .Tap(onFulfilled, onFaulted); + + await Task.Delay(10); + + Assert.NotEqual(expectedValue, actualValue); + } +} \ No newline at end of file diff --git a/tests/unit/Tap/WithBothActions.cs b/tests/unit/Tap/WithBothActions.cs new file mode 100644 index 0000000..75cdd4a --- /dev/null +++ b/tests/unit/Tap/WithBothActions.cs @@ -0,0 +1,147 @@ +using System; +using System.Threading.Tasks; +using RLC.TaskChaining; +using Xunit; + +namespace RLC.TaskChainingTests.Tap; + +public class WithBothActions +{ + [Fact] + public async Task ItShouldPerformASideEffectOnAResolution() + { + int actualValue = 0; + int expectedValue = 5; + Action onFulfilled = value => { actualValue = value; }; + Action onFaulted = _ => { }; + + await Task.FromResult(5) + .Tap(onFulfilled, onFaulted); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectOnAResolutionWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 5; + Action onFulfilled = value => { actualValue = value; }; + Action onFaulted = _ => { }; + + _ = Task.FromResult(5) + .Tap(onFulfilled, onFaulted); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectOnAFault() + { + int actualValue = 0; + int expectedValue = 5; + Action onFulfilled = _ => { }; + Action onFaulted = _ => { actualValue = 5; }; + + try + { + await Task.FromException(new ArgumentNullException()) + .Tap(onFulfilled, onFaulted); + } + catch + { + // ignored + } + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectOnAFaultWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 5; + Action onFulfilled = _ => { }; + Action onFaulted = _ => { actualValue = 5; }; + + _ = Task.FromException(new ArgumentNullException()) + .Tap(onFulfilled, onFaulted); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectOnACancellation() + { + int actualValue = 0; + int expectedValue = 5; + Func func = _ => throw new TaskCanceledException(); + Action onFulfilled = _ => { actualValue = 0; }; + Action onFaulted = _ => { actualValue = 5; }; + + try + { + await Task.FromResult(0) + .Then(func) + .Tap(onFulfilled, onFaulted); + } + catch (TaskCanceledException) + { + } + + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectOnACancellationWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 5; + Func func = _ => throw new TaskCanceledException(); + Action onFulfilled = _ => { actualValue = 0; }; + Action onFaulted = _ => { actualValue = 5; }; + + _ = Task.FromResult(0) + .Then(func) + .Tap(onFulfilled, onFaulted); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldNotCallOnFaultedIfOnFulfilledThrows() + { + Action onFulfilled = _ => throw new ArgumentException(); + Action onFaulted = _ => { }; + + Task testTask = Task.FromResult(0) + .Tap(onFulfilled, onFaulted); + + await Assert.ThrowsAsync(() => testTask); + } + + [Fact] + public async Task ItShouldNotCallOnFaultedIfOnFulfilledThrowsWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 5; + Action onFulfilled = _ => throw new ArgumentException(); + Action onFaulted = _ => { actualValue = 5; }; + + _ = Task.FromResult(0) + .Tap(onFulfilled, onFaulted); + + await Task.Delay(10); + + Assert.NotEqual(expectedValue, actualValue); + } +} \ No newline at end of file diff --git a/tests/unit/Tap/WithBothFullTasks.cs b/tests/unit/Tap/WithBothFullTasks.cs new file mode 100644 index 0000000..a4cabfc --- /dev/null +++ b/tests/unit/Tap/WithBothFullTasks.cs @@ -0,0 +1,179 @@ +using System; +using System.Threading.Tasks; +using RLC.TaskChaining; +using Xunit; + +namespace RLC.TaskChainingTests.Tap; + +public class WithBothFullTasks +{ + [Fact] + public async Task ItShouldPerformASideEffectOnAResolution() + { + int actualValue = 0; + int expectedValue = 5; + Func> onFulfilled = i => + { + actualValue = i; + + return Task.FromResult(i.ToString()); + }; + Func> onFaulted = _ => Task.FromResult("1"); + + await Task.FromResult(5) + .Tap(onFulfilled, onFaulted); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectOnAResolutionWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 5; + Func> onFulfilled = i => + { + actualValue = i; + + return Task.FromResult(i); + }; + Func> onFaulted = _ => Task.FromResult(1); + + _ = Task.FromResult(5) + .Tap(onFulfilled, onFaulted); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectOnAFault() + { + int actualValue = 0; + int expectedValue = 5; + Func> onFulfilled = _ => Task.FromResult(5); + Func> onFaulted = _ => + { + actualValue = 5; + return Task.FromResult(1); + }; + + try + { + await Task.FromException(new ArgumentNullException()) + .Tap(onFulfilled, onFaulted); + } + catch (ArgumentNullException) + { + } + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectOnAFaultWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 5; + Func> onFulfilled = _ => Task.FromResult(5); + Func> onFaulted = _ => + { + actualValue = 5; + return Task.FromResult(1); + }; + + _ = Task.FromException(new ArgumentNullException()) + .Tap(onFulfilled, onFaulted); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectOnACancellation() + { + int actualValue = 0; + int expectedValue = 5; + Func> func = _ => throw new TaskCanceledException(); + Func> onFulfilled = i => + { + actualValue = 0; + + return Task.FromResult(i); + }; + Func> onFaulted = _ => + { + actualValue = 5; + return Task.FromResult(1); + }; + + _ = Task.FromResult(0) + .Then(func) + .Tap(onFulfilled, onFaulted); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectOnACancellationWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 5; + Func func = _ => throw new TaskCanceledException(); + Func> onFulfilled = i => + { + actualValue = 0; + + return Task.FromResult(i); + }; + Func> onFaulted = _ => + { + actualValue = 5; + return Task.FromResult(1); + }; + + _ = Task.FromResult(0) + .Then(func) + .Tap(onFulfilled, onFaulted); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldNotCallOnFaultedIfOnFulfilledThrows() + { + Func> onFulfilled = _ => throw new ArgumentException(); + Func> onFaulted = _ => Task.FromResult(1); + + Task testTask = Task.FromResult(0) + .Tap(onFulfilled, onFaulted); + + await Assert.ThrowsAsync(() => testTask); + } + + [Fact] + public async Task ItShouldNotCallOnFaultedIfOnFulfilledThrowsWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 5; + Func> onFulfilled = _ => throw new ArgumentException(); + Func> onFaulted = _ => + { + actualValue = 5; + return Task.FromResult(1); + }; + + _ = Task.FromResult(0) + .Tap(onFulfilled, onFaulted); + + await Task.Delay(10); + + Assert.NotEqual(expectedValue, actualValue); + } +} \ No newline at end of file diff --git a/tests/unit/Tap/WithBothRawTasks.cs b/tests/unit/Tap/WithBothRawTasks.cs new file mode 100644 index 0000000..2a98dc3 --- /dev/null +++ b/tests/unit/Tap/WithBothRawTasks.cs @@ -0,0 +1,177 @@ +using System; +using System.Threading.Tasks; +using RLC.TaskChaining; +using Xunit; + +namespace RLC.TaskChainingTests.Tap; + +public class WithBothRawTasks +{ + [Fact] + public async Task ItShouldPerformASideEffectOnAResolution() + { + int actualValue = 0; + int expectedValue = 5; + Func onFulfilled = i => + { + actualValue = i; + + return Task.CompletedTask; + }; + Func onFaulted = _ => Task.CompletedTask; + + await Task.FromResult(5) + .Tap(onFulfilled, onFaulted); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectOnAResolutionWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 5; + Func onFulfilled = value => + { + actualValue = value; + return Task.CompletedTask; + }; + Func onFaulted = _ => Task.CompletedTask; + + _ = Task.FromResult(5) + .Tap(onFulfilled, onFaulted); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectOnAFault() + { + int actualValue = 0; + int expectedValue = 5; + Func onFulfilled = _ => Task.CompletedTask; + Func onFaulted = _ => + { + actualValue = 5; + return Task.CompletedTask; + }; + + try + { + await Task.FromException(new ArgumentNullException()) + .Tap(onFulfilled, onFaulted); + } + catch + { + // ignored + } + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectOnAFaultWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 5; + Func onFulfilled = _ => Task.CompletedTask; + Func onFaulted = _ => + { + actualValue = 5; + return Task.CompletedTask; + }; + + _ = Task.FromException(new ArgumentNullException()) + .Tap(onFulfilled, onFaulted); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectOnACancellation() + { + int actualValue = 0; + int expectedValue = 5; + Func func = _ => throw new TaskCanceledException(); + Func onFulfilled = _ => + { + actualValue = 0; + return Task.CompletedTask; + }; + Func onFaulted = _ => + { + actualValue = 5; + return Task.CompletedTask; + }; + + _ = Task.FromResult(0) + .Then(func) + .Tap(onFulfilled, onFaulted); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectOnACancellationWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 5; + Func func = _ => throw new TaskCanceledException(); + Func onFulfilled = _ => + { + actualValue = 0; + return Task.CompletedTask; + }; + Func onFaulted = _ => + { + actualValue = 5; + return Task.CompletedTask; + }; + + _ = Task.FromResult(0) + .Then(func) + .Tap(onFulfilled, onFaulted); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldNotCallOnFaultedIfOnFulfilledThrows() + { + Func onFulfilled = _ => throw new ArgumentException(); + Func onFaulted = _ => Task.CompletedTask; + + Task testTask = Task.FromResult(0) + .Tap(onFulfilled, onFaulted); + + await Assert.ThrowsAsync(() => testTask); + } + + [Fact] + public async Task ItShouldNotCallOnFaultedIfOnFulfilledThrowsWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 5; + Func onFulfilled = _ => throw new ArgumentException(); + Func onFaulted = _ => + { + actualValue = 5; + return Task.CompletedTask; + }; + + _ = Task.FromResult(0) + .Tap(onFulfilled, onFaulted); + + await Task.Delay(10); + + Assert.NotEqual(expectedValue, actualValue); + } +} \ No newline at end of file diff --git a/tests/unit/Tap/WithFullTaskOnFulfilledAndActionOnFaulted.cs b/tests/unit/Tap/WithFullTaskOnFulfilledAndActionOnFaulted.cs new file mode 100644 index 0000000..60d00b7 --- /dev/null +++ b/tests/unit/Tap/WithFullTaskOnFulfilledAndActionOnFaulted.cs @@ -0,0 +1,133 @@ +using System; +using System.Threading.Tasks; +using RLC.TaskChaining; +using Xunit; + +namespace RLC.TaskChainingTests.Tap; + +public class WithFullTaskOnFulfilledAndActionOnFaulted +{ + [Fact] + public async Task ItShouldPerformASideEffectOnAResolution() + { + int actualValue = 0; + int expectedValue = 5; + Func> onFulfilled = value => + { + actualValue = value; + return Task.FromResult(5); + }; + Action onFaulted = _ => { }; + + await Task.FromResult(5) + .Tap(onFulfilled, onFaulted); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectOnAResolutionWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 5; + Func> onFulfilled = value => + { + actualValue = value; + return Task.FromResult(5); + }; + Action onFaulted = _ => { }; + + _ = Task.FromResult(5) + .Tap(onFulfilled, onFaulted); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectOnAFault() + { + int actualValue = 0; + int expectedValue = 5; + Func> onFulfilled = _ => Task.FromResult(5); + Action onFaulted = _ => { actualValue = 5; }; + + try + { + await Task.FromException(new ArgumentNullException()) + .Tap(onFulfilled, onFaulted); + } + catch (ArgumentNullException) + { + } + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectOnAFaultWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 5; + Func> onFulfilled = value => Task.FromResult(5); + Action onFaulted = _ => { actualValue = 5; }; + + _ = Task.FromException(new ArgumentNullException()) + .Tap(onFulfilled, onFaulted); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectOnACancellationWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 5; + Func> func = _ => throw new TaskCanceledException(); + Func> onFulfilled = _ => + { + actualValue = 0; + return Task.FromResult(5); + }; + Action onFaulted = _ => { actualValue = 5; }; + + _ = Task.FromResult(0) + .Then(func) + .Tap(onFulfilled, onFaulted); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldNotCallOnFaultedIfOnFulfilledThrows() + { + Func> onFulfilled = _ => throw new ArgumentNullException(); + Action onFaulted = _ => { }; + + Task testTask = Task.FromResult(0) + .Tap(onFulfilled, onFaulted); + + await Assert.ThrowsAsync(() => testTask); + } + + [Fact] + public async Task ItShouldNotCallOnFaultedIfOnFulfilledThrowsWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 5; + Func> onFulfilled = _ => throw new ArgumentNullException(); + Action onFaulted = _ => { actualValue = 5; }; + + _ = Task.FromResult(0) + .Tap(onFulfilled, onFaulted); + + await Task.Delay(10); + + Assert.NotEqual(expectedValue, actualValue); + } +} \ No newline at end of file diff --git a/tests/unit/Tap/WithRawTaskOnFulfilledAndActionOnFaulted.cs b/tests/unit/Tap/WithRawTaskOnFulfilledAndActionOnFaulted.cs new file mode 100644 index 0000000..020da9e --- /dev/null +++ b/tests/unit/Tap/WithRawTaskOnFulfilledAndActionOnFaulted.cs @@ -0,0 +1,158 @@ +using System; +using System.Threading.Tasks; +using RLC.TaskChaining; +using Xunit; + +namespace RLC.TaskChainingTests.Tap; + +public class WithRawTaskOnFulfilledAndActionOnFaulted +{ + [Fact] + public async Task ItShouldPerformASideEffectOnAResolution() + { + int actualValue = 0; + int expectedValue = 5; + Func onFulfilled = i => + { + actualValue = i; + + return Task.CompletedTask; + }; + Action onFaulted = _ => { }; + + await Task.FromResult(5) + .Tap(onFulfilled, onFaulted); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectOnAResolutionWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 5; + Func onFulfilled = i => + { + actualValue = i; + + return Task.CompletedTask; + }; + Action onFaulted = _ => { }; + + _ = Task.FromResult(5) + .Tap(onFulfilled, onFaulted); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectOnAFault() + { + int actualValue = 0; + int expectedValue = 5; + Func onFulfilled = _ => Task.CompletedTask; + Action onFaulted = _ => { actualValue = 5; }; + + try + { + await Task.FromException(new ArgumentNullException()) + .Tap(onFulfilled, onFaulted); + } + catch + { + // ignored + } + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectOnAFaultWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 5; + Func onFulfilled = _ => Task.CompletedTask; + Action onFaulted = _ => { actualValue = 5; }; + + _ = Task.FromException(new ArgumentNullException()) + .Tap(onFulfilled, onFaulted); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectOnACancellation() + { + int actualValue = 0; + int expectedValue = 5; + Func> func = _ => throw new TaskCanceledException(); + Func onFulfilled = _ => + { + actualValue = 0; + return Task.CompletedTask; + }; + Action onFaulted = _ => { actualValue = 5; }; + + _ = Task.FromResult(0) + .Then(func) + .Tap(onFulfilled, onFaulted); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectOnACancellationWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 5; + Func func = _ => throw new TaskCanceledException(); + Func onFulfilled = _ => + { + actualValue = 0; + return Task.CompletedTask; + }; + Action onFaulted = _ => { actualValue = 5; }; + + _ = Task.FromResult(0) + .Then(func) + .Tap(onFulfilled, onFaulted); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldNotCallOnFaultedIfOnFulfilledThrows() + { + Func onFulfilled = _ => throw new ArgumentException(); + Action onFaulted = _ => { }; + + Task testTask = Task.FromResult(0) + .Tap(onFulfilled, onFaulted); + + await Assert.ThrowsAsync(() => testTask); + } + + [Fact] + public async Task ItShouldNotCallOnFaultedIfOnFulfilledThrowsWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 5; + Func onFulfilled = _ => throw new ArgumentException(); + Action onFaulted = _ => { actualValue = 5; }; + + _ = Task.FromResult(0) + .Tap(onFulfilled, onFaulted); + + await Task.Delay(10); + + Assert.NotEqual(expectedValue, actualValue); + } +} \ No newline at end of file diff --git a/tests/unit/Tap/WithRawTaskOnFulfilledAndFullTaskOnFaulted.cs b/tests/unit/Tap/WithRawTaskOnFulfilledAndFullTaskOnFaulted.cs new file mode 100644 index 0000000..3e06440 --- /dev/null +++ b/tests/unit/Tap/WithRawTaskOnFulfilledAndFullTaskOnFaulted.cs @@ -0,0 +1,177 @@ +using System; +using System.Threading.Tasks; +using RLC.TaskChaining; +using Xunit; + +namespace RLC.TaskChainingTests.Tap; + +public class WithRawTaskOnFulfilledAndFullTaskOnFaulted +{ + [Fact] + public async Task ItShouldPerformASideEffectOnAResolution() + { + int actualValue = 0; + int expectedValue = 5; + Func onFulfilled = i => + { + actualValue = i; + + return Task.CompletedTask; + }; + Func> onFaulted = _ => Task.FromResult(1); + + await Task.FromResult(5) + .Tap(onFulfilled, onFaulted); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectOnAResolutionWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 5; + Func onFulfilled = i => + { + actualValue = i; + + return Task.CompletedTask; + }; + Func> onFaulted = _ => Task.FromResult(1); + + _ = Task.FromResult(5) + .Tap(onFulfilled, onFaulted); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectOnAFault() + { + int actualValue = 0; + int expectedValue = 5; + Func onFulfilled = _ => Task.CompletedTask; + Func> onFaulted = _ => + { + actualValue = 5; + return Task.FromResult(1); + }; + + try + { + await Task.FromException(new ArgumentNullException()) + .Tap(onFulfilled, onFaulted); + } + catch (ArgumentNullException) + { + } + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectOnAFaultWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 5; + Func onFulfilled = i => Task.CompletedTask; + Func> onFaulted = _ => + { + actualValue = 5; + return Task.FromResult(1); + }; + + _ = Task.FromException(new ArgumentNullException()) + .Tap(onFulfilled, onFaulted); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectOnACancellation() + { + int actualValue = 0; + int expectedValue = 5; + Func> func = _ => throw new TaskCanceledException(); + Func onFulfilled = i => + { + actualValue = 0; + return Task.CompletedTask; + }; + Func> onFaulted = _ => + { + actualValue = 5; + return Task.FromResult(1); + }; + + _ = Task.FromResult(0) + .Then(func) + .Tap(onFulfilled, onFaulted); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldPerformASideEffectOnACancellationWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 5; + Func func = _ => throw new TaskCanceledException(); + Func onFulfilled = i => + { + actualValue = 0; + return Task.CompletedTask; + }; + Func> onFaulted = _ => + { + actualValue = 5; + return Task.FromResult(1); + }; + + _ = Task.FromResult(0) + .Then(func) + .Tap(onFulfilled, onFaulted); + + await Task.Delay(10); + + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public async Task ItShouldNotCallOnFaultedIfOnFulfilledThrows() + { + Func onFulfilled = _ => throw new ArgumentException(); + Func> onFaulted = _ => Task.FromResult(1); + + Task testTask = Task.FromResult(0) + .Tap(onFulfilled, onFaulted); + + await Assert.ThrowsAsync(() => testTask); + } + + [Fact] + public async Task ItShouldNotCallOnFaultedIfOnFulfilledThrowsWithoutAwaiting() + { + int actualValue = 0; + int expectedValue = 5; + Func onFulfilled = _ => throw new ArgumentException(); + Func> onFaulted = _ => + { + actualValue = 5; + return Task.FromResult(1); + }; + + _ = Task.FromResult(0) + .Tap(onFulfilled, onFaulted); + + await Task.Delay(10); + + Assert.NotEqual(expectedValue, actualValue); + } +} \ No newline at end of file diff --git a/tests/unit/TaskChainingIfFaultedTests.cs b/tests/unit/TaskChainingIfFaultedTests.cs deleted file mode 100644 index b1a2eef..0000000 --- a/tests/unit/TaskChainingIfFaultedTests.cs +++ /dev/null @@ -1,177 +0,0 @@ -using System; -using System.Threading.Tasks; - -using RLC.TaskChaining; -using Xunit; - -using static RLC.TaskChaining.TaskStatics; - -public class TaskChainingIfFaultedTests -{ - [Fact] - public async void ItShouldPerformASideEffectWithoutAwaiting() - { - int actualValue = 0; - int expectedValue = 5; - - _ = Task.FromException(new ArgumentNullException()) - .IfFaulted((Exception _) => - { - actualValue = 5; - }); - - await Task.Delay(2); - - Assert.Equal(expectedValue, actualValue); - } - - [Fact] - public async void ItShouldNotPerformASideEffectForAResolution() - { - int actualValue = 0; - int expectedValue = 0; - - await Task.FromResult(5) - .IfFaulted((Exception _) => - { - actualValue = 5; - }); - - Assert.Equal(expectedValue, actualValue); - } - - [Fact] - public async void ItShouldNotPerformASideEffectForAResolutionWithoutAwaiting() - { - int actualValue = 0; - int expectedValue = 0; - - _ = Task.FromResult(5) - .IfFaulted((Exception _) => - { - actualValue = 5; - }); - - await Task.Delay(2); - - Assert.Equal(expectedValue, actualValue); - } - - [Fact] - public async void ItShouldPerformASideEffectForACancellation() - { - int actualValue = 0; - int expectedValue = 5; - Func func = _ => throw new TaskCanceledException(); - - try - { - await Task.FromResult(0) - .Then(func) - .IfFaulted(_ => - { - actualValue = 5; - }); - } - catch (OperationCanceledException) - { - } - - Assert.Equal(expectedValue, actualValue); - } - - [Fact] - public async void ItShouldPerformASideEffectForACancellationWithoutAwaiting() - { - int actualValue = 0; - int expectedValue = 5; - Func func = _ => throw new TaskCanceledException(); - - _ = Task.FromResult(0) - .Then(func) - .IfFaulted(_ => - { - actualValue = 5; - }); - - await Task.Delay(2); - - Assert.Equal(expectedValue, actualValue); - } - - public class WithTaskFunc - { - [Fact] - public async void ItShouldPerformASideEffectWithoutAwaiting() - { - int actualValue = 0; - int expectedValue = 5; - Func> func = exception => - { - actualValue = 5; - - return Task.FromResult(Guid.NewGuid().ToString()); - }; - - _ = Task.FromException(new ArgumentNullException()) - .IfFaulted(func); - - await Task.Delay(2); - - Assert.Equal(expectedValue, actualValue); - } - - [Fact] - public async void ItShouldNotPerformASideEffectForAResolution() - { - int actualValue = 0; - int expectedValue = 0; - Func> func = exception => - { - actualValue = 5; - - return Task.FromResult(Guid.NewGuid().ToString()); - }; - - await Task.FromResult(5) - .IfFaulted(func); - - Assert.Equal(expectedValue, actualValue); - } - - [Fact] - public async void ItShouldNotChangeExceptionTypesForExceptionThrowingFunc() - { - Func> func = exception => - { - throw new InvalidOperationException(); - }; - - await Assert.ThrowsAsync(async () => await Task.FromException(new ArgumentNullException()) - .IfFaulted(func)); - } - } - - public class WithAsyncAction - { - [Fact] - public async void ItShouldContinueAsyncTasks() - { - int actualValue = 0; - int expectedValue = 5; - Func func = async exception => - { - await Task.Delay(1); - - actualValue = 5; - }; - - _ = Task.FromException(new Exception()) - .IfFaulted(func); - - await Task.Delay(5); - - Assert.Equal(expectedValue, actualValue); - } - } -} diff --git a/tests/unit/TaskChainingIfFulfilledTests.cs b/tests/unit/TaskChainingIfFulfilledTests.cs deleted file mode 100644 index ddd5f69..0000000 --- a/tests/unit/TaskChainingIfFulfilledTests.cs +++ /dev/null @@ -1,239 +0,0 @@ -using System; -using System.Threading.Tasks; - -using RLC.TaskChaining; -using Xunit; - -using static RLC.TaskChaining.TaskStatics; - -public class TaskChainingIfFulfilledTests -{ - [Fact] - public async void ItShouldPerformASideEffect() - { - int actualValue = 0; - int expectedValue = 5; - - await Task.FromResult(5) - .IfFulfilled(value => - { - actualValue = value; - }); - - Assert.Equal(expectedValue, actualValue); - } - - [Fact] - public async void ItShouldPerformASideEffectWithoutAwaiting() - { - int actualValue = 0; - int expectedValue = 5; - - _ = Task.FromResult(5) - .IfFulfilled(value => - { - actualValue = value; - }); - - await Task.Delay(2); - - Assert.Equal(expectedValue, actualValue); - } - - [Fact] - public async void ItShouldNotPerformASideEffectForAFault() - { - int actualValue = 0; - int expectedValue = 0; - - try - { - await Task.FromException(new ArgumentNullException()) - .IfFulfilled((int value) => - { - actualValue = 5; - }); - } - catch (ArgumentNullException) - { - } - - Assert.Equal(expectedValue, actualValue); - } - - [Fact] - public async void ItShouldNotPerformASideEffectForAFaultWithoutAwaiting() - { - int actualValue = 0; - int expectedValue = 0; - - _ = Task.FromException(new ArgumentNullException()) - .IfFulfilled((int _) => - { - actualValue = 5; - }); - - await Task.Delay(2); - - Assert.Equal(expectedValue, actualValue); - } - - [Fact] - public async void ItShouldNotPerformASideEffectForACancellation() - { - int actualValue = 0; - int expectedValue = 0; - Func func = _ => throw new TaskCanceledException(); - - try - { - await Task.FromResult(0) - .Then(func) - .IfFulfilled(_ => - { - actualValue = 5; - }); - } - catch (OperationCanceledException) - { - } - - Assert.Equal(expectedValue, actualValue); - } - - [Fact] - public async void ItShouldNotPerformASideEffectForACancellationWithoutAwaiting() - { - int actualValue = 0; - int expectedValue = 0; - Func func = _ => throw new TaskCanceledException(); - - _ = Task.FromResult(0) - .Then(func) - .IfFulfilled(_ => - { - actualValue = 5; - }); - - await Task.Delay(2); - - Assert.Equal(expectedValue, actualValue); - } - - public class WithTaskFunc - { - [Fact] - public async void ItShouldPerformASideEffect() - { - int actualValue = 0; - int expectedValue = 5; - Func> func = i => - { - actualValue = i; - - return Task.FromResult(i.ToString()); - }; - - await Task.FromResult(5) - .IfFulfilled(func); - - Assert.Equal(expectedValue, actualValue); - } - - [Fact] - public async void ItShouldNotPerformASideEffectForAFault() - { - int actualValue = 0; - int expectedValue = 0; - Func> func = i => - { - actualValue = i; - - return Task.FromResult(i.ToString()); - }; - - try - { - await Task.FromException(new ArgumentNullException()) - .IfFulfilled(func); - } - catch (ArgumentNullException) - { - } - - Assert.Equal(expectedValue, actualValue); - } - - [Fact] - public async void ItShouldNotResultInAFaultedTaskWithAnException() - { - Func> func = i => - { - throw new ArgumentException(); - }; - - int expectedValue = 5; - int actualValue = await Task.FromResult(5) - .IfFulfilled(func); - - Assert.Equal(expectedValue, actualValue); - } - } - - public class WithAsyncAction - { - [Fact] - public async void ItShouldContinueAsyncTasks() - { - string testValue = "12345"; - int expectedValue = 5; - int actualValue = 0; - Func func = async value => - { - await Task.Delay(1); - - actualValue = value.Length; - }; - - _ = Task.FromResult(testValue) - .IfFulfilled(func); - - await Task.Delay(5); - - Assert.Equal(expectedValue, actualValue); - } - - [Fact] - public async Task ItShouldAwaitAsyncSideEffects() - { - int coin = 5; - int testValue = 12345; - int expectedSideEffectValue = 12345; - string expectedValue = "12345"; - int? sideEffectIntValue = null; - string? sideEffectStringValue = null; - Func func = value => - { - return Task.FromResult(value) - .Delay(TimeSpan.FromSeconds(1)) - .Then(int.Parse) - .Then(v => - { - sideEffectIntValue = v; - return v; - }); - }; - - string actualValue = await Task.FromResult(testValue) - .Then(val => val.ToString()) - .IfFulfilled(value => coin < 6 - ? func(value) - : Task.FromResult(value) - ); - - Assert.Equal(expectedValue, actualValue); - Assert.Equal(expectedSideEffectValue, sideEffectIntValue); - Assert.Null(sideEffectStringValue); - } - } -} diff --git a/tests/unit/TaskChainingTapTests.cs b/tests/unit/TaskChainingTapTests.cs deleted file mode 100644 index d44ff45..0000000 --- a/tests/unit/TaskChainingTapTests.cs +++ /dev/null @@ -1,196 +0,0 @@ -using System; -using System.Threading.Tasks; - -using RLC.TaskChaining; -using Xunit; - -public class TaskChainingTapTests -{ - [Fact] - public async void ItShouldPerformASideEffectOnAResolution() - { - int actualValue = 0; - int expectedValue = 5; - - await Task.FromResult(5) - .Tap(value => - { - actualValue = value; - }, - _ => { } - ); - - Assert.Equal(expectedValue, actualValue); - } - - [Fact] - public async void ItShouldPerformASideEffectOnAResolutionWithoutAwaiting() - { - int actualValue = 0; - int expectedValue = 5; - - _ = Task.FromResult(5) - .Tap(value => - { - actualValue = value; - }, - _ => { } - ); - - await Task.Delay(10); - - Assert.Equal(expectedValue, actualValue); - } - - [Fact] - public async void ItShouldPerformASideEffectOnAFaultWithoutAwaiting() - { - int actualValue = 0; - int expectedValue = 5; - - _ = Task.FromException(new ArgumentNullException()) - .Tap( - _ => { }, - _ => - { - actualValue = 5; - } - ); - - await Task.Delay(10); - - Assert.Equal(expectedValue, actualValue); - } - - [Fact] - public async void ItShouldPerformASideEffectOnACancellationWithoutAwaiting() - { - int actualValue = 0; - int expectedValue = 5; - Func func = i => - { - throw new TaskCanceledException(); - }; - - _ = Task.FromResult(0) - .Then(func) - .Tap( - i => { actualValue = 0; }, - ex => { actualValue = 5; } - ); - - await Task.Delay(10); - - Assert.Equal(expectedValue, actualValue); - } - - public class WithTaskReturningFunc - { - [Fact] - public async void ItShouldPerformASideEffectOnAResolution() - { - int actualValue = 0; - int expectedValue = 5; - Func> func = i => - { - actualValue = i; - - return Task.FromResult(i.ToString()); - }; - - await Task.FromResult(5) - .Tap(func, _ => { }); - - Assert.Equal(expectedValue, actualValue); - } - - [Fact] - public async void ItShouldPerformASideEffectOnACancellation() - { - int actualValue = 0; - int expectedValue = 5; - Func> func = i => - { - throw new TaskCanceledException(); - }; - - _ = Task.FromResult(0) - .Then(func) - .Tap( - i => { actualValue = 0; }, - ex => { actualValue = 5; } - ); - - await Task.Delay(10); - - Assert.Equal(expectedValue, actualValue); - } - } - - public class WithAsyncAction - { - [Fact] - public async void ItShouldPerformASideEffectOnAResolution() - { - int actualValue = 0; - int expectedValue = 5; - Func func = async i => - { - await Task.Delay(1); - - actualValue = i; - }; - - _ = Task.FromResult(5) - .Tap(func, ex => Task.FromException(ex)); - - await Task.Delay(10); - - Assert.Equal(expectedValue, actualValue); - } - - [Fact] - public async void ItShouldPerformASideEffectOnAFault() - { - int actualValue = 0; - int expectedValue = 5; - Func func = async i => - { - await Task.Delay(1); - - actualValue = 5; - }; - - _ = Task.FromException(new Exception()) - .Tap(i => Task.FromResult(i), func); - - await Task.Delay(10); - - Assert.Equal(expectedValue, actualValue); - } - - [Fact] - public async void ItShouldPerformASideEffectOnACancellation() - { - int actualValue = 0; - int expectedValue = 5; - Func> func = async i => - { - await Task.Delay(1); - - throw new TaskCanceledException(); - }; - - _ = Task.FromResult(0) - .Then(func) - .Tap( - i => { actualValue = 0; }, - ex => { actualValue = 5; } - ); - - await Task.Delay(10); - - Assert.Equal(expectedValue, actualValue); - } - } -}