Adding integration tests project

This commit is contained in:
Louis DeJardin 2012-11-19 22:55:27 -08:00
Родитель 269dc44671
Коммит 05cfdbf65e
15 изменённых файлов: 3157 добавлений и 1 удалений

Просмотреть файл

@ -58,6 +58,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Owin.Host.HttpLis
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Owin.Host.SystemWeb45.Tests", "tests\Microsoft.Owin.Host.SystemWeb.Tests\Microsoft.Owin.Host.SystemWeb45.Tests.csproj", "{9F0C72D8-E43F-4F01-9DEB-919191919191}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Owin.Host.IntegrationTests", "tests\Microsoft.Owin.Host.IntegrationTests\Microsoft.Owin.Host.IntegrationTests.csproj", "{4B189181-0978-49FE-9A66-BAE377A7DC80}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -238,6 +240,16 @@ Global
{9F0C72D8-E43F-4F01-9DEB-919191919191}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{9F0C72D8-E43F-4F01-9DEB-919191919191}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{9F0C72D8-E43F-4F01-9DEB-919191919191}.Release|x86.ActiveCfg = Release|Any CPU
{4B189181-0978-49FE-9A66-BAE377A7DC80}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4B189181-0978-49FE-9A66-BAE377A7DC80}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4B189181-0978-49FE-9A66-BAE377A7DC80}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{4B189181-0978-49FE-9A66-BAE377A7DC80}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{4B189181-0978-49FE-9A66-BAE377A7DC80}.Debug|x86.ActiveCfg = Debug|Any CPU
{4B189181-0978-49FE-9A66-BAE377A7DC80}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4B189181-0978-49FE-9A66-BAE377A7DC80}.Release|Any CPU.Build.0 = Release|Any CPU
{4B189181-0978-49FE-9A66-BAE377A7DC80}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{4B189181-0978-49FE-9A66-BAE377A7DC80}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{4B189181-0978-49FE-9A66-BAE377A7DC80}.Release|x86.ActiveCfg = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -255,6 +267,7 @@ Global
{9F0C72D8-E43F-4F01-9DEB-9E8FE0AE179E} = {E80B5DAB-7D94-4E26-8A9E-98E5A887A72A}
{9F0C72D8-E43F-4F01-9DEB-919191911919} = {E80B5DAB-7D94-4E26-8A9E-98E5A887A72A}
{9F0C72D8-E43F-4F01-9DEB-919191919191} = {E80B5DAB-7D94-4E26-8A9E-98E5A887A72A}
{4B189181-0978-49FE-9A66-BAE377A7DC80} = {E80B5DAB-7D94-4E26-8A9E-98E5A887A72A}
{311DF219-244B-4A21-AF09-F594E8235877} = {4EC1647A-7297-45BB-8E4A-9D9D8C301B57}
{80D03986-6128-4614-8715-C7E86BD16513} = {4EC1647A-7297-45BB-8E4A-9D9D8C301B57}
{1FBD6DF1-9794-48F3-A7F9-1FDDE580240B} = {ABC8FD67-E4C3-4236-AC51-280551470522}

Просмотреть файл

@ -58,7 +58,7 @@ set if='IsMono' skip='${new[]{
}}'
var BUILD_PROJECTS='${Files.Include("src/**/*.csproj").Exclude(skip)}'
var TEST_PROJECTS='${Files.Include("tests/**/*.Tests.csproj").Exclude("tests/**/*.HttpListener.Tests.csproj")}'
var TEST_PROJECTS='${Files.Include("tests/**/*.Tests.csproj").Include("tests/**/*.IntegrationTests.csproj").Exclude("tests/**/*.HttpListener.Tests.csproj")}'
test if='IsMono'
set TEST_PROJECTS='${Files.Include("./**/Katana.Engine.Tests.csproj")}'
@ -147,6 +147,11 @@ use-standard-goals features='nuget,xunit'
set extra='${extra} -Source ${NUGET_DEPLOY}' if='!string.IsNullOrEmpty(NUGET_DEPLOY)'
nuget-push each='var nupkgFile in Files.Include("target/*.nupkg")'
#xunit-integration-test target="integration-test" description='Run xunit tests'
var INTEGRATIONTEST_ASSEMBLIES='${Files.Include("target/test/**/*.IntegrationTests.dll")}'
xunit each='var testFile in INTEGRATIONTEST_ASSEMBLIES'
#move-main-nuspec-file target='package-prepare'
-File.Move("target/build/Katana/Katana.nuspec", "target/build/Katana.nuspec");

Просмотреть файл

@ -0,0 +1,139 @@
// Licensed to Monkey Square, Inc. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work or additional information regarding copyright
// ownership. Monkey Square, Inc. licenses this file to you
// under the Apache License, Version 2.0 (the "License"); you
// may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
namespace Owin
{
using AppFunc = Func<IDictionary<string, object>, Task>;
internal static partial class StartupExtensions
{
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "By design")]
public static IAppBuilder UseFunc<TApp>(this IAppBuilder builder, Func<TApp, TApp> middleware)
{
if (builder == null)
{
throw new ArgumentNullException("builder");
}
return builder.Use(middleware);
}
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "By design")]
public static IAppBuilder UseFunc(this IAppBuilder builder, Func<AppFunc, AppFunc> middleware)
{
if (builder == null)
{
throw new ArgumentNullException("builder");
}
return builder.Use(middleware);
}
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "By design")]
public static IAppBuilder UseFunc<T1>(this IAppBuilder builder, Func<AppFunc, T1, AppFunc> middleware, T1 arg1)
{
if (builder == null)
{
throw new ArgumentNullException("builder");
}
return builder.UseFunc<AppFunc>(app => middleware(app, arg1));
}
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "By design")]
public static IAppBuilder UseFunc<T1, T2>(this IAppBuilder builder, Func<AppFunc, T1, T2, AppFunc> middleware, T1 arg1, T2 arg2)
{
if (builder == null)
{
throw new ArgumentNullException("builder");
}
return builder.UseFunc<AppFunc>(app => middleware(app, arg1, arg2));
}
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "By design")]
public static IAppBuilder UseFunc<T1, T2, T3>(this IAppBuilder builder, Func<AppFunc, T1, T2, T3, AppFunc> middleware, T1 arg1, T2 arg2, T3 arg3)
{
if (builder == null)
{
throw new ArgumentNullException("builder");
}
return builder.UseFunc<AppFunc>(app => middleware(app, arg1, arg2, arg3));
}
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "By design")]
public static IAppBuilder UseFunc<T1, T2, T3, T4>(this IAppBuilder builder, Func<AppFunc, T1, T2, T3, T4, AppFunc> middleware, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
{
if (builder == null)
{
throw new ArgumentNullException("builder");
}
return builder.UseFunc<AppFunc>(app => middleware(app, arg1, arg2, arg3, arg4));
}
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "By design")]
public static IAppBuilder UseFunc<T1>(this IAppBuilder builder, Func<T1, Func<AppFunc, AppFunc>> middleware, T1 arg1)
{
if (builder == null)
{
throw new ArgumentNullException("builder");
}
return builder.UseFunc<AppFunc>(app => middleware(arg1)(app));
}
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "By design")]
public static IAppBuilder UseFunc<T1, T2>(this IAppBuilder builder, Func<T1, T2, Func<AppFunc, AppFunc>> middleware, T1 arg1, T2 arg2)
{
if (builder == null)
{
throw new ArgumentNullException("builder");
}
return builder.UseFunc<AppFunc>(app => middleware(arg1, arg2)(app));
}
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "By design")]
public static IAppBuilder UseFunc<T1, T2, T3>(this IAppBuilder builder, Func<T1, T2, T3, Func<AppFunc, AppFunc>> middleware, T1 arg1, T2 arg2, T3 arg3)
{
if (builder == null)
{
throw new ArgumentNullException("builder");
}
return builder.UseFunc<AppFunc>(app => middleware(arg1, arg2, arg3)(app));
}
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "By design")]
public static IAppBuilder UseFunc<T1, T2, T3, T4>(this IAppBuilder builder, Func<T1, T2, T3, T4, Func<AppFunc, AppFunc>> middleware, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
{
if (builder == null)
{
throw new ArgumentNullException("builder");
}
return builder.UseFunc<AppFunc>(app => middleware(arg1, arg2, arg3, arg4)(app));
}
}
}

Просмотреть файл

