Adding integration tests project
This commit is contained in:
Родитель
269dc44671
Коммит
05cfdbf65e
13
Katana.sln
13
Katana.sln
|
@ -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>
|
Загрузка…
Ссылка в новой задаче