-
Notifications
You must be signed in to change notification settings - Fork 219
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add AsyncTaskCodeActivity and VoidResult * Added tests for AsyncTaskCodeActivity, removed ifdef for .NET 5 or older * Add VoidResultTests * Revert TerminateWorkflowTests changes, Remove IAsyncTaskCodeActivity, Remove VoidResult in favor of object * Revert Assembly Attribute, Add non-generic test * Remove comment about VoidResult type * add Yield * code reuse through a base class * cosmetic * error case * use ApmAsyncFactory.ToBegin * cancellation test Co-authored-by: Lucian Bargaoanu <[email protected]>
- Loading branch information
1 parent
d675597
commit 411c9e2
Showing
3 changed files
with
142 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
using Nito.AsyncEx.Interop; | ||
using System.ComponentModel; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
namespace System.Activities | ||
{ | ||
public abstract class AsyncTaskCodeActivity : TaskCodeActivity<object> | ||
{ | ||
public abstract Task ExecuteAsync(AsyncCodeActivityContext context, CancellationToken cancellationToken); | ||
internal sealed override async Task<object> ExecuteAsyncCore(AsyncCodeActivityContext context, CancellationToken cancellationToken) | ||
{ | ||
await ExecuteAsync(context, cancellationToken); | ||
return null; | ||
} | ||
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] | ||
[Browsable(false)] | ||
public new OutArgument<object> Result { get; } | ||
} | ||
public abstract class AsyncTaskCodeActivity<TResult> : TaskCodeActivity<TResult> | ||
{ | ||
public abstract Task<TResult> ExecuteAsync(AsyncCodeActivityContext context, CancellationToken cancellationToken); | ||
internal sealed override Task<TResult> ExecuteAsyncCore(AsyncCodeActivityContext context, CancellationToken cancellationToken) => | ||
ExecuteAsync(context, cancellationToken); | ||
} | ||
public abstract class TaskCodeActivity<TResult> : AsyncCodeActivity<TResult> | ||
{ | ||
protected sealed override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state) | ||
{ | ||
var cts = new CancellationTokenSource(); | ||
context.UserState = cts; | ||
|
||
var task = ExecuteAsyncCore(context, cts.Token); | ||
|
||
return ApmAsyncFactory.ToBegin(task, callback, state); | ||
} | ||
protected sealed override TResult EndExecute(AsyncCodeActivityContext context, IAsyncResult result) | ||
{ | ||
using ((CancellationTokenSource)context.UserState) | ||
{ | ||
return ((Task<TResult>)result).Result; | ||
} | ||
} | ||
protected sealed override void Cancel(AsyncCodeActivityContext context) => ((CancellationTokenSource)context.UserState).Cancel(); | ||
internal abstract Task<TResult> ExecuteAsyncCore(AsyncCodeActivityContext context, CancellationToken cancellationToken); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
95 changes: 95 additions & 0 deletions
95
src/Test/TestCases.Activities/AsyncTaskCodeActivityTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
using Shouldly; | ||
using System; | ||
using System.Activities; | ||
using System.IO; | ||
using System.Text; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Xunit; | ||
|
||
namespace TestCases.Activities | ||
{ | ||
public sealed class AsyncTaskCodeActivityTests | ||
{ | ||
[Fact] | ||
public void ShouldReturnVoidResult() | ||
{ | ||
var genericActivity = new AsyncTaskActivity<object>(_=>Task.FromResult<object>(null)); | ||
|
||
object vr1 = null; | ||
object vr2 = WorkflowInvoker.Invoke<object>(genericActivity); | ||
|
||
Assert.Equal(vr1, vr2); | ||
} | ||
[Fact] | ||
public void ShouldThrow() | ||
{ | ||
Activity activity = new AsyncTaskActivity(_ => Task.FromException(new InvalidOperationException("@"))); | ||
new Action(() => WorkflowInvoker.Invoke(activity)).ShouldThrow<InvalidOperationException>().Message.ShouldBe("@"); | ||
} | ||
[Fact] | ||
public async Task ShouldCancel() | ||
{ | ||
Activity activity = new AsyncTaskActivity(token=> Task.Delay(Timeout.Infinite, token)); | ||
var invoker = new WorkflowInvoker(activity); | ||
var taskCompletionSource = new TaskCompletionSource(); | ||
InvokeCompletedEventArgs args = null; | ||
invoker.InvokeCompleted += (sender, localArgs) => | ||
{ | ||
args = localArgs; | ||
taskCompletionSource.SetResult(); | ||
}; | ||
invoker.InvokeAsync(invoker); | ||
invoker.CancelAsync(invoker); | ||
await taskCompletionSource.Task; | ||
args.Error.ShouldBeOfType<WorkflowApplicationAbortedException>(); | ||
} | ||
[Theory] | ||
[InlineData(1)] | ||
[InlineData(2)] | ||
[InlineData(3)] | ||
public void ShouldReturnConstantResult(int value) | ||
{ | ||
var activity = new AsyncTaskActivity<int>(async _=> | ||
{ | ||
await Task.Yield(); | ||
return value; | ||
}); | ||
var result = WorkflowInvoker.Invoke(activity); | ||
|
||
Assert.Equal(value, result); | ||
} | ||
[Fact] | ||
public void ShouldWriteCorrectString() | ||
{ | ||
const string stringToWrite = "Hello, World!"; | ||
|
||
using var memory = new MemoryStream(); | ||
|
||
Activity activity = new AsyncTaskActivity(async _ => | ||
{ | ||
using var writer = new StreamWriter(memory); | ||
writer.Write(stringToWrite); | ||
writer.Flush(); | ||
}); | ||
|
||
_ = WorkflowInvoker.Invoke(activity); | ||
|
||
byte[] buffer = memory.ToArray(); | ||
|
||
Assert.Equal(stringToWrite, Encoding.UTF8.GetString(buffer, 0, buffer.Length)); | ||
} | ||
} | ||
public class AsyncTaskActivity : AsyncTaskCodeActivity | ||
{ | ||
private readonly Func<CancellationToken, Task> _action; | ||
public AsyncTaskActivity(Func<CancellationToken, Task> action) => _action = action; | ||
public override Task ExecuteAsync(AsyncCodeActivityContext context, CancellationToken cancellationToken) => _action(cancellationToken); | ||
} | ||
public class AsyncTaskActivity<TResult> : AsyncTaskCodeActivity<TResult> | ||
{ | ||
private readonly Func<CancellationToken, Task<TResult>> _action; | ||
public AsyncTaskActivity(Func<CancellationToken, Task<TResult>> action) => _action = action; | ||
public override Task<TResult> ExecuteAsync(AsyncCodeActivityContext context, CancellationToken cancellationToken) => _action(cancellationToken); | ||
} | ||
} |