@ -0,0 +1,50 @@
// Licensed to Monkey Square, Inc. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work or additional information regarding copyright
// ownership. Monkey Square, Inc. licenses this file to you
// under the Apache License, Version 2.0 (the "License"); you
// may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
namespace Owin
{
using AppFunc = Func<IDictionary<string, object>, Task>;
internal static partial class StartupExtensions
{
[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "By design")]
public static IAppBuilder UseType<TMiddleware>(this IAppBuilder builder, params object[] args)
{
if (builder == null)
{
throw new ArgumentNullException("builder");
}
return builder.Use(typeof(TMiddleware), args);
}
public static IAppBuilder UseType(this IAppBuilder builder, Type type, params object[] args)
{
if (builder == null)
{
throw new ArgumentNullException("builder");
}
return builder.Use(type, args);
}
}
}

Просмотреть файл

@ -0,0 +1,104 @@
// Licensed to Monkey Square, Inc. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work or additional information regarding copyright
// ownership. Monkey Square, Inc. licenses this file to you
// under the Apache License, Version 2.0 (the "License"); you
// may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
namespace Owin
{
using AppFunc = Func<IDictionary<string, object>, Task>;
internal static partial class StartupExtensions
{
public static void Run(this IAppBuilder builder, object app)
{
if (builder == null)
{
throw new ArgumentNullException("builder");
}
builder.Use(new Func<object, object>(ignored => app));
}
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "By design")]
public static AppFunc Build(this IAppBuilder builder)
{
return builder.Build<AppFunc>();
}
public static TApp Build<TApp>(this IAppBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException("builder");
}
return (TApp)builder.Build(typeof(TApp));
}
[SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix", Justification = "By design")]
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "By design")]
public static AppFunc BuildNew(this IAppBuilder builder, Action<IAppBuilder> configuration)
{
if (builder == null)
{
throw new ArgumentNullException("builder");
}
if (configuration == null)
{
throw new ArgumentNullException("configuration");
}
return builder.BuildNew<AppFunc>(configuration);
}
[SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix", Justification = "By design")]
public static TApp BuildNew<TApp>(this IAppBuilder builder, Action<IAppBuilder> configuration)
{
if (builder == null)
{
throw new ArgumentNullException("builder");
}
if (configuration == null)
{
throw new ArgumentNullException("configuration");
}
var nested = builder.New();
configuration(nested);
return nested.Build<TApp>();
}
public static void AddSignatureConversion(
this IAppBuilder builder,
Delegate conversion)
{
object value;
if (builder.Properties.TryGetValue("builder.AddSignatureConversion", out value) &&
value is Action<Delegate>)
{
((Action<Delegate>)value).Invoke(conversion);
}
else
{
throw new InvalidOperationException("IAppBuilder does not contain builder.AddSignatureConversion method");
}
}
}
}

Просмотреть файл

