using System; using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using AMWD.Common.Utilities; namespace AMWD.Common.Tests.Utilities { [TestClass] public class DelayedTaskTest { [TestMethod] public void ShouldCreateNewDelayedTaskNotStarting() { // arrange int executionCount = 0; var delay = TimeSpan.FromMilliseconds(100); void Action() { executionCount++; } // act var cts = new CancellationTokenSource(delay.Add(TimeSpan.FromSeconds(1))); var delayedTask = DelayedTask.Create(Action, delay); SpinWait.SpinUntil(() => executionCount > 0 || cts.IsCancellationRequested); // assert Assert.IsNotNull(delayedTask); Assert.AreEqual(delay, delayedTask.Delay); Assert.IsFalse(delayedTask.IsRunning); Assert.IsFalse(delayedTask.IsWaitingToRun); Assert.IsNull(delayedTask.Exception); Assert.AreEqual(0, executionCount); } [TestMethod] public void ShouldCreateNewDelayedTaskStarting() { // arrange int executionCount = 0; var delay = TimeSpan.FromMilliseconds(100); void Action() { executionCount++; } // act var cts = new CancellationTokenSource(delay.Add(TimeSpan.FromSeconds(1))); var delayedTask = DelayedTask.Run(Action, delay); SpinWait.SpinUntil(() => executionCount > 0 || cts.IsCancellationRequested); // assert Assert.IsNotNull(delayedTask); Assert.AreEqual(delay, delayedTask.Delay); Assert.IsFalse(delayedTask.IsRunning); Assert.IsFalse(delayedTask.IsWaitingToRun); Assert.IsNull(delayedTask.Exception); Assert.AreEqual(1, executionCount); } [TestMethod] public void ShouldRunOnceAfterReset() { // arrange int executionCount = 0; var delay = TimeSpan.FromMilliseconds(100); void Action() { executionCount++; } // act var cts = new CancellationTokenSource(delay.Add(TimeSpan.FromSeconds(1))); var delayedTask = DelayedTask.Create(Action, delay); delayedTask.Reset(); SpinWait.SpinUntil(() => executionCount > 0 || cts.IsCancellationRequested); // assert Assert.IsNotNull(delayedTask); Assert.AreEqual(delay, delayedTask.Delay); Assert.IsFalse(delayedTask.IsRunning); Assert.IsFalse(delayedTask.IsWaitingToRun); Assert.IsNull(delayedTask.Exception); Assert.AreEqual(1, executionCount); } [TestMethod] public void ShouldCancelWaitingTask() { // arrange int executionCount = 0; var delay = TimeSpan.FromMilliseconds(100); void Action() { executionCount++; } // act var cts = new CancellationTokenSource(delay.Add(TimeSpan.FromSeconds(1))); var delayedTask = DelayedTask.Run(Action, delay); delayedTask.Cancel(); SpinWait.SpinUntil(() => executionCount > 0 || cts.IsCancellationRequested); // assert Assert.IsNotNull(delayedTask); Assert.AreEqual(delay, delayedTask.Delay); Assert.IsFalse(delayedTask.IsRunning); Assert.IsFalse(delayedTask.IsWaitingToRun); Assert.IsNull(delayedTask.Exception); Assert.AreEqual(0, executionCount); } [TestMethod] public async Task ShouldResetRunningDelay() { // arrange int executionCount = 0; var sw = new Stopwatch(); var delay = TimeSpan.FromMilliseconds(200); void Action() { sw.Stop(); executionCount++; } // act var cts = new CancellationTokenSource(delay.Add(TimeSpan.FromSeconds(1))); sw.Start(); var delayedTask = DelayedTask.Run(Action, delay); await Task.Delay(50); delayedTask.Reset(); SpinWait.SpinUntil(() => executionCount > 0 || cts.IsCancellationRequested); // assert Assert.IsNotNull(delayedTask); Assert.AreEqual(delay, delayedTask.Delay); Assert.IsFalse(delayedTask.IsRunning); Assert.IsFalse(delayedTask.IsWaitingToRun); Assert.IsNull(delayedTask.Exception); Assert.AreEqual(1, executionCount); // delta of 60ms as the precision below 50ms is really bad for System.Timer Assert.AreEqual(250, sw.ElapsedMilliseconds, 60); } [TestMethod] public async Task ShouldNotExecutedImmediateOnCreated() { // arrange int executionCount = 0; var sw = new Stopwatch(); var delay = TimeSpan.FromMilliseconds(200); void Action() { sw.Stop(); executionCount++; } // act var cts = new CancellationTokenSource(delay.Add(TimeSpan.FromSeconds(1))); sw.Start(); var delayedTask = DelayedTask.Create(Action, delay); await Task.Delay(50); bool isSuccess = delayedTask.ExecutePending(); SpinWait.SpinUntil(() => executionCount > 0 || cts.IsCancellationRequested); sw.Stop(); // assert Assert.IsNotNull(delayedTask); Assert.AreEqual(delay, delayedTask.Delay); Assert.IsFalse(delayedTask.IsRunning); Assert.IsFalse(delayedTask.IsWaitingToRun); Assert.IsNull(delayedTask.Exception); Assert.AreEqual(0, executionCount); // delta of 60ms as the precision below 50ms is really bad for System.Timer Assert.AreEqual(1250, sw.ElapsedMilliseconds, 60); Assert.IsFalse(isSuccess); } [TestMethod] public async Task ShouldExecuteImmediateOnExecutePendingWhenRunning() { // arrange int executionCount = 0; var sw = new Stopwatch(); var delay = TimeSpan.FromMilliseconds(200); void Action() { sw.Stop(); executionCount++; } // act var cts = new CancellationTokenSource(delay.Add(TimeSpan.FromSeconds(1))); sw.Start(); var delayedTask = DelayedTask.Run(Action, delay); await Task.Delay(50); bool isSuccess = delayedTask.ExecutePending(); SpinWait.SpinUntil(() => executionCount > 0 || cts.IsCancellationRequested); // assert Assert.IsNotNull(delayedTask); Assert.AreEqual(delay, delayedTask.Delay); Assert.IsFalse(delayedTask.IsRunning); Assert.IsFalse(delayedTask.IsWaitingToRun); Assert.IsNull(delayedTask.Exception); Assert.AreEqual(1, executionCount); // delta of 60ms as the precision below 50ms is really bad for System.Timer Assert.AreEqual(50, sw.ElapsedMilliseconds, 60); Assert.IsTrue(isSuccess); } [TestMethod] public void ShouldReturnAnAwaiter() { // arrange int executionCount = 0; var delay = TimeSpan.FromMilliseconds(100); void Action() { executionCount++; } var delayedTask = DelayedTask.Create(Action, delay); // act delayedTask.Reset(); var awaiter = delayedTask.GetAwaiter(); SpinWait.SpinUntil(() => awaiter.IsCompleted); // assert Assert.IsNotNull(delayedTask); Assert.IsNotNull(awaiter); Assert.IsFalse(delayedTask.IsRunning); Assert.IsFalse(delayedTask.IsWaitingToRun); Assert.IsNull(delayedTask.Exception); Assert.AreEqual(1, executionCount); } [TestMethod] public void ShouldHaveAnExceptionSet() { // arrange var delay = TimeSpan.FromMilliseconds(100); static void Action() { throw new Exception("TEST :D"); } // act var delayedTask = DelayedTask.Run(Action, delay); var awaiter = delayedTask.GetAwaiter(); SpinWait.SpinUntil(() => awaiter.IsCompleted); // assert Assert.IsNotNull(delayedTask); Assert.IsFalse(delayedTask.IsRunning); Assert.IsFalse(delayedTask.IsWaitingToRun); Assert.IsNotNull(delayedTask.Exception); } [TestMethod] public void ShouldUseExceptionHandler() { // arrange string exceptionText = null; var delay = TimeSpan.FromMilliseconds(100); void Action() { throw new Exception("TEST :D"); } void ExceptionHandler(Exception ex) { exceptionText = ex.Message; } // act var delayedTask = DelayedTask.Run(Action, delay) .WithExceptionHandler(ExceptionHandler); var awaiter = delayedTask.GetAwaiter(); SpinWait.SpinUntil(() => awaiter.IsCompleted); // assert Assert.IsNotNull(delayedTask); Assert.IsFalse(delayedTask.IsRunning); Assert.IsFalse(delayedTask.IsWaitingToRun); Assert.IsNotNull(delayedTask.Exception); Assert.AreEqual("TEST :D", exceptionText); } [TestMethod] public async Task ShouldReturnNormalTask() { // arrange int executionCount = 0; var delay = TimeSpan.FromMilliseconds(100); void Action() { executionCount++; } var delayedTask = DelayedTask.Create(Action, delay); // act delayedTask.Reset(); var task = delayedTask.Task; await task; // assert Assert.IsNotNull(delayedTask); Assert.IsNotNull(task); Assert.IsInstanceOfType(task, typeof(Task)); Assert.IsFalse(delayedTask.IsRunning); Assert.IsFalse(delayedTask.IsWaitingToRun); Assert.AreEqual(1, executionCount); Assert.AreEqual(TaskStatus.RanToCompletion, task.Status); } } }