using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using UnitTestSharp; namespace Foundations.UnitTests { public class WorkSchedulerTests : UnitTestSharp.TestFixture { public void RunsAction() { var scheduler = new WorkScheduler(); bool ran = false; Task task = scheduler.BeginInvoke(() => ran = true); CheckTrue(scheduler.Process(millisecondsTimeout: 0)); CheckTrue(ran); CheckTrue(task.IsCompleted); } public void DoesNotRunActionIfNotProcessed() { var scheduler = new WorkScheduler(); bool ran = false; Task task = scheduler.BeginInvoke(() => ran = true); CheckFalse(ran); CheckFalse(task.IsCompleted); } public void RunsFunction() { var scheduler = new WorkScheduler(); Task task = scheduler.BeginInvoke(() => 42); CheckTrue(scheduler.Process(millisecondsTimeout: 0)); CheckTrue(task.IsCompleted); CheckEqual(42, task.Result); } public void DoesNotRunFunctionIfNotProcessed() { var scheduler = new WorkScheduler(); Task task = scheduler.BeginInvoke(() => 42); CheckTrue(scheduler.Process(millisecondsTimeout: 0)); CheckTrue(task.IsCompleted); CheckEqual(42, task.Result); } public void ProcessReturnsFalseIfTimedOut() { var scheduler = new WorkScheduler(); CheckFalse(scheduler.Process(millisecondsTimeout: 0)); } public void AsyncActionDoesNotExecuteBeforeProcess() { var scheduler = new WorkScheduler(); int value = 42; TaskCompletionSource source = new TaskCompletionSource(); Task task = scheduler.BeginInvoke(async () => { value = 10; await source.Task; value = 20; }); CheckEqual(42, value); CheckFalse(task.IsCompleted); } public void AsyncActionExecutesUpToAwait() { var scheduler = new WorkScheduler(); int value = 42; TaskCompletionSource source = new TaskCompletionSource(); Task task = scheduler.BeginInvoke(async () => { value = 10; await source.Task; value = 20; }); CheckTrue(scheduler.Process(millisecondsTimeout: 0)); CheckEqual(10, value); CheckFalse(task.IsCompleted); } public void AsyncActionExecutesPastAwaitIfPossible() { var scheduler = new WorkScheduler(); int value = 42; TaskCompletionSource source = new TaskCompletionSource(); source.SetResult(true); Task task = scheduler.BeginInvoke(async () => { value = 10; await source.Task; value = 20; }); CheckTrue(scheduler.Process(millisecondsTimeout: 0)); CheckEqual(20, value); CheckTrue(task.IsCompleted); } public void AsyncActionContinuesAfterAwaitNextProcess() { var scheduler = new WorkScheduler(); int value = 42; TaskCompletionSource source = new TaskCompletionSource(); Task task = scheduler.BeginInvoke(async () => { value = 10; await source.Task; value = 20; }); scheduler.Process(millisecondsTimeout: 0); source.SetResult(true); CheckTrue(scheduler.Process(millisecondsTimeout: 0)); CheckEqual(20, value); CheckTrue(task.IsCompleted); } public void AsyncFunctionDoesNotExecuteBeforeProcess() { var scheduler = new WorkScheduler(); int value = 42; TaskCompletionSource source = new TaskCompletionSource(); Task task = scheduler.BeginInvoke(async () => { value = 10; await source.Task; value = 20; return 30; }); CheckEqual(42, value); CheckFalse(task.IsCompleted); } public void AsyncFunctionExecutesUpToAwait() { var scheduler = new WorkScheduler(); int value = 42; TaskCompletionSource source = new TaskCompletionSource(); Task task = scheduler.BeginInvoke(async () => { value = 10; await source.Task; value = 20; return 30; }); CheckTrue(scheduler.Process(millisecondsTimeout: 0)); CheckEqual(10, value); CheckFalse(task.IsCompleted); } public void AsyncFunctionExecutesPastAwaitIfPossible() { var scheduler = new WorkScheduler(); int value = 42; TaskCompletionSource source = new TaskCompletionSource(); source.SetResult(true); Task task = scheduler.BeginInvoke(async () => { value = 10; await source.Task; value = 20; return 30; }); CheckTrue(scheduler.Process(millisecondsTimeout: 0)); CheckEqual(20, value); CheckTrue(task.IsCompleted); CheckEqual(30, task.Result); } public void AsyncFunctionContinuesAfterAwaitNextProcess() { var scheduler = new WorkScheduler(); int value = 42; TaskCompletionSource source = new TaskCompletionSource(); Task task = scheduler.BeginInvoke(async () => { value = 10; await source.Task; value = 20; return 30; }); scheduler.Process(millisecondsTimeout: 0); source.SetResult(true); CheckTrue(scheduler.Process(millisecondsTimeout: 0)); CheckEqual(20, value); CheckTrue(task.IsCompleted); CheckEqual(30, task.Result); } public class CustomException : Exception { } public void ExceptionsAreSetOnActionTask() { var exception = new CustomException(); var scheduler = new WorkScheduler(); Task task = scheduler.BeginInvoke(() => { throw exception; }); CheckTrue(scheduler.Process(millisecondsTimeout: 0)); CheckThrow(typeof(CustomException)); task.GetAwaiter().GetResult(); } public void ExceptionsAreSetOnFunctionTask() { var exception = new CustomException(); var scheduler = new WorkScheduler(); Func function = () => { throw exception; }; Task task = scheduler.BeginInvoke(function); CheckTrue(scheduler.Process(millisecondsTimeout: 0)); CheckThrow(typeof(CustomException)); task.GetAwaiter().GetResult(); } public void ExceptionsAreNotSetOnActionTaskBeforeAwait() { var exception = new CustomException(); var scheduler = new WorkScheduler(); TaskCompletionSource source = new TaskCompletionSource(); Task task = scheduler.BeginInvoke(async () => { await source.Task; throw exception; }); CheckTrue(scheduler.Process(millisecondsTimeout: 0)); CheckFalse(task.IsCompleted); CheckFalse(task.IsFaulted); } public void ExceptionsAreSetOnActionTaskAfterAwait() { var exception = new CustomException(); var scheduler = new WorkScheduler(); TaskCompletionSource source = new TaskCompletionSource(); Task task = scheduler.BeginInvoke(async () => { await source.Task; throw exception; }); source.SetResult(true); scheduler.Process(millisecondsTimeout: 0); CheckTrue(task.IsCompleted); CheckThrow(typeof(CustomException)); task.GetAwaiter().GetResult(); } public void ExceptionsAreNotSetOnFunctionTaskBeforeAwait() { var exception = new CustomException(); var scheduler = new WorkScheduler(); TaskCompletionSource source = new TaskCompletionSource(); Func> asyncFunction = async () => { await source.Task; throw exception; }; Task task = scheduler.BeginInvoke(asyncFunction); CheckTrue(scheduler.Process(millisecondsTimeout: 0)); CheckFalse(task.IsCompleted); CheckFalse(task.IsFaulted); } public void ExceptionsAreSetOnFunctionTaskAfterAwait() { var exception = new CustomException(); var scheduler = new WorkScheduler(); TaskCompletionSource source = new TaskCompletionSource(); Func> asyncFunction = async () => { await source.Task; throw exception; }; Task task = scheduler.BeginInvoke(asyncFunction); source.SetResult(true); CheckTrue(scheduler.Process(millisecondsTimeout: 0)); CheckTrue(task.IsCompleted); CheckTrue(task.IsFaulted); CheckThrow(typeof(CustomException)); task.GetAwaiter().GetResult(); } public void PrecancelledCancellationTokenExitsImmediately() { var scheduler = new WorkScheduler(); bool ran = false; Task task = scheduler.BeginInvoke(() => ran = true); var cancellationSource = new CancellationTokenSource(); cancellationSource.Cancel(); CheckFalse(scheduler.Process(millisecondsTimeout: -1, cancellationToken: cancellationSource.Token)); CheckFalse(ran); CheckFalse(task.IsCompleted); } public void ProcessCallDoesNotBlockAfterCancel() { var scheduler = new WorkScheduler(); var cancellationSource = new CancellationTokenSource(); // A dedicated thread produces a faster test than using CancellationTokenSource's inbuilt timer for some // reason. var thread = new Thread(() => { Thread.Sleep(1); cancellationSource.Cancel(); }); thread.Start(); CheckFalse(scheduler.Process(millisecondsTimeout: -1, cancellationToken: cancellationSource.Token)); thread.Join(); } public void AsyncActionDisallowedIfInThrowMode() { var scheduler = new WorkScheduler(WorkScheduler.NestedAsyncBehaviour.Throw); TaskCompletionSource source = new TaskCompletionSource(); CheckThrow(typeof(InvalidOperationException)); scheduler.BeginInvoke(async () => { await source.Task; }); } public void AsyncFuncDisallowedIfInThrowMode() { var scheduler = new WorkScheduler(WorkScheduler.NestedAsyncBehaviour.Throw); TaskCompletionSource source = new TaskCompletionSource(); CheckThrow(typeof(InvalidOperationException)); scheduler.BeginInvoke(async () => { await source.Task; return 5; }); } public void ActionAllowedIfInThrowMode() { var scheduler = new WorkScheduler(WorkScheduler.NestedAsyncBehaviour.Throw); bool? done = null; scheduler.BeginInvoke(() => { done = true; } ); scheduler.Process(); CheckEqual(true, done); } public void FuncAllowedIfInThrowMode() { var scheduler = new WorkScheduler(WorkScheduler.NestedAsyncBehaviour.Throw); Task task = scheduler.BeginInvoke(() => 5); scheduler.Process(); CheckEqual(5, task.Result); } public class AsyncClassHelper { public int AsyncStage { get; set; } public TaskCompletionSource Source = new TaskCompletionSource(); public async Task AsyncActionMethod() { AsyncStage = 10; await Source.Task; AsyncStage = 20; } public async Task AsyncFuncMethod() { AsyncStage = 10; await Source.Task; AsyncStage = 20; return 20; } public void ActionMethod() { AsyncStage = 20; } public int FuncMethod() { return 20; } } public void AsyncActionMethodDisallowedIfInThrowMode() { var scheduler = new WorkScheduler(WorkScheduler.NestedAsyncBehaviour.Throw); var helper = new AsyncClassHelper(); CheckThrow(typeof(InvalidOperationException)); scheduler.BeginInvoke((System.Func)helper.AsyncActionMethod); } public void AsyncFuncMethodDisallowedIfInThrowMode() { var scheduler = new WorkScheduler(WorkScheduler.NestedAsyncBehaviour.Throw); var helper = new AsyncClassHelper(); CheckThrow(typeof(InvalidOperationException)); scheduler.BeginInvoke((System.Func>)helper.AsyncFuncMethod); } public void ActionMethodAllowedIfInThrowMode() { var scheduler = new WorkScheduler(WorkScheduler.NestedAsyncBehaviour.Throw); var helper = new AsyncClassHelper(); scheduler.BeginInvoke((System.Action)helper.ActionMethod); } public void FuncMethodAllowedIfInThrowMode() { var scheduler = new WorkScheduler(WorkScheduler.NestedAsyncBehaviour.Throw); var helper = new AsyncClassHelper(); scheduler.BeginInvoke((System.Func)helper.FuncMethod); } } }