@ -0,0 +1,396 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
namespace System.Threading.Tasks
{
/// <summary>
/// Helpers for safely using Task libraries.
/// </summary>
internal static class TaskHelpers
{
private static readonly Task _defaultCompleted = FromResult<AsyncVoid>(default(AsyncVoid));
private static readonly Task<object> _completedTaskReturningNull = FromResult<object>(null);
/// <summary>
/// Returns a canceled Task. The task is completed, IsCanceled = True, IsFaulted = False.
/// </summary>
internal static Task Canceled()
{
return CancelCache<AsyncVoid>.Canceled;
}
/// <summary>
/// Returns a canceled Task of the given type. The task is completed, IsCanceled = True, IsFaulted = False.
/// </summary>
internal static Task<TResult> Canceled<TResult>()
{
return CancelCache<TResult>.Canceled;
}
/// <summary>
/// Returns a completed task that has no result.
/// </summary>
internal static Task Completed()
{
return _defaultCompleted;
}
/// <summary>
/// Returns an error task. The task is Completed, IsCanceled = False, IsFaulted = True
/// </summary>
internal static Task FromError(Exception exception)
{
return FromError<AsyncVoid>(exception);
}
/// <summary>
/// Returns an error task of the given type. The task is Completed, IsCanceled = False, IsFaulted = True
/// </summary>
/// <typeparam name="TResult"></typeparam>
internal static Task<TResult> FromError<TResult>(Exception exception)
{
TaskCompletionSource<TResult> tcs = new TaskCompletionSource<TResult>();
tcs.SetException(exception);
return tcs.Task;
}
/// <summary>
/// Returns an error task of the given type. The task is Completed, IsCanceled = False, IsFaulted = True
/// </summary>
internal static Task FromErrors(IEnumerable<Exception> exceptions)
{
return FromErrors<AsyncVoid>(exceptions);
}
/// <summary>
/// Returns an error task of the given type. The task is Completed, IsCanceled = False, IsFaulted = True
/// </summary>
internal static Task<TResult> FromErrors<TResult>(IEnumerable<Exception> exceptions)
{
TaskCompletionSource<TResult> tcs = new TaskCompletionSource<TResult>();
tcs.SetException(exceptions);
return tcs.Task;
}
/// <summary>
/// Returns a successful completed task with the given result.
/// </summary>
internal static Task<TResult> FromResult<TResult>(TResult result)
{
TaskCompletionSource<TResult> tcs = new TaskCompletionSource<TResult>();
tcs.SetResult(result);
return tcs.Task;
}
internal static Task<object> NullResult()
{
return _completedTaskReturningNull;
}
/// <summary>
/// Return a task that runs all the tasks inside the iterator sequentially. It stops as soon
/// as one of the tasks fails or cancels, or after all the tasks have run succesfully.
/// </summary>
/// <param name="asyncIterator">collection of tasks to wait on</param>
/// <param name="cancellationToken">cancellation token</param>
/// <param name="disposeEnumerator">whether or not to dispose the enumerator we get from <paramref name="asyncIterator"/>.
/// Only set to <c>false</c> if you can guarantee that <paramref name="asyncIterator"/>'s enumerator does not have any resources it needs to dispose.</param>
/// <returns>a task that signals completed when all the incoming tasks are finished.</returns>
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The exception is propagated in a Task.")]
internal static Task Iterate(IEnumerable<Task> asyncIterator, CancellationToken cancellationToken = default(CancellationToken), bool disposeEnumerator = true)
{
Contract.Assert(asyncIterator != null);
IEnumerator<Task> enumerator = null;
try
{
enumerator = asyncIterator.GetEnumerator();
Task task = IterateImpl(enumerator, cancellationToken);
return (disposeEnumerator && enumerator != null) ? task.Finally(enumerator.Dispose, runSynchronously: true) : task;
}
catch (Exception ex)
{
return TaskHelpers.FromError(ex);
}
}
/// <summary>
/// Provides the implementation of the Iterate method.
/// Contains special logic to help speed up common cases.
/// </summary>
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The exception is propagated in a Task.")]
internal static Task IterateImpl(IEnumerator<Task> enumerator, CancellationToken cancellationToken)
{
try
{
while (true)
{
// short-circuit: iteration canceled
if (cancellationToken.IsCancellationRequested)
{
return TaskHelpers.Canceled();
}
// short-circuit: iteration complete
if (!enumerator.MoveNext())
{
return TaskHelpers.Completed();
}
// fast case: Task completed synchronously & successfully
Task currentTask = enumerator.Current;
if (currentTask.Status == TaskStatus.RanToCompletion)
{
continue;
}
// fast case: Task completed synchronously & unsuccessfully
if (currentTask.IsCanceled || currentTask.IsFaulted)
{
return currentTask;
}
// slow case: Task isn't yet complete
return IterateImplIncompleteTask(enumerator, currentTask, cancellationToken);
}
}
catch (Exception ex)
{
return TaskHelpers.FromError(ex);
}
}
/// <summary>
/// Fallback for IterateImpl when the antecedent Task isn't yet complete.
/// </summary>
internal static Task IterateImplIncompleteTask(IEnumerator<Task> enumerator, Task currentTask, CancellationToken cancellationToken)
{
// There's a race condition here, the antecedent Task could complete between
// the check in Iterate and the call to Then below. If this happens, we could
// end up growing the stack indefinitely. But the chances of (a) even having
// enough Tasks in the enumerator in the first place and of (b) *every* one
// of them hitting this race condition are so extremely remote that it's not
// worth worrying about.
return currentTask.Then(() => IterateImpl(enumerator, cancellationToken));
}
/// <summary>
/// Replacement for Task.Factory.StartNew when the code can run synchronously.
/// We run the code immediately and avoid the thread switch.
/// This is used to help synchronous code implement task interfaces.
/// </summary>
/// <param name="action">action to run synchronouslyt</param>
/// <param name="token">cancellation token. This is only checked before we run the task, and if cancelled, we immediately return a cancelled task.</param>
/// <returns>a task who result is the result from Func()</returns>
/// <remarks>
/// Avoid calling Task.Factory.StartNew.
/// This avoids gotchas with StartNew:
/// - ensures cancellation token is checked (StartNew doesn't check cancellation tokens).
/// - Keeps on the same thread.
/// - Avoids switching synchronization contexts.
/// Also take in a lambda so that we can wrap in a try catch and honor task failure semantics.
/// </remarks>
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The caught exception type is reflected into a faulted task.")]
public static Task RunSynchronously(Action action, CancellationToken token = default(CancellationToken))
{
if (token.IsCancellationRequested)
{
return Canceled();
}
try
{
action();
return Completed();
}
catch (Exception e)
{
return FromError(e);
}
}
/// <summary>
/// Replacement for Task.Factory.StartNew when the code can run synchronously.
/// We run the code immediately and avoid the thread switch.
/// This is used to help synchronous code implement task interfaces.
/// </summary>
/// <typeparam name="TResult">type of result that task will return.</typeparam>
/// <param name="func">function to run synchronously and produce result</param>
/// <param name="cancellationToken">cancellation token. This is only checked before we run the task, and if cancelled, we immediately return a cancelled task.</param>
/// <returns>a task who result is the result from Func()</returns>
/// <remarks>
/// Avoid calling Task.Factory.StartNew.
/// This avoids gotchas with StartNew:
/// - ensures cancellation token is checked (StartNew doesn't check cancellation tokens).
/// - Keeps on the same thread.
/// - Avoids switching synchronization contexts.
/// Also take in a lambda so that we can wrap in a try catch and honor task failure semantics.
/// </remarks>
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The caught exception type is reflected into a faulted task.")]
internal static Task<TResult> RunSynchronously<TResult>(Func<TResult> func, CancellationToken cancellationToken = default(CancellationToken))
{
if (cancellationToken.IsCancellationRequested)
{
return Canceled<TResult>();
}
try
{
return FromResult(func());
}
catch (Exception e)
{
return FromError<TResult>(e);
}
}
/// <summary>
/// Overload of RunSynchronously that avoids a call to Unwrap().
/// This overload is useful when func() starts doing some synchronous work and then hits IO and
/// needs to create a task to finish the work.
/// </summary>
/// <typeparam name="TResult">type of result that Task will return</typeparam>
/// <param name="func">function that returns a task</param>
/// <param name="cancellationToken">cancellation token. This is only checked before we run the task, and if cancelled, we immediately return a cancelled task.</param>
/// <returns>a task, created by running func().</returns>
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The caught exception type is reflected into a faulted task.")]
internal static Task<TResult> RunSynchronously<TResult>(Func<Task<TResult>> func, CancellationToken cancellationToken = default(CancellationToken))
{
if (cancellationToken.IsCancellationRequested)
{
return Canceled<TResult>();
}
try
{
return func();
}
catch (Exception e)
{
return FromError<TResult>(e);
}
}
/// <summary>
/// Update the completion source if the task failed (cancelled or faulted). No change to completion source if the task succeeded.
/// </summary>
/// <typeparam name="TResult">result type of completion source</typeparam>
/// <param name="tcs">completion source to update</param>
/// <param name="source">task to update from.</param>
/// <returns>true on success</returns>
internal static bool SetIfTaskFailed<TResult>(this TaskCompletionSource<TResult> tcs, Task source)
{
switch (source.Status)
{
case TaskStatus.Canceled:
case TaskStatus.Faulted:
return tcs.TrySetFromTask(source);
}
return false;
}
/// <summary>
/// Set a completion source from the given Task.
/// </summary>
/// <typeparam name="TResult">result type for completion source.</typeparam>
/// <param name="tcs">completion source to set</param>
/// <param name="source">Task to get values from.</param>
/// <returns>true if this successfully sets the completion source.</returns>
[SuppressMessage("Microsoft.WebAPI", "CR4001:DoNotCallProblematicMethodsOnTask", Justification = "This is a known safe usage of Task.Result, since it only occurs when we know the task's state to be completed.")]
internal static bool TrySetFromTask<TResult>(this TaskCompletionSource<TResult> tcs, Task source)
{
if (source.Status == TaskStatus.Canceled)
{
return tcs.TrySetCanceled();
}
if (source.Status == TaskStatus.Faulted)
{
return tcs.TrySetException(source.Exception.InnerExceptions);
}
if (source.Status == TaskStatus.RanToCompletion)
{
Task<TResult> taskOfResult = source as Task<TResult>;
return tcs.TrySetResult(taskOfResult == null ? default(TResult) : taskOfResult.Result);
}
return false;
}
/// <summary>
/// Set a completion source from the given Task. If the task ran to completion and the result type doesn't match
/// the type of the completion source, then a default value will be used. This is useful for converting Task into
/// Task{AsyncVoid}, but it can also accidentally be used to introduce data loss (by passing the wrong
/// task type), so please execute this method with care.
/// </summary>
/// <typeparam name="TResult">result type for completion source.</typeparam>
/// <param name="tcs">completion source to set</param>
/// <param name="source">Task to get values from.</param>
/// <returns>true if this successfully sets the completion source.</returns>
[SuppressMessage("Microsoft.WebAPI", "CR4001:DoNotCallProblematicMethodsOnTask", Justification = "This is a known safe usage of Task.Result, since it only occurs when we know the task's state to be completed.")]
internal static bool TrySetFromTask<TResult>(this TaskCompletionSource<Task<TResult>> tcs, Task source)
{
if (source.Status == TaskStatus.Canceled)
{
return tcs.TrySetCanceled();
}
if (source.Status == TaskStatus.Faulted)
{
return tcs.TrySetException(source.Exception.InnerExceptions);
}
if (source.Status == TaskStatus.RanToCompletion)
{
// Sometimes the source task is Task<Task<TResult>>, and sometimes it's Task<TResult>.
// The latter usually happens when we're in the middle of a sync-block postback where
// the continuation is a function which returns Task<TResult> rather than just TResult,
// but the originating task was itself just Task<TResult>. An example of this can be
// found in TaskExtensions.CatchImpl().
Task<Task<TResult>> taskOfTaskOfResult = source as Task<Task<TResult>>;
if (taskOfTaskOfResult != null)
{
return tcs.TrySetResult(taskOfTaskOfResult.Result);
}
Task<TResult> taskOfResult = source as Task<TResult>;
if (taskOfResult != null)
{
return tcs.TrySetResult(taskOfResult);
}
return tcs.TrySetResult(TaskHelpers.FromResult(default(TResult)));
}
return false;
}
/// <summary>
/// Used as the T in a "conversion" of a Task into a Task{T}
/// </summary>
private struct AsyncVoid
{
}
/// <summary>
/// This class is a convenient cache for per-type cancelled tasks
/// </summary>
private static class CancelCache<TResult>
{
public static readonly Task<TResult> Canceled = GetCancelledTask();
private static Task<TResult> GetCancelledTask()
{
TaskCompletionSource<TResult> tcs = new TaskCompletionSource<TResult>();
tcs.SetCanceled();
return tcs.Task;
}
}
}
}

Просмотреть файл

