Skip to content

Instantly share code, notes, and snippets.

@loudej
Forked from panesofglass/OwinDraft2.fs
Created December 28, 2010 06:22
Show Gist options
  • Save loudej/756952 to your computer and use it in GitHub Desktop.
Save loudej/756952 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using NUnit.Framework;
namespace FuncGist {
// signature exposed by middleware and app
using FnApp = Action<
IDictionary<string, object> /* env */,
Action<Exception> /* fault(ex) */,
Action<int, IDictionary<string, string>, IEnumerable<object>> /* result(status, headers, body) */ >;
// signature that can be used by host or middleware callers if they choose wrap with AsTask() adapter
using FnAppTask = Func<
IDictionary<string, object> /* env */,
Task<Tuple<int, IDictionary<string, string>, IEnumerable<object>>> /* task<result> */>;
[TestFixture]
public class Sample {
public static FnAppTask AsTask(FnApp app) {
// wrap Task conventions around a callback func
return env => {
var source = new TaskCompletionSource<Tuple<int, IDictionary<string, string>, IEnumerable<object>>>(TaskCreationOptions.AttachedToParent);
try {
app(env, source.SetException, (status, headers, body) => source.SetResult(Tuple.Create(status, headers, body)));
}
catch (Exception ex) {
source.SetException(ex);
}
return source.Task;
};
}
/// <summary>
/// Host-side, calling app using callback delegates
/// </summary>
[Test]
public void FireAsCallback() {
FnApp app1 = SyncApp;
FnApp app2 = AsyncApp;
var env = new Dictionary<string, object>();
IEnumerable<object> body1 = null;
app1(env,
ex => { },
(status, headers, body) => { body1 = body; });
IEnumerable<object> body2 = null;
app2(env,
ex => { },
(status, headers, body) => { body2 = body; });
// sorry, no such thing as async unit tests
SpinWait.SpinUntil(() => body1 != null);
SpinWait.SpinUntil(() => body2 != null);
AssertBody(body1, "<p>hello world</p>");
AssertBody(body2, "<p>hello async</p>");
}
/// <summary>
/// Host-side, calling app using Task
/// </summary>
[Test]
public void FireAsTask() {
var app1 = AsTask(SyncApp);
var app2 = AsTask(AsyncApp);
var env = new Dictionary<string, object>();
var task1 = app1(env);
var task2 = app2(env);
AssertBody(task1.Result.Item3, "<p>hello world</p>");
AssertBody(task2.Result.Item3, "<p>hello async</p>");
}
static void AssertBody(IEnumerable<object> body, string value) {
Assert.That(body.Aggregate("", (a, b) => a + b), Is.EqualTo(value));
}
/// <summary>
/// Application-side, implemented synchronously
/// </summary>
private static void SyncApp(
IDictionary<string, object> env,
Action<Exception> fault,
Action<int, IDictionary<string, string>, IEnumerable<object>> result) {
try {
result(
200,
new Dictionary<string, string> { { "Content-Type", "text/html" } },
new[] { "<p>hello world</p>" });
}
catch (Exception ex) {
fault(ex);
}
}
/// <summary>
/// Application-side, implemented asynchronously (by whatever means is appropriate, Thread is obviously not)
/// </summary>
private static void AsyncApp(
IDictionary<string, object> env,
Action<Exception> fault,
Action<int, IDictionary<string, string>, IEnumerable<object>> result) {
try {
// the classic fake async maneuver
new Thread(_ => {
try {
Thread.Sleep(1000);
result(
200,
new Dictionary<string, string> { { "Content-Type", "text/html" } },
new[] { "<p>hello async</p>" });
}
catch (Exception ex) {
fault(ex);
}
}).Start();
}
catch (Exception ex) {
fault(ex);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment