File Browser: Adding async task exception handling (#504)

* Replacing Dictionary with ConcurrentDictionary since values are accessed in async contexts

* Adding new method to allow async tasks to be executed in the exception continuation

* Adding unit tests for the aforementioned

* Adding exception handling to async tasks in file browser service

* Updating query execution async handling to use the async version

* Removing unnecesary send result from continuewithonfaulted
This commit is contained in:
Benjamin Russell
2017-10-19 11:25:29 -07:00
committed by GitHub
parent 4b66203dfc
commit 9600125186
5 changed files with 220 additions and 107 deletions

View File

@@ -12,14 +12,16 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Utility
{
public class TaskExtensionTests
{
#region Continue with Action
[Fact]
public async Task ContinueWithOnFaultedNullContinuation()
public async Task ContinueWithOnFaultedActionNullContinuation()
{
// Setup: Create a task that will definitely fault
Task failureTask = new Task(() => throw new Exception("It fail!"));
Task failureTask = new Task(() => { throw new Exception("It fail!"); });
// If: I continue on fault and start the task
Task continuationTask = failureTask.ContinueWithOnFaulted(null);
Task continuationTask = failureTask.ContinueWithOnFaulted((Action<Task>)null);
failureTask.Start();
await continuationTask;
@@ -28,11 +30,11 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Utility
}
[Fact]
public async Task ContinueWithOnFaultedContinuatation()
public async Task ContinueWithOnFaultedActionContinuatation()
{
// Setup:
// ... Create a new task that will definitely fault
Task failureTask = new Task(() => throw new Exception("It fail!"));
Task failureTask = new Task(() => { throw new Exception("It fail!"); });
// ... Create a quick continuation task that will signify if it's been called
Task providedTask = null;
@@ -49,5 +51,111 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Utility
// ... The continuation action should have been called with the original failure task
Assert.Equal(failureTask, providedTask);
}
[Fact]
public async Task ContinueWithOnFaultedActionExceptionInContinuation()
{
// Setup:
// ... Create a new task that will definitely fault
Task failureTask = new Task(() => { throw new Exception("It fail!"); });
// ... Create a quick continuation task that will signify if it's been called
Task providedTask = null;
// If: I continue on fault, with a continuation task that will fail
Action<Task> failureContinuation = task =>
{
providedTask = task;
throw new Exception("It fail!");
};
Task continuationTask = failureTask.ContinueWithOnFaulted(failureContinuation);
failureTask.Start();
await continuationTask;
// Then:
// ... The task should have completed without fault
Assert.Equal(TaskStatus.RanToCompletion, continuationTask.Status);
// ... The continuation action should have been called with the original failure task
Assert.Equal(failureTask, providedTask);
}
#endregion
#region Continue with Task
[Fact]
public async Task ContinueWithOnFaultedFuncNullContinuation()
{
// Setup: Create a task that will definitely fault
Task failureTask = new Task(() => { throw new Exception("It fail!"); });
// If: I continue on fault and start the task
// ReSharper disable once RedundantCast -- Just to enforce we're running the right overload
Task continuationTask = failureTask.ContinueWithOnFaulted((Func<Task, Task>)null);
failureTask.Start();
await continuationTask;
// Then: The task should have completed without fault
Assert.Equal(TaskStatus.RanToCompletion, continuationTask.Status);
}
[Fact]
public async Task ContinueWithOnFaultedFuncContinuatation()
{
// Setup:
// ... Create a new task that will definitely fault
Task failureTask = new Task(() => { throw new Exception("It fail!"); });
// ... Create a quick continuation task that will signify if it's been called
Task providedTask = null;
// If: I continue on fault, with a continuation task
Func<Task, Task> continuationFunc = task =>
{
providedTask = task;
return Task.CompletedTask;
};
Task continuationTask = failureTask.ContinueWithOnFaulted(continuationFunc);
failureTask.Start();
await continuationTask;
// Then:
// ... The task should have completed without fault
Assert.Equal(TaskStatus.RanToCompletion, continuationTask.Status);
// ... The continuation action should have been called with the original failure task
Assert.Equal(failureTask, providedTask);
}
[Fact]
public async Task ContinueWithOnFaultedFuncExceptionInContinuation()
{
// Setup:
// ... Create a new task that will definitely fault
Task failureTask = new Task(() => { throw new Exception("It fail!"); });
// ... Create a quick continuation task that will signify if it's been called
Task providedTask = null;
// If: I continue on fault, with a continuation task that will fail
Func<Task, Task> failureContinuation = task =>
{
providedTask = task;
throw new Exception("It fail!");
};
Task continuationTask = failureTask.ContinueWithOnFaulted(failureContinuation);
failureTask.Start();
await continuationTask;
// Then:
// ... The task should have completed without fault
Assert.Equal(TaskStatus.RanToCompletion, continuationTask.Status);
// ... The continuation action should have been called with the original failure task
Assert.Equal(failureTask, providedTask);
}
#endregion
}
}