@ -0,0 +1,943 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Linq.Expressions;
using System.Reflection;
namespace System.Threading.Tasks
{
internal static class TaskHelpersExtensions
{
private static Task<AsyncVoid> _defaultCompleted = TaskHelpers.FromResult<AsyncVoid>(default(AsyncVoid));
private static readonly Action<Task> _rethrowWithNoStackLossDelegate = GetRethrowWithNoStackLossDelegate();
/// <summary>
/// Calls the given continuation, after the given task completes, if it ends in a faulted state.
/// Will not be called if the task did not fault (meaning, it will not be called if the task ran
/// to completion or was canceled). Intended to roughly emulate C# 5's support for "try/catch" in
/// async methods. Note that this method allows you to return a Task, so that you can either return
/// a completed Task (indicating that you swallowed the exception) or a faulted task (indicating that
/// that the exception should be propagated). In C#, you cannot normally use await within a catch
/// block, so returning a real async task should never be done from Catch().
/// </summary>
internal static Task Catch(this Task task, Func<CatchInfo, CatchInfo.CatchResult> continuation, CancellationToken cancellationToken = default(CancellationToken))
{
// Fast path for successful tasks, to prevent an extra TCS allocation
if (task.Status == TaskStatus.RanToCompletion)
{
return task;
}
return task.CatchImpl(() => continuation(new CatchInfo(task)).Task.ToTask<AsyncVoid>(), cancellationToken);
}
/// <summary>
/// Calls the given continuation, after the given task completes, if it ends in a faulted state.
/// Will not be called if the task did not fault (meaning, it will not be called if the task ran
/// to completion or was canceled). Intended to roughly emulate C# 5's support for "try/catch" in
/// async methods. Note that this method allows you to return a Task, so that you can either return
/// a completed Task (indicating that you swallowed the exception) or a faulted task (indicating that
/// that the exception should be propagated). In C#, you cannot normally use await within a catch
/// block, so returning a real async task should never be done from Catch().
/// </summary>
internal static Task<TResult> Catch<TResult>(this Task<TResult> task, Func<CatchInfo<TResult>, CatchInfo<TResult>.CatchResult> continuation, CancellationToken cancellationToken = default(CancellationToken))
{
// Fast path for successful tasks, to prevent an extra TCS allocation
if (task.Status == TaskStatus.RanToCompletion)
{
return task;
}
return task.CatchImpl(() => continuation(new CatchInfo<TResult>(task)).Task, cancellationToken);
}
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The caught exception type is reflected into a faulted task.")]
[SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "CatchInfo", Justification = "This is the name of a class.")]
[SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "TaskHelpersExtensions", Justification = "This is the name of a class.")]
private static Task<TResult> CatchImpl<TResult>(this Task task, Func<Task<TResult>> continuation, CancellationToken cancellationToken)
{
// Stay on the same thread if we can
if (task.IsCompleted)
{
if (task.IsFaulted)
{
try
{
Task<TResult> resultTask = continuation();
if (resultTask == null)
{
// Not a resource because this is an internal class, and this is a guard clause that's intended
// to be thrown by us to us, never escaping out to end users.
throw new InvalidOperationException("You must set the Task property of the CatchInfo returned from the TaskHelpersExtensions.Catch continuation.");
}
return resultTask;
}
catch (Exception ex)
{
return TaskHelpers.FromError<TResult>(ex);
}
}
if (task.IsCanceled || cancellationToken.IsCancellationRequested)
{
return TaskHelpers.Canceled<TResult>();
}
if (task.Status == TaskStatus.RanToCompletion)
{
TaskCompletionSource<TResult> tcs = new TaskCompletionSource<TResult>();
tcs.TrySetFromTask(task);
return tcs.Task;
}
}
// Split into a continuation method so that we don't create a closure unnecessarily
return CatchImplContinuation(task, continuation);
}
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The caught exception type is reflected into a faulted task.")]
[SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "TaskHelpersExtensions", Justification = "This is the name of a class.")]
[SuppressMessage("Microsoft.WebAPI", "CR4001:DoNotCallProblematicMethodsOnTask", Justification = "The usages here are deemed safe, and provide the implementations that this rule relies upon.")]
private static Task<TResult> CatchImplContinuation<TResult>(Task task, Func<Task<TResult>> continuation)
{
SynchronizationContext syncContext = SynchronizationContext.Current;
TaskCompletionSource<Task<TResult>> tcs = new TaskCompletionSource<Task<TResult>>();
// this runs only if the inner task did not fault
task.ContinueWith(innerTask => tcs.TrySetFromTask(innerTask), TaskContinuationOptions.NotOnFaulted | TaskContinuationOptions.ExecuteSynchronously);
// this runs only if the inner task faulted
task.ContinueWith(innerTask =>
{
if (syncContext != null)
{
syncContext.Post(state =>
{
try
{
Task<TResult> resultTask = continuation();
if (resultTask == null)
{
throw new InvalidOperationException("You cannot return null from the TaskHelpersExtensions.Catch continuation. You must return a valid task or throw an exception.");
}
tcs.TrySetResult(resultTask);
}
catch (Exception ex)
{
tcs.TrySetException(ex);
}
}, state: null);
}
else
{
try
{
Task<TResult> resultTask = continuation();
if (resultTask == null)
{
throw new InvalidOperationException("You cannot return null from the TaskHelpersExtensions.Catch continuation. You must return a valid task or throw an exception.");
}
tcs.TrySetResult(resultTask);
}
catch (Exception ex)
{
tcs.TrySetException(ex);
}
}
}, TaskContinuationOptions.OnlyOnFaulted);
return tcs.Task.FastUnwrap();
}
/// <summary>
/// Upon completion of the task, copies its result into the given task completion source, regardless of the
/// completion state. This causes the original task to be fully observed, and the task that is returned by
/// this method will always successfully run to completion, regardless of the original task state.
/// Since this method consumes a task with no return value, you must provide the return value to be used
/// when the inner task ran to successful completion.
/// </summary>
internal static Task CopyResultToCompletionSource<TResult>(this Task task, TaskCompletionSource<TResult> tcs, TResult completionResult)
{
return task.CopyResultToCompletionSourceImpl(tcs, innerTask => completionResult);
}
/// <summary>
/// Upon completion of the task, copies its result into the given task completion source, regardless of the
/// completion state. This causes the original task to be fully observed, and the task that is returned by
/// this method will always successfully run to completion, regardless of the original task state.
/// </summary>
[SuppressMessage("Microsoft.WebAPI", "CR4001:DoNotCallProblematicMethodsOnTask", Justification = "The usages here are deemed safe, and provide the implementations that this rule relies upon.")]
internal static Task CopyResultToCompletionSource<TResult>(this Task<TResult> task, TaskCompletionSource<TResult> tcs)
{
return task.CopyResultToCompletionSourceImpl(tcs, innerTask => innerTask.Result);
}
private static Task CopyResultToCompletionSourceImpl<TTask, TResult>(this TTask task, TaskCompletionSource<TResult> tcs, Func<TTask, TResult> resultThunk)
where TTask : Task
{
// Stay on the same thread if we can
if (task.IsCompleted)
{
switch (task.Status)
{
case TaskStatus.Canceled:
case TaskStatus.Faulted:
TaskHelpers.TrySetFromTask(tcs, task);
break;
case TaskStatus.RanToCompletion:
tcs.TrySetResult(resultThunk(task));
break;
}
return TaskHelpers.Completed();
}
// Split into a continuation method so that we don't create a closure unnecessarily
return CopyResultToCompletionSourceImplContinuation(task, tcs, resultThunk);
}
[SuppressMessage("Microsoft.WebAPI", "CR4001:DoNotCallProblematicMethodsOnTask", Justification = "The usages here are deemed safe, and provide the implementations that this rule relies upon.")]
private static Task CopyResultToCompletionSourceImplContinuation<TTask, TResult>(TTask task, TaskCompletionSource<TResult> tcs, Func<TTask, TResult> resultThunk)
where TTask : Task
{
return task.ContinueWith(innerTask =>
{
switch (innerTask.Status)
{
case TaskStatus.Canceled:
case TaskStatus.Faulted:
TaskHelpers.TrySetFromTask(tcs, innerTask);
break;
case TaskStatus.RanToCompletion:
tcs.TrySetResult(resultThunk(task));
break;
}
}, TaskContinuationOptions.ExecuteSynchronously);
}
/// <summary>
/// Cast Task to Task of object
/// </summary>
[SuppressMessage("Microsoft.WebAPI", "CR4001:DoNotCallProblematicMethodsOnTask", Justification = "The usages here are deemed safe, and provide the implementations that this rule relies upon.")]
internal static Task<object> CastToObject(this Task task)
{
// Stay on the same thread if we can
if (task.IsCompleted)
{
if (task.IsFaulted)
{
return TaskHelpers.FromErrors<object>(task.Exception.InnerExceptions);
}
if (task.IsCanceled)
{
return TaskHelpers.Canceled<object>();
}
if (task.Status == TaskStatus.RanToCompletion)
{
return TaskHelpers.FromResult<object>((object)null);
}
}
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
// schedule a synchronous task to cast: no need to worry about sync context or try/catch
task.ContinueWith(innerTask =>
{
if (innerTask.IsFaulted)
{
tcs.SetException(innerTask.Exception.InnerExceptions);
}
else if (innerTask.IsCanceled)
{
tcs.SetCanceled();
}
else
{
tcs.SetResult((object)null);
}
}, TaskContinuationOptions.ExecuteSynchronously);
return tcs.Task;
}
/// <summary>
/// Cast Task of T to Task of object
/// </summary>
[SuppressMessage("Microsoft.WebAPI", "CR4001:DoNotCallProblematicMethodsOnTask", Justification = "The usages here are deemed safe, and provide the implementations that this rule relies upon.")]
internal static Task<object> CastToObject<T>(this Task<T> task)
{
// Stay on the same thread if we can
if (task.IsCompleted)
{
if (task.IsFaulted)
{
return TaskHelpers.FromErrors<object>(task.Exception.InnerExceptions);
}
if (task.IsCanceled)
{
return TaskHelpers.Canceled<object>();
}
if (task.Status == TaskStatus.RanToCompletion)
{
return TaskHelpers.FromResult<object>((object)task.Result);
}
}
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
// schedule a synchronous task to cast: no need to worry about sync context or try/catch
task.ContinueWith(innerTask =>
{
if (innerTask.IsFaulted)
{
tcs.SetException(innerTask.Exception.InnerExceptions);
}
else if (innerTask.IsCanceled)
{
tcs.SetCanceled();
}
else
{
tcs.SetResult((object)innerTask.Result);
}
}, TaskContinuationOptions.ExecuteSynchronously);
return tcs.Task;
}
/// <summary>
/// Cast Task of object to Task of T
/// </summary>
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The caught exception type is reflected into a faulted task.")]
[SuppressMessage("Microsoft.WebAPI", "CR4001:DoNotCallProblematicMethodsOnTask", Justification = "The usages here are deemed safe, and provide the implementations that this rule relies upon.")]
internal static Task<TOuterResult> CastFromObject<TOuterResult>(this Task<object> task)
{
// Stay on the same thread if we can
if (task.IsCompleted)
{
if (task.IsFaulted)
{
return TaskHelpers.FromErrors<TOuterResult>(task.Exception.InnerExceptions);
}
if (task.IsCanceled)
{
return TaskHelpers.Canceled<TOuterResult>();
}
if (task.Status == TaskStatus.RanToCompletion)
{
try
{
return TaskHelpers.FromResult<TOuterResult>((TOuterResult)task.Result);
}
catch (Exception exception)
{
return TaskHelpers.FromError<TOuterResult>(exception);
}
}
}
TaskCompletionSource<TOuterResult> tcs = new TaskCompletionSource<TOuterResult>();
// schedule a synchronous task to cast: no need to worry about sync context or try/catch
task.ContinueWith(innerTask =>
{
if (innerTask.IsFaulted)
{
tcs.SetException(innerTask.Exception.InnerExceptions);
}
else if (innerTask.IsCanceled)
{
tcs.SetCanceled();
}
else
{
try
{
tcs.SetResult((TOuterResult)innerTask.Result);
}
catch (Exception exception)
{
tcs.SetException(exception);
}
}
}, TaskContinuationOptions.ExecuteSynchronously);
return tcs.Task;
}
/// <summary>
/// A version of task.Unwrap that is optimized to prevent unnecessarily capturing the
/// execution context when the antecedent task is already completed.
/// </summary>
[SuppressMessage("Microsoft.WebAPI", "CR4000:DoNotUseProblematicTaskTypes", Justification = "The usages here are deemed safe, and provide the implementations that this rule relies upon.")]
[SuppressMessage("Microsoft.WebAPI", "CR4001:DoNotCallProblematicMethodsOnTask", Justification = "The usages here are deemed safe, and provide the implementations that this rule relies upon.")]
internal static Task FastUnwrap(this Task<Task> task)
{
Task innerTask = task.Status == TaskStatus.RanToCompletion ? task.Result : null;
return innerTask ?? task.Unwrap();
}
/// <summary>
/// A version of task.Unwrap that is optimized to prevent unnecessarily capturing the
/// execution context when the antecedent task is already completed.
/// </summary>
[SuppressMessage("Microsoft.WebAPI", "CR4000:DoNotUseProblematicTaskTypes", Justification = "The usages here are deemed safe, and provide the implementations that this rule relies upon.")]
[SuppressMessage("Microsoft.WebAPI", "CR4001:DoNotCallProblematicMethodsOnTask", Justification = "The usages here are deemed safe, and provide the implementations that this rule relies upon.")]
internal static Task<TResult> FastUnwrap<TResult>(this Task<Task<TResult>> task)
{
Task<TResult> innerTask = task.Status == TaskStatus.RanToCompletion ? task.Result : null;
return innerTask ?? task.Unwrap();
}
/// <summary>
/// Calls the given continuation, after the given task has completed, regardless of the state
/// the task ended in. Intended to roughly emulate C# 5's support for "finally" in async methods.
/// </summary>
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The caught exception type is reflected into a faulted task.")]
internal static Task Finally(this Task task, Action continuation, bool runSynchronously = false)
{
// Stay on the same thread if we can
if (task.IsCompleted)
{
try
{
continuation();
return task;
}
catch (Exception ex)
{
MarkExceptionsObserved(task);
return TaskHelpers.FromError(ex);
}
}
// Split into a continuation method so that we don't create a closure unnecessarily
return FinallyImplContinuation<AsyncVoid>(task, continuation, runSynchronously);
}
/// <summary>
/// Calls the given continuation, after the given task has completed, regardless of the state
/// the task ended in. Intended to roughly emulate C# 5's support for "finally" in async methods.
/// </summary>
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The caught exception type is reflected into a faulted task.")]
internal static Task<TResult> Finally<TResult>(this Task<TResult> task, Action continuation, bool runSynchronously = false)
{
// Stay on the same thread if we can
if (task.IsCompleted)
{
try
{
continuation();
return task;
}
catch (Exception ex)
{
MarkExceptionsObserved(task);
return TaskHelpers.FromError<TResult>(ex);
}
}
// Split into a continuation method so that we don't create a closure unnecessarily
return FinallyImplContinuation<TResult>(task, continuation, runSynchronously);
}
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The caught exception type is reflected into a faulted task.")]
[SuppressMessage("Microsoft.WebAPI", "CR4001:DoNotCallProblematicMethodsOnTask", Justification = "The usages here are deemed safe, and provide the implementations that this rule relies upon.")]
private static Task<TResult> FinallyImplContinuation<TResult>(Task task, Action continuation, bool runSynchronously = false)
{
SynchronizationContext syncContext = SynchronizationContext.Current;
TaskCompletionSource<TResult> tcs = new TaskCompletionSource<TResult>();
task.ContinueWith(innerTask =>
{
try
{
if (syncContext != null)
{
syncContext.Post(state =>
{
try
{
continuation();
tcs.TrySetFromTask(innerTask);
}
catch (Exception ex)
{
MarkExceptionsObserved(innerTask);
tcs.SetException(ex);
}
}, state: null);
}
else
{
continuation();
tcs.TrySetFromTask(innerTask);
}
}
catch (Exception ex)
{
MarkExceptionsObserved(innerTask);
tcs.TrySetException(ex);
}
}, runSynchronously ? TaskContinuationOptions.ExecuteSynchronously : TaskContinuationOptions.None);
return tcs.Task;
}
[SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes", Justification = "This general exception is not intended to be seen by the user")]
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "This general exception is not intended to be seen by the user")]
[SuppressMessage("Microsoft.WebAPI", "CR4001:DoNotCallProblematicMethodsOnTask", Justification = "The usages here are deemed safe, and provide the implementations that this rule relies upon.")]
private static Action<Task> GetRethrowWithNoStackLossDelegate()
{
MethodInfo getAwaiterMethod = typeof(Task).GetMethod("GetAwaiter", Type.EmptyTypes);
if (getAwaiterMethod != null)
{
// .NET 4.5 - dump the same code the 'await' keyword would have dumped
// >> task.GetAwaiter().GetResult()
// No-ops if the task completed successfully, else throws the originating exception complete with the correct call stack.
var taskParameter = Expression.Parameter(typeof(Task));
var getAwaiterCall = Expression.Call(taskParameter, getAwaiterMethod);
var getResultCall = Expression.Call(getAwaiterCall, "GetResult", Type.EmptyTypes);
var lambda = Expression.Lambda<Action<Task>>(getResultCall, taskParameter);
return lambda.Compile();
}
else
{
Func<Exception, Exception> prepForRemoting = null;
try
{
if (AppDomain.CurrentDomain.IsFullyTrusted)
{
// .NET 4 - do the same thing Lazy<T> does by calling Exception.PrepForRemoting
// This is an internal method in mscorlib.dll, so pass a test Exception to it to make sure we can call it.
var exceptionParameter = Expression.Parameter(typeof(Exception));
var prepForRemotingCall = Expression.Call(exceptionParameter, "PrepForRemoting", Type.EmptyTypes);
var lambda = Expression.Lambda<Func<Exception, Exception>>(prepForRemotingCall, exceptionParameter);
var func = lambda.Compile();
func(new Exception()); // make sure the method call succeeds before assigning the 'prepForRemoting' local variable
prepForRemoting = func;
}
}
catch
{
} // If delegate creation fails (medium trust) we will simply throw the base exception.
return task =>
{
try
{
task.Wait();
}
catch (AggregateException ex)
{
Exception baseException = ex.GetBaseException();
if (prepForRemoting != null)
{
baseException = prepForRemoting(baseException);
}
throw baseException;
}
};
}
}
/// <summary>
/// Marks a Task as "exception observed". The Task is required to have been completed first.
/// </summary>
/// <remarks>
/// Useful for 'finally' clauses, as if the 'finally' action throws we'll propagate the new
/// exception and lose track of the inner exception.
/// </remarks>
[SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", MessageId = "unused", Justification = "We only call the property getter for its side effect; we don't care about the value.")]
private static void MarkExceptionsObserved(this Task task)
{
Contract.Assert(task.IsCompleted);
Exception unused = task.Exception;
}
/// <summary>
/// Calls the given continuation, after the given task has completed, if the task successfully ran
/// to completion (i.e., was not cancelled and did not fault).
/// </summary>
internal static Task Then(this Task task, Action continuation, CancellationToken cancellationToken = default(CancellationToken), bool runSynchronously = false)
{
return task.ThenImpl(t => ToAsyncVoidTask(continuation), cancellationToken, runSynchronously);
}
/// <summary>
/// Calls the given continuation, after the given task has completed, if the task successfully ran
/// to completion (i.e., was not cancelled and did not fault).
/// </summary>
internal static Task<TOuterResult> Then<TOuterResult>(this Task task, Func<TOuterResult> continuation, CancellationToken cancellationToken = default(CancellationToken), bool runSynchronously = false)
{
return task.ThenImpl(t => TaskHelpers.FromResult(continuation()), cancellationToken, runSynchronously);
}
/// <summary>
/// Calls the given continuation, after the given task has completed, if the task successfully ran
/// to completion (i.e., was not cancelled and did not fault).
/// </summary>
internal static Task Then(this Task task, Func<Task> continuation, CancellationToken cancellationToken = default(CancellationToken), bool runSynchronously = false)
{
return task.Then(() => continuation().Then(() => default(AsyncVoid)),
cancellationToken, runSynchronously);
}
/// <summary>
/// Calls the given continuation, after the given task has completed, if the task successfully ran
/// to completion (i.e., was not cancelled and did not fault).
/// </summary>
internal static Task<TOuterResult> Then<TOuterResult>(this Task task, Func<Task<TOuterResult>> continuation, CancellationToken cancellationToken = default(CancellationToken), bool runSynchronously = false)
{
return task.ThenImpl(t => continuation(), cancellationToken, runSynchronously);
}
/// <summary>
/// Calls the given continuation, after the given task has completed, if the task successfully ran
/// to completion (i.e., was not cancelled and did not fault). The continuation is provided with the
/// result of the task as its sole parameter.
/// </summary>
[SuppressMessage("Microsoft.WebAPI", "CR4001:DoNotCallProblematicMethodsOnTask", Justification = "The usages here are deemed safe, and provide the implementations that this rule relies upon.")]
internal static Task Then<TInnerResult>(this Task<TInnerResult> task, Action<TInnerResult> continuation, CancellationToken cancellationToken = default(CancellationToken), bool runSynchronously = false)
{
return task.ThenImpl(t => ToAsyncVoidTask(() => continuation(t.Result)), cancellationToken, runSynchronously);
}
/// <summary>
/// Calls the given continuation, after the given task has completed, if the task successfully ran
/// to completion (i.e., was not cancelled and did not fault). The continuation is provided with the
/// result of the task as its sole parameter.
/// </summary>
[SuppressMessage("Microsoft.WebAPI", "CR4001:DoNotCallProblematicMethodsOnTask", Justification = "The usages here are deemed safe, and provide the implementations that this rule relies upon.")]
internal static Task<TOuterResult> Then<TInnerResult, TOuterResult>(this Task<TInnerResult> task, Func<TInnerResult, TOuterResult> continuation, CancellationToken cancellationToken = default(CancellationToken), bool runSynchronously = false)
{
return task.ThenImpl(t => TaskHelpers.FromResult(continuation(t.Result)), cancellationToken, runSynchronously);
}
/// <summary>
/// Calls the given continuation, after the given task has completed, if the task successfully ran
/// to completion (i.e., was not cancelled and did not fault). The continuation is provided with the
/// result of the task as its sole parameter.
/// </summary>
[SuppressMessage("Microsoft.WebAPI", "CR4001:DoNotCallProblematicMethodsOnTask", Justification = "The usages here are deemed safe, and provide the implementations that this rule relies upon.")]
internal static Task Then<TInnerResult>(this Task<TInnerResult> task, Func<TInnerResult, Task> continuation, CancellationToken token = default(CancellationToken), bool runSynchronously = false)
{
return task.ThenImpl(t => continuation(t.Result).ToTask<AsyncVoid>(), token, runSynchronously);
}
/// <summary>
/// Calls the given continuation, after the given task has completed, if the task successfully ran
/// to completion (i.e., was not cancelled and did not fault). The continuation is provided with the
/// result of the task as its sole parameter.
/// </summary>
[SuppressMessage("Microsoft.WebAPI", "CR4001:DoNotCallProblematicMethodsOnTask", Justification = "The usages here are deemed safe, and provide the implementations that this rule relies upon.")]
internal static Task<TOuterResult> Then<TInnerResult, TOuterResult>(this Task<TInnerResult> task, Func<TInnerResult, Task<TOuterResult>> continuation, CancellationToken cancellationToken = default(CancellationToken), bool runSynchronously = false)
{
return task.ThenImpl(t => continuation(t.Result), cancellationToken, runSynchronously);
}
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The caught exception type is reflected into a faulted task.")]
private static Task<TOuterResult> ThenImpl<TTask, TOuterResult>(this TTask task, Func<TTask, Task<TOuterResult>> continuation, CancellationToken cancellationToken, bool runSynchronously)
where TTask : Task
{
// Stay on the same thread if we can
if (task.IsCompleted)
{
if (task.IsFaulted)
{
return TaskHelpers.FromErrors<TOuterResult>(task.Exception.InnerExceptions);
}
if (task.IsCanceled || cancellationToken.IsCancellationRequested)
{
return TaskHelpers.Canceled<TOuterResult>();
}
if (task.Status == TaskStatus.RanToCompletion)
{
try
{
return continuation(task);
}
catch (Exception ex)
{
return TaskHelpers.FromError<TOuterResult>(ex);
}
}
}
// Split into a continuation method so that we don't create a closure unnecessarily
return ThenImplContinuation(task, continuation, cancellationToken, runSynchronously);
}
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The caught exception type is reflected into a faulted task.")]
[SuppressMessage("Microsoft.WebAPI", "CR4001:DoNotCallProblematicMethodsOnTask", Justification = "The usages here are deemed safe, and provide the implementations that this rule relies upon.")]
private static Task<TOuterResult> ThenImplContinuation<TOuterResult, TTask>(TTask task, Func<TTask, Task<TOuterResult>> continuation, CancellationToken cancellationToken, bool runSynchronously = false)
where TTask : Task
{
SynchronizationContext syncContext = SynchronizationContext.Current;
TaskCompletionSource<Task<TOuterResult>> tcs = new TaskCompletionSource<Task<TOuterResult>>();
task.ContinueWith(innerTask =>
{
if (innerTask.IsFaulted)
{
tcs.TrySetException(innerTask.Exception.InnerExceptions);
}
else if (innerTask.IsCanceled || cancellationToken.IsCancellationRequested)
{
tcs.TrySetCanceled();
}
else
{
if (syncContext != null)
{
syncContext.Post(state =>
{
try
{
tcs.TrySetResult(continuation(task));
}
catch (Exception ex)
{
tcs.TrySetException(ex);
}
}, state: null);
}
else
{
tcs.TrySetResult(continuation(task));
}
}
}, runSynchronously ? TaskContinuationOptions.ExecuteSynchronously : TaskContinuationOptions.None);
return tcs.Task.FastUnwrap();
}
/// <summary>
/// Throws the first faulting exception for a task which is faulted. It attempts to preserve the original
/// stack trace when throwing the exception (which should always work in 4.5, and should also work in 4.0
/// when running in full trust). Note: It is the caller's responsibility not to pass incomplete tasks to
/// this method, because it does degenerate into a call to the equivalent of .Wait() on the task when it
/// hasn't yet completed.
/// </summary>
internal static void ThrowIfFaulted(this Task task)
{
_rethrowWithNoStackLossDelegate(task);
}
/// <summary>
/// Adapts any action into a Task (returning AsyncVoid, so that it's usable with Task{T} extension methods).
/// </summary>
private static Task<AsyncVoid> ToAsyncVoidTask(Action action)
{
return TaskHelpers.RunSynchronously<AsyncVoid>(() =>
{
action();
return _defaultCompleted;
});
}
/// <summary>
/// Changes the return value of a task to the given result, if the task ends in the RanToCompletion state.
/// This potentially imposes an extra ContinueWith to convert a non-completed task, so use this with caution.
/// </summary>
internal static Task<TResult> ToTask<TResult>(this Task task, CancellationToken cancellationToken = default(CancellationToken), TResult result = default(TResult))
{
if (task == null)
{
return null;
}
// Stay on the same thread if we can
if (task.IsCompleted)
{
if (task.IsFaulted)
{
return TaskHelpers.FromErrors<TResult>(task.Exception.InnerExceptions);
}
if (task.IsCanceled || cancellationToken.IsCancellationRequested)
{
return TaskHelpers.Canceled<TResult>();
}
if (task.Status == TaskStatus.RanToCompletion)
{
return TaskHelpers.FromResult(result);
}
}
// Split into a continuation method so that we don't create a closure unnecessarily
return ToTaskContinuation(task, result);
}
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The caught exception type is reflected into a faulted task.")]
[SuppressMessage("Microsoft.WebAPI", "CR4001:DoNotCallProblematicMethodsOnTask", Justification = "The usages here are deemed safe, and provide the implementations that this rule relies upon.")]
private static Task<TResult> ToTaskContinuation<TResult>(Task task, TResult result)
{
TaskCompletionSource<TResult> tcs = new TaskCompletionSource<TResult>();
task.ContinueWith(innerTask =>
{
if (task.Status == TaskStatus.RanToCompletion)
{
tcs.TrySetResult(result);
}
else
{
tcs.TrySetFromTask(innerTask);
}
}, TaskContinuationOptions.ExecuteSynchronously);
return tcs.Task;
}
/// <summary>
/// Attempts to get the result value for the given task. If the task ran to completion, then
/// it will return true and set the result value; otherwise, it will return false.
/// </summary>
[SuppressMessage("Microsoft.WebAPI", "CR4001:DoNotCallProblematicMethodsOnTask", Justification = "The usages here are deemed safe, and provide the implementations that this rule relies upon.")]
internal static bool TryGetResult<TResult>(this Task<TResult> task, out TResult result)
{
if (task.Status == TaskStatus.RanToCompletion)
{
result = task.Result;
return true;
}
result = default(TResult);
return false;
}
/// <summary>
/// Used as the T in a "conversion" of a Task into a Task{T}
/// </summary>
private struct AsyncVoid
{
}
}
internal abstract class CatchInfoBase<TTask>
where TTask : Task
{
private Exception _exception;
private TTask _task;
protected CatchInfoBase(TTask task)
{
Contract.Assert(task != null);
_task = task;
_exception = _task.Exception.GetBaseException(); // Observe the exception early, to prevent tasks tearing down the app domain
}
/// <summary>
/// The exception that was thrown to cause the Catch block to execute.
/// </summary>
public Exception Exception
{
get { return _exception; }
}
/// <summary>
/// Returns a CatchResult that re-throws the original exception.
/// </summary>
public CatchResult Throw()
{
return new CatchResult { Task = _task };
}
/// <summary>
/// Represents a result to be returned from a Catch handler.
/// </summary>
internal struct CatchResult
{
/// <summary>
/// Gets or sets the task to be returned to the caller.
/// </summary>
internal TTask Task { get; set; }
}
}
internal class CatchInfo : CatchInfoBase<Task>
{
private static CatchResult _completed = new CatchResult { Task = TaskHelpers.Completed() };
public CatchInfo(Task task)
: base(task)
{
}
/// <summary>
/// Returns a CatchResult that returns a completed (non-faulted) task.
/// </summary>
[SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "This would result in poor usability.")]
public CatchResult Handled()
{
return _completed;
}
/// <summary>
/// Returns a CatchResult that executes the given task and returns it, in whatever state it finishes.
/// </summary>
/// <param name="task">The task to return.</param>
[SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "This would result in poor usability.")]
public CatchResult Task(Task task)
{
return new CatchResult { Task = task };
}
/// <summary>
/// Returns a CatchResult that throws the given exception.
/// </summary>
/// <param name="ex">The exception to throw.</param>
[SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "This would result in poor usability.")]
public CatchResult Throw(Exception ex)
{
return new CatchResult { Task = TaskHelpers.FromError<object>(ex) };
}
}
internal class CatchInfo<T> : CatchInfoBase<Task<T>>
{
public CatchInfo(Task<T> task)
: base(task)
{
}
/// <summary>
/// Returns a CatchResult that returns a completed (non-faulted) task.
/// </summary>
/// <param name="returnValue">The return value of the task.</param>
[SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "This would result in poor usability.")]
public CatchResult Handled(T returnValue)
{
return new CatchResult { Task = TaskHelpers.FromResult(returnValue) };
}
/// <summary>
/// Returns a CatchResult that executes the given task and returns it, in whatever state it finishes.
/// </summary>
/// <param name="task">The task to return.</param>
[SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "This would result in poor usability.")]
public CatchResult Task(Task<T> task)
{
return new CatchResult { Task = task };
}
/// <summary>
/// Returns a CatchResult that throws the given exception.
/// </summary>
/// <param name="ex">The exception to throw.</param>
[SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "This would result in poor usability.")]
public CatchResult Throw(Exception ex)
{
return new CatchResult { Task = TaskHelpers.FromError<T>(ex) };
}
}
}

Просмотреть файл

@ -0,0 +1,113 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{4B189181-0978-49FE-9A66-BAE377A7DC80}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Microsoft.Owin.Host.IntegrationTests</RootNamespace>
<AssemblyName>Microsoft.Owin.Host.IntegrationTests</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
<RestorePackages>true</RestorePackages>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json">
<HintPath>..\..\packages\Newtonsoft.Json.4.5.10\lib\net40\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="Owin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0ebd12fd5e55cc5, processorArchitecture=MSIL">
<Private>True</Private>
<HintPath>..\..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath>
</Reference>
<Reference Include="Shouldly, Version=1.1.1.1, Culture=neutral, PublicKeyToken=6042cbcb05cbc941, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\packages\Shouldly.1.1.1.1\lib\35\Shouldly.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Net.Http.Formatting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.AspNet.WebApi.Client.4.0.20710.0\lib\net40\System.Net.Http.Formatting.dll</HintPath>
</Reference>
<Reference Include="System.Net.Http.WebRequest" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="xunit">
<HintPath>..\..\packages\xunit.1.9.1\lib\net20\xunit.dll</HintPath>
</Reference>
<Reference Include="xunit.extensions">
<HintPath>..\..\packages\xunit.extensions.1.9.1\lib\net20\xunit.extensions.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\build\CommonAssemblyInfo.cs">
<Link>Properties\CommonAssemblyInfo.cs</Link>
</Compile>
<Compile Include="App_Packages\Owin.Extensions.Sources.0.6.3\StartupExtensions.cs" />
<Compile Include="App_Packages\Owin.Extensions.Sources.0.6.3\StartupExtensions.Func.cs" />
<Compile Include="App_Packages\Owin.Extensions.Sources.0.6.3\StartupExtensions.Type.cs" />
<Compile Include="App_Packages\TaskHelpers.Sources.0.1\TaskHelpers.cs" />
<Compile Include="App_Packages\TaskHelpers.Sources.0.1\TaskHelpersExtensions.cs" />
<Compile Include="SimpleGetTests.cs" />
<Compile Include="TestBase.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TestBaseWorks.cs" />
</ItemGroup>
<ItemGroup>
<None Include="applicationHost.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="packages.config" />
<None Include="web.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Katana.Engine\Katana.Engine.csproj">
<Project>{c225eb2e-e7a7-463f-b058-1705f204978e}</Project>
<Name>Katana.Engine</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Microsoft.Owin.Host.HttpListener\Microsoft.Owin.Host.HttpListener.csproj">
<Project>{452c45c7-57a5-4161-bf7d-c1cd5ad4bb84}</Project>
<Name>Microsoft.Owin.Host.HttpListener</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Microsoft.Owin.Host.SystemWeb\Microsoft.Owin.Host.SystemWeb.csproj">
<Project>{e31826e6-c29a-4f08-bd4f-e17aca793bbc}</Project>
<Name>Microsoft.Owin.Host.SystemWeb</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

Просмотреть файл

@ -0,0 +1,19 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Microsoft.Owin.Host.Integration.Tests")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("11b3a9c8-336e-45da-b21b-7b712e6319f3")]

Просмотреть файл

@ -0,0 +1,60 @@
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
using Owin;
using Shouldly;
using Xunit.Extensions;
namespace Microsoft.Owin.Host.IntegrationTests
{
public class SimpleGetTests : TestBase
{
public void Configuration(IAppBuilder app)
{
app.UseFunc(_ => Invoke);
}
public Task Invoke(IDictionary<string, object> env)
{
var headers = (IDictionary<string, string[]>)env["owin.ResponseHeaders"];
headers["Content-Type"] = new string[] { "text/plain" };
var body = (Stream)env["owin.ResponseBody"];
using (var writer = new StreamWriter(body))
{
writer.Write("<p>alpha</p>");
}
return TaskHelpers.Completed();
}
[Theory]
[InlineData("Microsoft.Owin.Host.SystemWeb")]
[InlineData("Microsoft.Owin.Host.HttpListener")]
public void ResponseBodyShouldArrive(string serverName)
{
var port = base.RunWebServer(
serverName,
typeof(SimpleGetTests).FullName);
var client = new HttpClient();
client.GetStringAsync("http://localhost:" + port + "/text")
.Then(body => body.ShouldBe("<p>alpha</p>"));
}
[Theory]
[InlineData("Microsoft.Owin.Host.SystemWeb")]
[InlineData("Microsoft.Owin.Host.HttpListener")]
public void ContentTypeShouldBeSet(string serverName)
{
var port = base.RunWebServer(
serverName,
typeof(SimpleGetTests).FullName);
var client = new HttpClient();
client.GetAsync("http://localhost:" + port + "/text")
.Then(message => message.Content.Headers.ContentType.MediaType.ShouldBe("text/html"));
}
}
}

Просмотреть файл

@ -0,0 +1,214 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Katana.Engine;
namespace Microsoft.Owin.Host.IntegrationTests
{
public class TestBase : IDisposable
{
readonly CancellationTokenSource _disposing = new CancellationTokenSource();
public void Dispose()
{
_disposing.Cancel(false);
}
public int RunWebServer(
string serverName = null,
string applicationName = null)
{
if (serverName == "Microsoft.Owin.Host.SystemWeb")
{
return RunWebServerSystemWeb(applicationName);
}
else
{
return RunWebServerViaEngine(serverName, applicationName);
}
}
private int RunWebServerViaEngine(
string serverName,
string applicationName)
{
var port = 8080;
var sourceDirectory = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
var targetDirectory = BuildTargetDirectory(
sourceDirectory,
applicationName,
port);
Directory.SetCurrentDirectory(targetDirectory);
var server = WebApplication.Start(
boot: "Default",
server: serverName,
app: applicationName,
port: port);
_disposing.Token.Register(() =>
{
server.Dispose();
try
{
Directory.Delete(targetDirectory, true);
}
catch(Exception ex)
{
Trace.WriteLine(string.Format("Cleanup error {0}", ex.Message));
}
});
return port;
}
private int RunWebServerSystemWeb(string applicationName)
{
var tcs = new TaskCompletionSource<object>();
var port = GetAvailablePort();
var sourceDirectory = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
var targetDirectory = BuildTargetDirectory(
sourceDirectory,
applicationName,
port);
var targetHostConfig = Path.Combine(targetDirectory, "ApplicationHost.config");
string programFile32 = Environment.GetEnvironmentVariable("ProgramFiles(x86)");
string iisExpressExe = Path.Combine(programFile32, "IIS Express", "iisexpress.exe");
var info = new ProcessStartInfo
{
WorkingDirectory = targetDirectory,
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
ErrorDialog = false,
CreateNoWindow = false,
FileName = iisExpressExe,
Arguments = "/config:\"" + targetHostConfig + "\" /trace:error",
};
//Log.Debug("Executing {0}", Definition.Command);
Process process = Process.Start(info);
process.OutputDataReceived += OutputDataReceived;
process.ErrorDataReceived += OutputDataReceived;
process.Exited += (a, b) => tcs.TrySetResult(null);
process.EnableRaisingEvents = true;
process.BeginOutputReadLine();
process.BeginErrorReadLine();
_disposing.Token.Register(() =>
{
//process.CloseMainWindow();
//process.StandardInput.WriteLine("Q");
//process.StandardInput.Flush();
//process.StandardInput.Close();
//tcs.Task.Wait(250);
if (!process.HasExited)
{
SetForegroundWindow(process.MainWindowHandle);
SendKeys.SendWait("Q");
tcs.Task.Wait(1000);
}
if (!process.HasExited)
{
process.Kill();
tcs.Task.Wait();
}
try
{
Directory.Delete(targetDirectory, true);
}
catch (Exception ex)
{
Trace.WriteLine(string.Format("Cleanup error {0}", ex.Message));
}
});
return port;
}
private static string BuildTargetDirectory(
string workingDirectory,
string applicationName,
int port)
{
string targetDirectory = Path.Combine(workingDirectory, "Port" + port + "-" + Guid.NewGuid().ToString("n"));
string binDirectory = Path.Combine(targetDirectory, "bin");
string sourceHostConfig = Path.Combine(workingDirectory, "applicationHost.config");
var targetHostConfig = Path.Combine(targetDirectory, "applicationHost.config");
string sourceWebConfig = Path.Combine(workingDirectory, "web.config");
string targetWebConfig = Path.Combine(targetDirectory, "web.config");
Directory.CreateDirectory(targetDirectory);
Directory.CreateDirectory(binDirectory);
File.WriteAllText(
targetHostConfig,
File.ReadAllText(sourceHostConfig)
.Replace("TheBindingInformation", ":" + port + ":localhost")
.Replace("ThePhysicalPath", targetDirectory));
File.WriteAllText(
targetWebConfig,
File.ReadAllText(sourceWebConfig)
.Replace("TheApplicationName", applicationName));
foreach (var assemblyName in Directory.GetFiles(workingDirectory, "*.dll"))
{
File.Copy(
Path.Combine(workingDirectory, assemblyName),
Path.Combine(binDirectory, Path.GetFileName(assemblyName)),
overwrite: false);
}
return targetDirectory;
}
private static int GetAvailablePort()
{
var socket = new Socket(
AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.IP);
socket.Bind(new IPEndPoint(IPAddress.Any, 0));
var ipEndPoint = socket.LocalEndPoint as IPEndPoint;
int port = 0;
if (ipEndPoint != null)
{
port = ipEndPoint.Port;
}
socket.Close();
return port;
}
// Activate an application window.
[DllImport("USER32.DLL")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
private void OutputDataReceived(object sender, DataReceivedEventArgs e)
{
Trace.WriteLine(e.Data);
}
}
}

Просмотреть файл

@ -0,0 +1,54 @@
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
using Owin;
using Shouldly;
using Xunit;
using Xunit.Extensions;
namespace Microsoft.Owin.Host.IntegrationTests
{
public class TestBaseWorks : TestBase
{
[Fact]
public Task TestShouldRunWebServer()
{
var port = RunWebServer(
serverName: "Microsoft.Owin.Host.SystemWeb",
applicationName: typeof(TestBaseWorks).FullName + ".HelloWorld");
var client = new HttpClient();
return client.GetStringAsync("http://localhost:" + port)
.Then(responseMessage => responseMessage.ShouldBe("Hello world!"));
}
public void HelloWorld(IAppBuilder app)
{
app.UseFunc(_ => env =>
{
var output = (Stream)env["owin.ResponseBody"];
using (var writer = new StreamWriter(output))
{
writer.Write("Hello world!");
}
return TaskHelpers.Completed();
});
}
[Theory]
[InlineData("Microsoft.Owin.Host.SystemWeb")]
[InlineData("Microsoft.Owin.Host.HttpListener")]
public Task ServerMayBeSystemWebOrHttpListener(string serverName)
{
var port = RunWebServer(
serverName: serverName,
applicationName: typeof(TestBaseWorks).FullName + ".HelloWorld");
var client = new HttpClient();
return client.GetStringAsync("http://localhost:" + port)
.Then(responseMessage => responseMessage.ShouldBe("Hello world!"));
}
}
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.AspNet.WebApi.Client" version="4.0.20710.0" targetFramework="net45" />
<package id="Microsoft.Net.Http" version="2.0.20710.0" targetFramework="net45" />
<package id="Newtonsoft.Json" version="4.5.10" targetFramework="net45" />
<package id="Owin" version="1.0" targetFramework="net45" />
<package id="Owin.Extensions.Sources" version="0.6.3" targetFramework="net45" />
<package id="Shouldly" version="1.1.1.1" targetFramework="net40" />
<package id="TaskHelpers.Sources" version="0.1" targetFramework="net40" />
<package id="xunit" version="1.9.1" targetFramework="net45" />
<package id="xunit.extensions" version="1.9.1" targetFramework="net40" />
</packages>

Просмотреть файл

@ -0,0 +1,7 @@
<configuration>
<appSettings>
<add key="owin:HandleAllRequests" value="true"/>
<add key="owin:SetCurrentDirectory" value="true"/>
<add key="owin:Configuration" value="TheApplicationName"/>
</appSettings>
</configuration>