From af034a9c5eec379c823a637b33d89f23a6a2716a Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Tue, 30 Oct 2018 14:18:11 -0700 Subject: [PATCH 001/183] Reorganize source code In preparation for merging many other projects into this repo, this establishes a new source code organization which groups projects together based on subject matter. \n\nCommit migrated from https://github.com/dotnet/extensions/commit/7ce647cfa3287e31497b72643eee28531eed1b7f --- src/ObjectPool/src/DefaultObjectPool.cs | 108 +++ .../src/DefaultObjectPoolProvider.cs | 17 + .../src/DefaultPooledObjectPolicy.cs | 20 + src/ObjectPool/src/IPooledObjectPolicy.cs | 12 + src/ObjectPool/src/LeakTrackingObjectPool.cs | 69 ++ .../src/LeakTrackingObjectPoolProvider.cs | 28 + .../Microsoft.Extensions.ObjectPool.csproj | 11 + src/ObjectPool/src/ObjectPool.cs | 12 + src/ObjectPool/src/ObjectPoolProvider.cs | 15 + .../src/ObjectPoolProviderExtensions.cs | 29 + src/ObjectPool/src/PooledObjectPolicy.cs | 12 + .../src/StringBuilderPooledObjectPolicy.cs | 31 + src/ObjectPool/src/baseline.netcore.json | 612 +++++++++++++ src/ObjectPool/test/DefaultObjectPoolTest.cs | 87 ++ ...crosoft.Extensions.ObjectPool.Tests.csproj | 11 + .../AspNetCoreBenchmarkAttribute.cs | 39 + .../BenchmarkRunner/DefaultCoreConfig.cs | 36 + .../DefaultCoreValidationConfig.cs | 25 + .../BenchmarkRunner/Directory.Build.props | 8 + .../ParameterizedJobConfigAttribute.cs | 15 + src/Shared/BenchmarkRunner/Program.cs | 88 ++ .../CertificateManager.cs | 720 +++++++++++++++ .../CertificatePurpose.cs | 12 + .../Directory.Build.props | 8 + .../EnsureCertificateResult.cs | 20 + .../ClosedGenericMatcher.cs | 106 +++ .../CommandLine/AnsiConsole.cs | 143 +++ .../CommandLine/CommandArgument.cs | 29 + .../CommandLine/CommandLineApplication.cs | 555 ++++++++++++ .../CommandLine/CommandOption.cs | 108 +++ .../CommandLine/CommandOptionType.cs | 13 + .../CommandLine/CommandParsingException.cs | 18 + ...Extensions.CommandLineUtils.Sources.shproj | 13 + .../Utilities/ArgumentEscaper.cs | 109 +++ .../CommandLineUtils/Utilities/DotNetMuxer.cs | 58 ++ .../CopyOnWriteDictionary.cs | 155 ++++ .../CopyOnWriteDictionaryHolder.cs | 166 ++++ .../HashCodeCombiner/HashCodeCombiner.cs | 84 ++ .../ObjectMethodExecutor/AwaitableInfo.cs | 127 +++ .../CoercedAwaitableInfo.cs | 55 ++ .../ObjectMethodExecutor.cs | 340 +++++++ .../ObjectMethodExecutorAwaitable.cs | 114 +++ .../ObjectMethodExecutorFSharpSupport.cs | 151 ++++ src/Shared/Process/ProcessHelper.cs | 113 +++ .../PropertyActivator/PropertyActivator.cs | 110 +++ src/Shared/PropertyHelper/PropertyHelper.cs | 526 +++++++++++ src/Shared/RazorViews/AttributeValue.cs | 38 + src/Shared/RazorViews/BaseView.cs | 279 ++++++ src/Shared/RazorViews/HelperResult.cs | 34 + src/Shared/SecurityHelper/SecurityHelper.cs | 40 + .../ExceptionDetails/ExceptionDetails.cs | 29 + .../ExceptionDetailsProvider.cs | 170 ++++ .../StackFrame/MethodDisplayInfo.cs | 49 ++ .../StackFrame/ParameterDisplayInfo.cs | 33 + .../StackFrame/PortablePdbReader.cs | 135 +++ .../StackTrace/StackFrame/StackFrameInfo.cs | 18 + .../StackFrame/StackFrameSourceCodeInfo.cs | 54 ++ .../StackTrace/StackFrame/StackTraceHelper.cs | 261 ++++++ src/Shared/TypeNameHelper/TypeNameHelper.cs | 160 ++++ src/Shared/ValueStopwatch/ValueStopwatch.cs | 39 + .../Properties/EncoderResources.cs | 38 + src/Shared/WebEncoders/WebEncoders.cs | 388 ++++++++ .../test/Shared.Tests/ArgumentEscaperTests.cs | 24 + .../Shared.Tests/CertificateManagerTests.cs | 304 +++++++ .../Shared.Tests/ClosedGenericMatcherTest.cs | 360 ++++++++ .../CommandLineApplicationTests.cs | 693 +++++++++++++++ .../CopyOnWriteDictionaryHolderTest.cs | 91 ++ .../Shared.Tests/CopyOnWriteDictionaryTest.cs | 109 +++ .../test/Shared.Tests/DotNetMuxerTests.cs | 24 + .../test/Shared.Tests/HashCodeCombinerTest.cs | 39 + .../Microsoft.AspNetCore.Shared.Tests.csproj | 32 + .../Shared.Tests/ObjectMethodExecutorTest.cs | 634 +++++++++++++ .../Shared.Tests/PropertyActivatorTest.cs | 187 ++++ .../test/Shared.Tests/PropertyHelperTest.cs | 831 ++++++++++++++++++ src/Shared/test/Shared.Tests/Readme.txt | 4 + .../test/Shared.Tests/SecurityHelperTests.cs | 93 ++ .../test/Shared.Tests/StackTraceHelperTest.cs | 345 ++++++++ .../test/Shared.Tests/TypeNameHelperTest.cs | 263 ++++++ .../test/Shared.Tests/ValueStopwatchTest.cs | 39 + .../test/Shared.Tests/WebEncodersTests.cs | 113 +++ .../testassets/ThrowingLibrary/Thrower.cs | 20 + .../ThrowingLibrary/ThrowingLibrary.csproj | 8 + 82 files changed, 11116 insertions(+) create mode 100644 src/ObjectPool/src/DefaultObjectPool.cs create mode 100644 src/ObjectPool/src/DefaultObjectPoolProvider.cs create mode 100644 src/ObjectPool/src/DefaultPooledObjectPolicy.cs create mode 100644 src/ObjectPool/src/IPooledObjectPolicy.cs create mode 100644 src/ObjectPool/src/LeakTrackingObjectPool.cs create mode 100644 src/ObjectPool/src/LeakTrackingObjectPoolProvider.cs create mode 100644 src/ObjectPool/src/Microsoft.Extensions.ObjectPool.csproj create mode 100644 src/ObjectPool/src/ObjectPool.cs create mode 100644 src/ObjectPool/src/ObjectPoolProvider.cs create mode 100644 src/ObjectPool/src/ObjectPoolProviderExtensions.cs create mode 100644 src/ObjectPool/src/PooledObjectPolicy.cs create mode 100644 src/ObjectPool/src/StringBuilderPooledObjectPolicy.cs create mode 100644 src/ObjectPool/src/baseline.netcore.json create mode 100644 src/ObjectPool/test/DefaultObjectPoolTest.cs create mode 100644 src/ObjectPool/test/Microsoft.Extensions.ObjectPool.Tests.csproj create mode 100644 src/Shared/BenchmarkRunner/AspNetCoreBenchmarkAttribute.cs create mode 100644 src/Shared/BenchmarkRunner/DefaultCoreConfig.cs create mode 100644 src/Shared/BenchmarkRunner/DefaultCoreValidationConfig.cs create mode 100644 src/Shared/BenchmarkRunner/Directory.Build.props create mode 100644 src/Shared/BenchmarkRunner/ParameterizedJobConfigAttribute.cs create mode 100644 src/Shared/BenchmarkRunner/Program.cs create mode 100644 src/Shared/CertificateGeneration/CertificateManager.cs create mode 100644 src/Shared/CertificateGeneration/CertificatePurpose.cs create mode 100644 src/Shared/CertificateGeneration/Directory.Build.props create mode 100644 src/Shared/CertificateGeneration/EnsureCertificateResult.cs create mode 100644 src/Shared/ClosedGenericMatcher/ClosedGenericMatcher.cs create mode 100644 src/Shared/CommandLineUtils/CommandLine/AnsiConsole.cs create mode 100644 src/Shared/CommandLineUtils/CommandLine/CommandArgument.cs create mode 100644 src/Shared/CommandLineUtils/CommandLine/CommandLineApplication.cs create mode 100644 src/Shared/CommandLineUtils/CommandLine/CommandOption.cs create mode 100644 src/Shared/CommandLineUtils/CommandLine/CommandOptionType.cs create mode 100644 src/Shared/CommandLineUtils/CommandLine/CommandParsingException.cs create mode 100644 src/Shared/CommandLineUtils/Microsoft.Extensions.CommandLineUtils.Sources.shproj create mode 100644 src/Shared/CommandLineUtils/Utilities/ArgumentEscaper.cs create mode 100644 src/Shared/CommandLineUtils/Utilities/DotNetMuxer.cs create mode 100644 src/Shared/CopyOnWriteDictionary/CopyOnWriteDictionary.cs create mode 100644 src/Shared/CopyOnWriteDictionary/CopyOnWriteDictionaryHolder.cs create mode 100644 src/Shared/HashCodeCombiner/HashCodeCombiner.cs create mode 100644 src/Shared/ObjectMethodExecutor/AwaitableInfo.cs create mode 100644 src/Shared/ObjectMethodExecutor/CoercedAwaitableInfo.cs create mode 100644 src/Shared/ObjectMethodExecutor/ObjectMethodExecutor.cs create mode 100644 src/Shared/ObjectMethodExecutor/ObjectMethodExecutorAwaitable.cs create mode 100644 src/Shared/ObjectMethodExecutor/ObjectMethodExecutorFSharpSupport.cs create mode 100644 src/Shared/Process/ProcessHelper.cs create mode 100644 src/Shared/PropertyActivator/PropertyActivator.cs create mode 100644 src/Shared/PropertyHelper/PropertyHelper.cs create mode 100644 src/Shared/RazorViews/AttributeValue.cs create mode 100644 src/Shared/RazorViews/BaseView.cs create mode 100644 src/Shared/RazorViews/HelperResult.cs create mode 100644 src/Shared/SecurityHelper/SecurityHelper.cs create mode 100644 src/Shared/StackTrace/ExceptionDetails/ExceptionDetails.cs create mode 100644 src/Shared/StackTrace/ExceptionDetails/ExceptionDetailsProvider.cs create mode 100644 src/Shared/StackTrace/StackFrame/MethodDisplayInfo.cs create mode 100644 src/Shared/StackTrace/StackFrame/ParameterDisplayInfo.cs create mode 100644 src/Shared/StackTrace/StackFrame/PortablePdbReader.cs create mode 100644 src/Shared/StackTrace/StackFrame/StackFrameInfo.cs create mode 100644 src/Shared/StackTrace/StackFrame/StackFrameSourceCodeInfo.cs create mode 100644 src/Shared/StackTrace/StackFrame/StackTraceHelper.cs create mode 100644 src/Shared/TypeNameHelper/TypeNameHelper.cs create mode 100644 src/Shared/ValueStopwatch/ValueStopwatch.cs create mode 100644 src/Shared/WebEncoders/Properties/EncoderResources.cs create mode 100644 src/Shared/WebEncoders/WebEncoders.cs create mode 100644 src/Shared/test/Shared.Tests/ArgumentEscaperTests.cs create mode 100644 src/Shared/test/Shared.Tests/CertificateManagerTests.cs create mode 100644 src/Shared/test/Shared.Tests/ClosedGenericMatcherTest.cs create mode 100644 src/Shared/test/Shared.Tests/CommandLineApplicationTests.cs create mode 100644 src/Shared/test/Shared.Tests/CopyOnWriteDictionaryHolderTest.cs create mode 100644 src/Shared/test/Shared.Tests/CopyOnWriteDictionaryTest.cs create mode 100644 src/Shared/test/Shared.Tests/DotNetMuxerTests.cs create mode 100644 src/Shared/test/Shared.Tests/HashCodeCombinerTest.cs create mode 100644 src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj create mode 100644 src/Shared/test/Shared.Tests/ObjectMethodExecutorTest.cs create mode 100644 src/Shared/test/Shared.Tests/PropertyActivatorTest.cs create mode 100644 src/Shared/test/Shared.Tests/PropertyHelperTest.cs create mode 100644 src/Shared/test/Shared.Tests/Readme.txt create mode 100644 src/Shared/test/Shared.Tests/SecurityHelperTests.cs create mode 100644 src/Shared/test/Shared.Tests/StackTraceHelperTest.cs create mode 100644 src/Shared/test/Shared.Tests/TypeNameHelperTest.cs create mode 100644 src/Shared/test/Shared.Tests/ValueStopwatchTest.cs create mode 100644 src/Shared/test/Shared.Tests/WebEncodersTests.cs create mode 100644 src/Shared/test/testassets/ThrowingLibrary/Thrower.cs create mode 100644 src/Shared/test/testassets/ThrowingLibrary/ThrowingLibrary.csproj diff --git a/src/ObjectPool/src/DefaultObjectPool.cs b/src/ObjectPool/src/DefaultObjectPool.cs new file mode 100644 index 000000000000..87825967ac7a --- /dev/null +++ b/src/ObjectPool/src/DefaultObjectPool.cs @@ -0,0 +1,108 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace Microsoft.Extensions.ObjectPool +{ + public class DefaultObjectPool : ObjectPool where T : class + { + private readonly ObjectWrapper[] _items; + private readonly IPooledObjectPolicy _policy; + private readonly bool _isDefaultPolicy; + private T _firstItem; + + // This class was introduced in 2.1 to avoid the interface call where possible + private readonly PooledObjectPolicy _fastPolicy; + + public DefaultObjectPool(IPooledObjectPolicy policy) + : this(policy, Environment.ProcessorCount * 2) + { + } + + public DefaultObjectPool(IPooledObjectPolicy policy, int maximumRetained) + { + _policy = policy ?? throw new ArgumentNullException(nameof(policy)); + _fastPolicy = policy as PooledObjectPolicy; + _isDefaultPolicy = IsDefaultPolicy(); + + // -1 due to _firstItem + _items = new ObjectWrapper[maximumRetained - 1]; + + bool IsDefaultPolicy() + { + var type = policy.GetType(); + + return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(DefaultPooledObjectPolicy<>); + } + } + + public override T Get() + { + T item = _firstItem; + + if (item == null || Interlocked.CompareExchange(ref _firstItem, null, item) != item) + { + item = GetViaScan(); + } + + return item; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private T GetViaScan() + { + ObjectWrapper[] items = _items; + + for (var i = 0; i < items.Length; i++) + { + T item = items[i]; + + if (item != null && Interlocked.CompareExchange(ref items[i].Element, null, item) == item) + { + return item; + } + } + + return Create(); + } + + // Non-inline to improve its code quality as uncommon path + [MethodImpl(MethodImplOptions.NoInlining)] + private T Create() => _fastPolicy?.Create() ?? _policy.Create(); + + public override void Return(T obj) + { + if (_isDefaultPolicy || (_fastPolicy?.Return(obj) ?? _policy.Return(obj))) + { + if (_firstItem != null || Interlocked.CompareExchange(ref _firstItem, obj, null) != null) + { + ReturnViaScan(obj); + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ReturnViaScan(T obj) + { + ObjectWrapper[] items = _items; + + for (var i = 0; i < items.Length && Interlocked.CompareExchange(ref items[i].Element, obj, null) != null; ++i) + { + } + } + + [DebuggerDisplay("{Element}")] + private struct ObjectWrapper + { + public T Element; + + public ObjectWrapper(T item) => Element = item; + + public static implicit operator T(ObjectWrapper wrapper) => wrapper.Element; + } + } +} diff --git a/src/ObjectPool/src/DefaultObjectPoolProvider.cs b/src/ObjectPool/src/DefaultObjectPoolProvider.cs new file mode 100644 index 000000000000..fb3c4bfa7ed8 --- /dev/null +++ b/src/ObjectPool/src/DefaultObjectPoolProvider.cs @@ -0,0 +1,17 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.Extensions.ObjectPool +{ + public class DefaultObjectPoolProvider : ObjectPoolProvider + { + public int MaximumRetained { get; set; } = Environment.ProcessorCount * 2; + + public override ObjectPool Create(IPooledObjectPolicy policy) + { + return new DefaultObjectPool(policy, MaximumRetained); + } + } +} diff --git a/src/ObjectPool/src/DefaultPooledObjectPolicy.cs b/src/ObjectPool/src/DefaultPooledObjectPolicy.cs new file mode 100644 index 000000000000..a7c386ae2a88 --- /dev/null +++ b/src/ObjectPool/src/DefaultPooledObjectPolicy.cs @@ -0,0 +1,20 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.Extensions.ObjectPool +{ + public class DefaultPooledObjectPolicy : PooledObjectPolicy where T : class, new() + { + public override T Create() + { + return new T(); + } + + // DefaultObjectPool doesn't call 'Return' for the default policy. + // So take care adding any logic to this method, as it might require changes elsewhere. + public override bool Return(T obj) + { + return true; + } + } +} diff --git a/src/ObjectPool/src/IPooledObjectPolicy.cs b/src/ObjectPool/src/IPooledObjectPolicy.cs new file mode 100644 index 000000000000..54611bad30ed --- /dev/null +++ b/src/ObjectPool/src/IPooledObjectPolicy.cs @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.Extensions.ObjectPool +{ + public interface IPooledObjectPolicy + { + T Create(); + + bool Return(T obj); + } +} diff --git a/src/ObjectPool/src/LeakTrackingObjectPool.cs b/src/ObjectPool/src/LeakTrackingObjectPool.cs new file mode 100644 index 000000000000..243d44d2daca --- /dev/null +++ b/src/ObjectPool/src/LeakTrackingObjectPool.cs @@ -0,0 +1,69 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace Microsoft.Extensions.ObjectPool +{ + public class LeakTrackingObjectPool : ObjectPool where T : class + { + private readonly ConditionalWeakTable _trackers = new ConditionalWeakTable(); + private readonly ObjectPool _inner; + + public LeakTrackingObjectPool(ObjectPool inner) + { + if (inner == null) + { + throw new ArgumentNullException(nameof(inner)); + } + + _inner = inner; + } + + public override T Get() + { + var value = _inner.Get(); + _trackers.Add(value, new Tracker()); + return value; + } + + public override void Return(T obj) + { + Tracker tracker; + if (_trackers.TryGetValue(obj, out tracker)) + { + _trackers.Remove(obj); + tracker.Dispose(); + } + + _inner.Return(obj); + } + + private class Tracker : IDisposable + { + private readonly string _stack; + private bool _disposed; + + public Tracker() + { + _stack = Environment.StackTrace; + } + + public void Dispose() + { + _disposed = true; + GC.SuppressFinalize(this); + } + + ~Tracker() + { + if (!_disposed && !Environment.HasShutdownStarted) + { + Debug.Fail($"{typeof(T).Name} was leaked. Created at: {Environment.NewLine}{_stack}"); + } + } + } + } +} diff --git a/src/ObjectPool/src/LeakTrackingObjectPoolProvider.cs b/src/ObjectPool/src/LeakTrackingObjectPoolProvider.cs new file mode 100644 index 000000000000..134eaf161c31 --- /dev/null +++ b/src/ObjectPool/src/LeakTrackingObjectPoolProvider.cs @@ -0,0 +1,28 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.Extensions.ObjectPool +{ + public class LeakTrackingObjectPoolProvider : ObjectPoolProvider + { + private readonly ObjectPoolProvider _inner; + + public LeakTrackingObjectPoolProvider(ObjectPoolProvider inner) + { + if (inner == null) + { + throw new ArgumentNullException(nameof(inner)); + } + + _inner = inner; + } + + public override ObjectPool Create(IPooledObjectPolicy policy) + { + var inner = _inner.Create(policy); + return new LeakTrackingObjectPool(inner); + } + } +} diff --git a/src/ObjectPool/src/Microsoft.Extensions.ObjectPool.csproj b/src/ObjectPool/src/Microsoft.Extensions.ObjectPool.csproj new file mode 100644 index 000000000000..cb42c5615a51 --- /dev/null +++ b/src/ObjectPool/src/Microsoft.Extensions.ObjectPool.csproj @@ -0,0 +1,11 @@ + + + + A simple object pool implementation. + netstandard2.0 + $(NoWarn);CS1591 + true + pooling + + + diff --git a/src/ObjectPool/src/ObjectPool.cs b/src/ObjectPool/src/ObjectPool.cs new file mode 100644 index 000000000000..8cf52c919595 --- /dev/null +++ b/src/ObjectPool/src/ObjectPool.cs @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.Extensions.ObjectPool +{ + public abstract class ObjectPool where T : class + { + public abstract T Get(); + + public abstract void Return(T obj); + } +} diff --git a/src/ObjectPool/src/ObjectPoolProvider.cs b/src/ObjectPool/src/ObjectPoolProvider.cs new file mode 100644 index 000000000000..909795dd35fe --- /dev/null +++ b/src/ObjectPool/src/ObjectPoolProvider.cs @@ -0,0 +1,15 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.Extensions.ObjectPool +{ + public abstract class ObjectPoolProvider + { + public ObjectPool Create() where T : class, new() + { + return Create(new DefaultPooledObjectPolicy()); + } + + public abstract ObjectPool Create(IPooledObjectPolicy policy) where T : class; + } +} diff --git a/src/ObjectPool/src/ObjectPoolProviderExtensions.cs b/src/ObjectPool/src/ObjectPoolProviderExtensions.cs new file mode 100644 index 000000000000..b9e93598894f --- /dev/null +++ b/src/ObjectPool/src/ObjectPoolProviderExtensions.cs @@ -0,0 +1,29 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Text; + +namespace Microsoft.Extensions.ObjectPool +{ + public static class ObjectPoolProviderExtensions + { + public static ObjectPool CreateStringBuilderPool(this ObjectPoolProvider provider) + { + return provider.Create(new StringBuilderPooledObjectPolicy()); + } + + public static ObjectPool CreateStringBuilderPool( + this ObjectPoolProvider provider, + int initialCapacity, + int maximumRetainedCapacity) + { + var policy = new StringBuilderPooledObjectPolicy() + { + InitialCapacity = initialCapacity, + MaximumRetainedCapacity = maximumRetainedCapacity, + }; + + return provider.Create(policy); + } + } +} diff --git a/src/ObjectPool/src/PooledObjectPolicy.cs b/src/ObjectPool/src/PooledObjectPolicy.cs new file mode 100644 index 000000000000..855b76496778 --- /dev/null +++ b/src/ObjectPool/src/PooledObjectPolicy.cs @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.Extensions.ObjectPool +{ + public abstract class PooledObjectPolicy : IPooledObjectPolicy + { + public abstract T Create(); + + public abstract bool Return(T obj); + } +} diff --git a/src/ObjectPool/src/StringBuilderPooledObjectPolicy.cs b/src/ObjectPool/src/StringBuilderPooledObjectPolicy.cs new file mode 100644 index 000000000000..94f318729a74 --- /dev/null +++ b/src/ObjectPool/src/StringBuilderPooledObjectPolicy.cs @@ -0,0 +1,31 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Text; + +namespace Microsoft.Extensions.ObjectPool +{ + public class StringBuilderPooledObjectPolicy : PooledObjectPolicy + { + public int InitialCapacity { get; set; } = 100; + + public int MaximumRetainedCapacity { get; set; } = 4 * 1024; + + public override StringBuilder Create() + { + return new StringBuilder(InitialCapacity); + } + + public override bool Return(StringBuilder obj) + { + if (obj.Capacity > MaximumRetainedCapacity) + { + // Too big. Discard this one. + return false; + } + + obj.Clear(); + return true; + } + } +} diff --git a/src/ObjectPool/src/baseline.netcore.json b/src/ObjectPool/src/baseline.netcore.json new file mode 100644 index 000000000000..253c1f6b6641 --- /dev/null +++ b/src/ObjectPool/src/baseline.netcore.json @@ -0,0 +1,612 @@ +{ + "AssemblyIdentity": "Microsoft.Extensions.ObjectPool, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "Types": [ + { + "Name": "Microsoft.Extensions.ObjectPool.DefaultObjectPool", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.Extensions.ObjectPool.ObjectPool", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "Get", + "Parameters": [], + "ReturnType": "T0", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Return", + "Parameters": [ + { + "Name": "obj", + "Type": "T0" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "policy", + "Type": "Microsoft.Extensions.ObjectPool.IPooledObjectPolicy" + } + ], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "policy", + "Type": "Microsoft.Extensions.ObjectPool.IPooledObjectPolicy" + }, + { + "Name": "maximumRetained", + "Type": "System.Int32" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "T", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.Extensions.ObjectPool.DefaultObjectPoolProvider", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.Extensions.ObjectPool.ObjectPoolProvider", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_MaximumRetained", + "Parameters": [], + "ReturnType": "System.Int32", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_MaximumRetained", + "Parameters": [ + { + "Name": "value", + "Type": "System.Int32" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Create", + "Parameters": [ + { + "Name": "policy", + "Type": "Microsoft.Extensions.ObjectPool.IPooledObjectPolicy" + } + ], + "ReturnType": "Microsoft.Extensions.ObjectPool.ObjectPool", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "T", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.Extensions.ObjectPool.DefaultPooledObjectPolicy", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.Extensions.ObjectPool.PooledObjectPolicy", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "Create", + "Parameters": [], + "ReturnType": "T0", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.Extensions.ObjectPool.IPooledObjectPolicy", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Return", + "Parameters": [ + { + "Name": "obj", + "Type": "T0" + } + ], + "ReturnType": "System.Boolean", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.Extensions.ObjectPool.IPooledObjectPolicy", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "T", + "ParameterPosition": 0, + "New": true, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.Extensions.ObjectPool.IPooledObjectPolicy", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "Create", + "Parameters": [], + "ReturnType": "T0", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Return", + "Parameters": [ + { + "Name": "obj", + "Type": "T0" + } + ], + "ReturnType": "System.Boolean", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "T", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.Extensions.ObjectPool.LeakTrackingObjectPool", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.Extensions.ObjectPool.ObjectPool", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "Get", + "Parameters": [], + "ReturnType": "T0", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Return", + "Parameters": [ + { + "Name": "obj", + "Type": "T0" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "inner", + "Type": "Microsoft.Extensions.ObjectPool.ObjectPool" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "T", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.Extensions.ObjectPool.LeakTrackingObjectPoolProvider", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.Extensions.ObjectPool.ObjectPoolProvider", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "Create", + "Parameters": [ + { + "Name": "policy", + "Type": "Microsoft.Extensions.ObjectPool.IPooledObjectPolicy" + } + ], + "ReturnType": "Microsoft.Extensions.ObjectPool.ObjectPool", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "T", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "inner", + "Type": "Microsoft.Extensions.ObjectPool.ObjectPoolProvider" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.Extensions.ObjectPool.ObjectPool", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "Get", + "Parameters": [], + "ReturnType": "T0", + "Virtual": true, + "Abstract": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Return", + "Parameters": [ + { + "Name": "obj", + "Type": "T0" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Abstract": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Protected", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "T", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.Extensions.ObjectPool.ObjectPoolProvider", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "Create", + "Parameters": [ + { + "Name": "policy", + "Type": "Microsoft.Extensions.ObjectPool.IPooledObjectPolicy" + } + ], + "ReturnType": "Microsoft.Extensions.ObjectPool.ObjectPool", + "Virtual": true, + "Abstract": true, + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "T", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "Create", + "Parameters": [], + "ReturnType": "Microsoft.Extensions.ObjectPool.ObjectPool", + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "T", + "ParameterPosition": 0, + "New": true, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Protected", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.Extensions.ObjectPool.ObjectPoolProviderExtensions", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "CreateStringBuilderPool", + "Parameters": [ + { + "Name": "provider", + "Type": "Microsoft.Extensions.ObjectPool.ObjectPoolProvider" + } + ], + "ReturnType": "Microsoft.Extensions.ObjectPool.ObjectPool", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CreateStringBuilderPool", + "Parameters": [ + { + "Name": "provider", + "Type": "Microsoft.Extensions.ObjectPool.ObjectPoolProvider" + }, + { + "Name": "initialCapacity", + "Type": "System.Int32" + }, + { + "Name": "maximumRetainedCapacity", + "Type": "System.Int32" + } + ], + "ReturnType": "Microsoft.Extensions.ObjectPool.ObjectPool", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.Extensions.ObjectPool.PooledObjectPolicy", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "ImplementedInterfaces": [ + "Microsoft.Extensions.ObjectPool.IPooledObjectPolicy" + ], + "Members": [ + { + "Kind": "Method", + "Name": "Create", + "Parameters": [], + "ReturnType": "T0", + "Virtual": true, + "Abstract": true, + "ImplementedInterface": "Microsoft.Extensions.ObjectPool.IPooledObjectPolicy", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Return", + "Parameters": [ + { + "Name": "obj", + "Type": "T0" + } + ], + "ReturnType": "System.Boolean", + "Virtual": true, + "Abstract": true, + "ImplementedInterface": "Microsoft.Extensions.ObjectPool.IPooledObjectPolicy", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Protected", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "T", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.Extensions.ObjectPool.StringBuilderPooledObjectPolicy", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.Extensions.ObjectPool.PooledObjectPolicy", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "Create", + "Parameters": [], + "ReturnType": "System.Text.StringBuilder", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.Extensions.ObjectPool.IPooledObjectPolicy", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Return", + "Parameters": [ + { + "Name": "obj", + "Type": "System.Text.StringBuilder" + } + ], + "ReturnType": "System.Boolean", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.Extensions.ObjectPool.IPooledObjectPolicy", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_InitialCapacity", + "Parameters": [], + "ReturnType": "System.Int32", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_InitialCapacity", + "Parameters": [ + { + "Name": "value", + "Type": "System.Int32" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_MaximumRetainedCapacity", + "Parameters": [], + "ReturnType": "System.Int32", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_MaximumRetainedCapacity", + "Parameters": [ + { + "Name": "value", + "Type": "System.Int32" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + } + ] +} \ No newline at end of file diff --git a/src/ObjectPool/test/DefaultObjectPoolTest.cs b/src/ObjectPool/test/DefaultObjectPoolTest.cs new file mode 100644 index 000000000000..ca14a3f53b6a --- /dev/null +++ b/src/ObjectPool/test/DefaultObjectPoolTest.cs @@ -0,0 +1,87 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Xunit; + +namespace Microsoft.Extensions.ObjectPool.Test +{ + public class DefaultObjectPoolTest + { + [Fact] + public void DefaultObjectPoolWithDefaultPolicy_GetAnd_ReturnObject_SameInstance() + { + // Arrange + var pool = new DefaultObjectPool(new DefaultPooledObjectPolicy()); + + var obj1 = pool.Get(); + pool.Return(obj1); + + // Act + var obj2 = pool.Get(); + + // Assert + Assert.Same(obj1, obj2); + } + + [Fact] + public void DefaultObjectPool_GetAndReturnObject_SameInstance() + { + // Arrange + var pool = new DefaultObjectPool>(new ListPolicy()); + + var list1 = pool.Get(); + pool.Return(list1); + + // Act + var list2 = pool.Get(); + + // Assert + Assert.Same(list1, list2); + } + + [Fact] + public void DefaultObjectPool_CreatedByPolicy() + { + // Arrange + var pool = new DefaultObjectPool>(new ListPolicy()); + + // Act + var list = pool.Get(); + + // Assert + Assert.Equal(17, list.Capacity); + } + + [Fact] + public void DefaultObjectPool_Return_RejectedByPolicy() + { + // Arrange + var pool = new DefaultObjectPool>(new ListPolicy()); + var list1 = pool.Get(); + list1.Capacity = 20; + + // Act + pool.Return(list1); + var list2 = pool.Get(); + + // Assert + Assert.NotSame(list1, list2); + } + + private class ListPolicy : IPooledObjectPolicy> + { + public List Create() + { + return new List(17); + } + + public bool Return(List obj) + { + return obj.Capacity == 17; + } + } + } +} diff --git a/src/ObjectPool/test/Microsoft.Extensions.ObjectPool.Tests.csproj b/src/ObjectPool/test/Microsoft.Extensions.ObjectPool.Tests.csproj new file mode 100644 index 000000000000..b922cc227e33 --- /dev/null +++ b/src/ObjectPool/test/Microsoft.Extensions.ObjectPool.Tests.csproj @@ -0,0 +1,11 @@ + + + + $(StandardTestTfms) + + + + + + + diff --git a/src/Shared/BenchmarkRunner/AspNetCoreBenchmarkAttribute.cs b/src/Shared/BenchmarkRunner/AspNetCoreBenchmarkAttribute.cs new file mode 100644 index 000000000000..a4044d1b5e84 --- /dev/null +++ b/src/Shared/BenchmarkRunner/AspNetCoreBenchmarkAttribute.cs @@ -0,0 +1,39 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Linq; +using System.Reflection; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Toolchains.InProcess; + +namespace BenchmarkDotNet.Attributes +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Assembly)] + internal class AspNetCoreBenchmarkAttribute : Attribute, IConfigSource + { + public static bool UseValidationConfig { get; set; } + + public Type ConfigType { get; } + public Type ValidationConfigType { get; } + + public AspNetCoreBenchmarkAttribute() : this(typeof(DefaultCoreConfig)) + { + } + + public AspNetCoreBenchmarkAttribute(Type configType) : this(configType, typeof(DefaultCoreValidationConfig)) + { + } + + public AspNetCoreBenchmarkAttribute(Type configType, Type validationConfigType) + { + ConfigType = configType; + ValidationConfigType = validationConfigType; + } + + public IConfig Config => (IConfig) Activator.CreateInstance(UseValidationConfig ? ValidationConfigType : ConfigType, Array.Empty()); + } +} diff --git a/src/Shared/BenchmarkRunner/DefaultCoreConfig.cs b/src/Shared/BenchmarkRunner/DefaultCoreConfig.cs new file mode 100644 index 000000000000..a8d9d6053646 --- /dev/null +++ b/src/Shared/BenchmarkRunner/DefaultCoreConfig.cs @@ -0,0 +1,36 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using BenchmarkDotNet.Columns; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Diagnosers; +using BenchmarkDotNet.Engines; +using BenchmarkDotNet.Exporters; +using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Loggers; +using BenchmarkDotNet.Toolchains.CsProj; +using BenchmarkDotNet.Toolchains.DotNetCli; +using BenchmarkDotNet.Validators; + +namespace BenchmarkDotNet.Attributes +{ + internal class DefaultCoreConfig : ManualConfig + { + public DefaultCoreConfig() + { + Add(ConsoleLogger.Default); + Add(MarkdownExporter.GitHub); + + Add(MemoryDiagnoser.Default); + Add(StatisticColumn.OperationsPerSecond); + Add(DefaultColumnProviders.Instance); + + Add(JitOptimizationsValidator.FailOnError); + + Add(Job.Core + .With(CsProjCoreToolchain.From(NetCoreAppSettings.NetCoreApp21)) + .With(new GcMode { Server = true }) + .With(RunStrategy.Throughput)); + } + } +} diff --git a/src/Shared/BenchmarkRunner/DefaultCoreValidationConfig.cs b/src/Shared/BenchmarkRunner/DefaultCoreValidationConfig.cs new file mode 100644 index 000000000000..95fc725564db --- /dev/null +++ b/src/Shared/BenchmarkRunner/DefaultCoreValidationConfig.cs @@ -0,0 +1,25 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Linq; +using System.Reflection; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Loggers; +using BenchmarkDotNet.Toolchains.InProcess; + +namespace BenchmarkDotNet.Attributes +{ + internal class DefaultCoreValidationConfig : ManualConfig + { + public DefaultCoreValidationConfig() + { + Add(ConsoleLogger.Default); + + Add(Job.Dry.With(InProcessToolchain.Instance)); + } + } +} diff --git a/src/Shared/BenchmarkRunner/Directory.Build.props b/src/Shared/BenchmarkRunner/Directory.Build.props new file mode 100644 index 000000000000..34cf593291de --- /dev/null +++ b/src/Shared/BenchmarkRunner/Directory.Build.props @@ -0,0 +1,8 @@ + + + + + + Microsoft.Extensions.$(ProjectDirName).Sources + + diff --git a/src/Shared/BenchmarkRunner/ParameterizedJobConfigAttribute.cs b/src/Shared/BenchmarkRunner/ParameterizedJobConfigAttribute.cs new file mode 100644 index 000000000000..9e0f947dc75d --- /dev/null +++ b/src/Shared/BenchmarkRunner/ParameterizedJobConfigAttribute.cs @@ -0,0 +1,15 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace BenchmarkDotNet.Attributes +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Assembly)] + internal class ParameterizedJobConfigAttribute: AspNetCoreBenchmarkAttribute + { + public ParameterizedJobConfigAttribute(Type configType) : base(configType) + { + } + } +} diff --git a/src/Shared/BenchmarkRunner/Program.cs b/src/Shared/BenchmarkRunner/Program.cs new file mode 100644 index 000000000000..3297d5dae98e --- /dev/null +++ b/src/Shared/BenchmarkRunner/Program.cs @@ -0,0 +1,88 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Toolchains.InProcess; + +namespace Microsoft.AspNetCore.BenchmarkDotNet.Runner +{ + partial class Program + { + private static TextWriter _standardOutput; + private static StringBuilder _standardOutputText; + + static partial void BeforeMain(string[] args); + + private static int Main(string[] args) + { + BeforeMain(args); + + CheckValidate(ref args); + var summaries = BenchmarkSwitcher.FromAssembly(typeof(Program).GetTypeInfo().Assembly) + .Run(args, ManualConfig.CreateEmpty()); + + foreach (var summary in summaries) + { + if (summary.HasCriticalValidationErrors) + { + return Fail(summary, nameof(summary.HasCriticalValidationErrors)); + } + + foreach (var report in summary.Reports) + { + if (!report.BuildResult.IsGenerateSuccess) + { + return Fail(report, nameof(report.BuildResult.IsGenerateSuccess)); + } + + if (!report.BuildResult.IsBuildSuccess) + { + return Fail(report, nameof(report.BuildResult.IsBuildSuccess)); + } + + if (!report.AllMeasurements.Any()) + { + return Fail(report, nameof(report.AllMeasurements)); + } + } + } + + return 0; + } + + private static int Fail(object o, string message) + { + _standardOutput?.WriteLine(_standardOutputText.ToString()); + + Console.Error.WriteLine("'{0}' failed, reason: '{1}'", o, message); + return 1; + } + + private static void CheckValidate(ref string[] args) + { + var argsList = args.ToList(); + if (argsList.Remove("--validate") || argsList.Remove("--validate-fast")) + { + SuppressConsole(); + AspNetCoreBenchmarkAttribute.UseValidationConfig = true; + } + + args = argsList.ToArray(); + } + + private static void SuppressConsole() + { + _standardOutput = Console.Out; + _standardOutputText = new StringBuilder(); + Console.SetOut(new StringWriter(_standardOutputText)); + } + } +} diff --git a/src/Shared/CertificateGeneration/CertificateManager.cs b/src/Shared/CertificateGeneration/CertificateManager.cs new file mode 100644 index 000000000000..4e2a0a99643f --- /dev/null +++ b/src/Shared/CertificateGeneration/CertificateManager.cs @@ -0,0 +1,720 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Text.RegularExpressions; + +namespace Microsoft.AspNetCore.Certificates.Generation +{ + internal class CertificateManager + { + public const string AspNetHttpsOid = "1.3.6.1.4.1.311.84.1.1"; + public const string AspNetHttpsOidFriendlyName = "ASP.NET Core HTTPS development certificate"; + + public const string AspNetIdentityOid = "1.3.6.1.4.1.311.84.1.2"; + public const string AspNetIdentityOidFriendlyName = "ASP.NET Core Identity Json Web Token signing development certificate"; + + private const string ServerAuthenticationEnhancedKeyUsageOid = "1.3.6.1.5.5.7.3.1"; + private const string ServerAuthenticationEnhancedKeyUsageOidFriendlyName = "Server Authentication"; + + private const string LocalhostHttpsDnsName = "localhost"; + private const string LocalhostHttpsDistinguishedName = "CN=" + LocalhostHttpsDnsName; + + private const string IdentityDistinguishedName = "CN=Microsoft.AspNetCore.Identity.Signing"; + + public const int RSAMinimumKeySizeInBits = 2048; + + private static readonly TimeSpan MaxRegexTimeout = TimeSpan.FromMinutes(1); + private const string CertificateSubjectRegex = "CN=(.*[^,]+).*"; + private const string MacOSSystemKeyChain = "/Library/Keychains/System.keychain"; + private static readonly string MacOSUserKeyChain = Environment.GetEnvironmentVariable("HOME") + "/Library/Keychains/login.keychain-db"; + private const string MacOSFindCertificateCommandLine = "security"; +#if NETCOREAPP2_0 || NETCOREAPP2_1 + private static readonly string MacOSFindCertificateCommandLineArgumentsFormat = "find-certificate -c {0} -a -Z -p " + MacOSSystemKeyChain; +#endif + private const string MacOSFindCertificateOutputRegex = "SHA-1 hash: ([0-9A-Z]+)"; + private const string MacOSRemoveCertificateTrustCommandLine = "sudo"; + private const string MacOSRemoveCertificateTrustCommandLineArgumentsFormat = "security remove-trusted-cert -d {0}"; + private const string MacOSDeleteCertificateCommandLine = "sudo"; + private const string MacOSDeleteCertificateCommandLineArgumentsFormat = "security delete-certificate -Z {0} {1}"; + private const string MacOSTrustCertificateCommandLine = "sudo"; +#if NETCOREAPP2_0 || NETCOREAPP2_1 + private static readonly string MacOSTrustCertificateCommandLineArguments = "security add-trusted-cert -d -r trustRoot -k " + MacOSSystemKeyChain + " "; +#endif + private const int UserCancelledErrorCode = 1223; + + public IList ListCertificates( + CertificatePurpose purpose, + StoreName storeName, + StoreLocation location, + bool isValid, + bool requireExportable = true) + { + var certificates = new List(); + try + { + using (var store = new X509Store(storeName, location)) + { + store.Open(OpenFlags.ReadOnly); + certificates.AddRange(store.Certificates.OfType()); + IEnumerable matchingCertificates = certificates; + switch (purpose) + { + case CertificatePurpose.All: + matchingCertificates = matchingCertificates + .Where(c => HasOid(c, AspNetHttpsOid) || HasOid(c, AspNetIdentityOid)); + break; + case CertificatePurpose.HTTPS: + matchingCertificates = matchingCertificates + .Where(c => HasOid(c, AspNetHttpsOid)); + break; + case CertificatePurpose.Signing: + matchingCertificates = matchingCertificates + .Where(c => HasOid(c, AspNetIdentityOid)); + break; + default: + break; + } + if (isValid) + { + // Ensure the certificate hasn't expired, has a private key and its exportable + // (for container/unix scenarios). + var now = DateTimeOffset.Now; + matchingCertificates = matchingCertificates + .Where(c => c.NotBefore <= now && + now <= c.NotAfter && + (!requireExportable || !RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || IsExportable(c))); + } + + // We need to enumerate the certificates early to prevent dispoisng issues. + matchingCertificates = matchingCertificates.ToList(); + + var certificatesToDispose = certificates.Except(matchingCertificates); + DisposeCertificates(certificatesToDispose); + + store.Close(); + + return (IList)matchingCertificates; + } + } + catch + { + DisposeCertificates(certificates); + certificates.Clear(); + return certificates; + } + + bool HasOid(X509Certificate2 certificate, string oid) => + certificate.Extensions.OfType() + .Any(e => string.Equals(oid, e.Oid.Value, StringComparison.Ordinal)); +#if !XPLAT + bool IsExportable(X509Certificate2 c) => + ((c.GetRSAPrivateKey() is RSACryptoServiceProvider rsaPrivateKey && + rsaPrivateKey.CspKeyContainerInfo.Exportable) || + (c.GetRSAPrivateKey() is RSACng cngPrivateKey && + cngPrivateKey.Key.ExportPolicy == CngExportPolicies.AllowExport)); +#else + // Only check for RSA CryptoServiceProvider and do not fail in XPlat tooling as + // System.Security.Cryptography.Cng is not pat of the shared framework and we don't + // want to bring the dependency in on CLI scenarios. This functionality will be used + // on CLI scenarios as part of the first run experience, so checking the exportability + // of the certificate is not important. + bool IsExportable(X509Certificate2 c) => + ((c.GetRSAPrivateKey() is RSACryptoServiceProvider rsaPrivateKey && + rsaPrivateKey.CspKeyContainerInfo.Exportable) || !(c.GetRSAPrivateKey() is RSACryptoServiceProvider)); +#endif + } + + private void DisposeCertificates(IEnumerable disposables) + { + foreach (var disposable in disposables) + { + try + { + disposable.Dispose(); + } + catch + { + } + } + } + +#if NETCOREAPP2_0 || NETCOREAPP2_1 + + public X509Certificate2 CreateAspNetCoreHttpsDevelopmentCertificate(DateTimeOffset notBefore, DateTimeOffset notAfter, string subjectOverride) + { + var subject = new X500DistinguishedName(subjectOverride ?? LocalhostHttpsDistinguishedName); + var extensions = new List(); + var sanBuilder = new SubjectAlternativeNameBuilder(); + sanBuilder.AddDnsName(LocalhostHttpsDnsName); + + var keyUsage = new X509KeyUsageExtension(X509KeyUsageFlags.KeyEncipherment, critical: true); + var enhancedKeyUsage = new X509EnhancedKeyUsageExtension( + new OidCollection() { + new Oid( + ServerAuthenticationEnhancedKeyUsageOid, + ServerAuthenticationEnhancedKeyUsageOidFriendlyName) + }, + critical: true); + + var basicConstraints = new X509BasicConstraintsExtension( + certificateAuthority: false, + hasPathLengthConstraint: false, + pathLengthConstraint: 0, + critical: true); + + var aspNetHttpsExtension = new X509Extension( + new AsnEncodedData( + new Oid(AspNetHttpsOid, AspNetHttpsOidFriendlyName), + Encoding.ASCII.GetBytes(AspNetHttpsOidFriendlyName)), + critical: false); + + extensions.Add(basicConstraints); + extensions.Add(keyUsage); + extensions.Add(enhancedKeyUsage); + extensions.Add(sanBuilder.Build(critical: true)); + extensions.Add(aspNetHttpsExtension); + + var certificate = CreateSelfSignedCertificate(subject, extensions, notBefore, notAfter); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + certificate.FriendlyName = AspNetHttpsOidFriendlyName; + } + + return certificate; + } + + public X509Certificate2 CreateApplicationTokenSigningDevelopmentCertificate(DateTimeOffset notBefore, DateTimeOffset notAfter, string subjectOverride) + { + var subject = new X500DistinguishedName(subjectOverride ?? IdentityDistinguishedName); + var extensions = new List(); + + var keyUsage = new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature, critical: true); + var enhancedKeyUsage = new X509EnhancedKeyUsageExtension( + new OidCollection() { + new Oid( + ServerAuthenticationEnhancedKeyUsageOid, + ServerAuthenticationEnhancedKeyUsageOidFriendlyName) + }, + critical: true); + + var basicConstraints = new X509BasicConstraintsExtension( + certificateAuthority: false, + hasPathLengthConstraint: false, + pathLengthConstraint: 0, + critical: true); + + var aspNetIdentityExtension = new X509Extension( + new AsnEncodedData( + new Oid(AspNetIdentityOid, AspNetIdentityOidFriendlyName), + Encoding.ASCII.GetBytes(AspNetIdentityOidFriendlyName)), + critical: false); + + extensions.Add(basicConstraints); + extensions.Add(keyUsage); + extensions.Add(enhancedKeyUsage); + extensions.Add(aspNetIdentityExtension); + + var certificate = CreateSelfSignedCertificate(subject, extensions, notBefore, notAfter); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + certificate.FriendlyName = AspNetIdentityOidFriendlyName; + } + + return certificate; + } + + public X509Certificate2 CreateSelfSignedCertificate( + X500DistinguishedName subject, + IEnumerable extensions, + DateTimeOffset notBefore, + DateTimeOffset notAfter) + { + var key = CreateKeyMaterial(RSAMinimumKeySizeInBits); + + var request = new CertificateRequest(subject, key, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + foreach (var extension in extensions) + { + request.CertificateExtensions.Add(extension); + } + + return request.CreateSelfSigned(notBefore, notAfter); + + RSA CreateKeyMaterial(int minimumKeySize) + { + var rsa = RSA.Create(minimumKeySize); + if (rsa.KeySize < minimumKeySize) + { + throw new InvalidOperationException($"Failed to create a key with a size of {minimumKeySize} bits"); + } + + return rsa; + } + } + + public X509Certificate2 SaveCertificateInStore(X509Certificate2 certificate, StoreName name, StoreLocation location) + { + var imported = certificate; + if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + // On non OSX systems we need to export the certificate and import it so that the transient + // key that we generated gets persisted. + var export = certificate.Export(X509ContentType.Pkcs12, ""); + imported = new X509Certificate2(export, "", X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable); + Array.Clear(export, 0, export.Length); + } + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + imported.FriendlyName = certificate.FriendlyName; + } + + using (var store = new X509Store(name, location)) + { + store.Open(OpenFlags.ReadWrite); + store.Add(imported); + store.Close(); + }; + + return imported; + } + + public void ExportCertificate(X509Certificate2 certificate, string path, bool includePrivateKey, string password) + { + if (Path.GetDirectoryName(path) != "") + { + Directory.CreateDirectory(Path.GetDirectoryName(path)); + } + + if (includePrivateKey) + { + var bytes = certificate.Export(X509ContentType.Pkcs12, password); + try + { + File.WriteAllBytes(path, bytes); + } + finally + { + Array.Clear(bytes, 0, bytes.Length); + } + } + else + { + var bytes = certificate.Export(X509ContentType.Cert); + File.WriteAllBytes(path, bytes); + } + } + + public void TrustCertificate(X509Certificate2 certificate) + { + // Strip certificate of the private key if any. + var publicCertificate = new X509Certificate2(certificate.Export(X509ContentType.Cert)); + + if (!IsTrusted(publicCertificate)) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + TrustCertificateOnWindows(certificate, publicCertificate); + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + TrustCertificateOnMac(publicCertificate); + } + } + } + + private void TrustCertificateOnMac(X509Certificate2 publicCertificate) + { + var tmpFile = Path.GetTempFileName(); + try + { + ExportCertificate(publicCertificate, tmpFile, includePrivateKey: false, password: null); + using (var process = Process.Start(MacOSTrustCertificateCommandLine, MacOSTrustCertificateCommandLineArguments + tmpFile)) + { + process.WaitForExit(); + if (process.ExitCode != 0) + { + throw new InvalidOperationException("There was an error trusting the certificate."); + } + } + } + finally + { + try + { + if (File.Exists(tmpFile)) + { + File.Delete(tmpFile); + } + } + catch + { + // We don't care if we can't delete the temp file. + } + } + } + + private static void TrustCertificateOnWindows(X509Certificate2 certificate, X509Certificate2 publicCertificate) + { + publicCertificate.FriendlyName = certificate.FriendlyName; + + using (var store = new X509Store(StoreName.Root, StoreLocation.CurrentUser)) + { + store.Open(OpenFlags.ReadWrite); + try + { + store.Add(publicCertificate); + } + catch (CryptographicException exception) when (exception.HResult == UserCancelledErrorCode) + { + throw new UserCancelledTrustException(); + } + store.Close(); + }; + } + + public bool IsTrusted(X509Certificate2 certificate) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return ListCertificates(CertificatePurpose.HTTPS, StoreName.Root, StoreLocation.CurrentUser, isValid: true, requireExportable: false) + .Any(c => c.Thumbprint == certificate.Thumbprint); + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + var subjectMatch = Regex.Match(certificate.Subject, CertificateSubjectRegex, RegexOptions.Singleline, MaxRegexTimeout); + if (!subjectMatch.Success) + { + throw new InvalidOperationException($"Can't determine the subject for the certificate with subject '{certificate.Subject}'."); + } + var subject = subjectMatch.Groups[1].Value; + using (var checkTrustProcess = Process.Start(new ProcessStartInfo( + MacOSFindCertificateCommandLine, + string.Format(MacOSFindCertificateCommandLineArgumentsFormat, subject)) + { + RedirectStandardOutput = true + })) + { + var output = checkTrustProcess.StandardOutput.ReadToEnd(); + checkTrustProcess.WaitForExit(); + var matches = Regex.Matches(output, MacOSFindCertificateOutputRegex, RegexOptions.Multiline, MaxRegexTimeout); + var hashes = matches.OfType().Select(m => m.Groups[1].Value).ToList(); + return hashes.Any(h => string.Equals(h, certificate.Thumbprint, StringComparison.Ordinal)); + } + } + else + { + return false; + } + } + + public void CleanupHttpsCertificates(string subject = LocalhostHttpsDistinguishedName) + { + CleanupCertificates(CertificatePurpose.HTTPS, subject); + } + + public void CleanupCertificates(CertificatePurpose purpose, string subject) + { + // On OS X we don't have a good way to manage trusted certificates in the system keychain + // so we do everything by invoking the native toolchain. + // This has some limitations, like for example not being able to identify our custom OID extension. For that + // matter, when we are cleaning up certificates on the machine, we start by removing the trusted certificates. + // To do this, we list the certificates that we can identify on the current user personal store and we invoke + // the native toolchain to remove them from the sytem keychain. Once we have removed the trusted certificates, + // we remove the certificates from the local user store to finish up the cleanup. + var certificates = ListCertificates(purpose, StoreName.My, StoreLocation.CurrentUser, isValid: false); + foreach (var certificate in certificates) + { + RemoveCertificate(certificate, RemoveLocations.All); + } + } + + public void RemoveAllCertificates(CertificatePurpose purpose, StoreName storeName, StoreLocation storeLocation, string subject = null) + { + var certificates = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? + ListCertificates(purpose, StoreName.My, StoreLocation.CurrentUser, isValid: false) : + ListCertificates(purpose, storeName, storeLocation, isValid: false); + var certificatesWithName = subject == null ? certificates : certificates.Where(c => c.Subject == subject); + + var removeLocation = storeName == StoreName.My ? RemoveLocations.Local : RemoveLocations.Trusted; + + foreach (var certificate in certificates) + { + RemoveCertificate(certificate, removeLocation); + } + + DisposeCertificates(certificates); + } + + private void RemoveCertificate(X509Certificate2 certificate, RemoveLocations locations) + { + switch (locations) + { + case RemoveLocations.Undefined: + throw new InvalidOperationException($"'{nameof(RemoveLocations.Undefined)}' is not a valid location."); + case RemoveLocations.Local: + RemoveCertificateFromUserStore(certificate); + break; + case RemoveLocations.Trusted when !RuntimeInformation.IsOSPlatform(OSPlatform.Linux): + RemoveCertificateFromTrustedRoots(certificate); + break; + case RemoveLocations.All: + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + RemoveCertificateFromTrustedRoots(certificate); + } + RemoveCertificateFromUserStore(certificate); + break; + default: + throw new InvalidOperationException("Invalid location."); + } + } + + private static void RemoveCertificateFromUserStore(X509Certificate2 certificate) + { + using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser)) + { + store.Open(OpenFlags.ReadWrite); + var matching = store.Certificates + .OfType() + .Single(c => c.SerialNumber == certificate.SerialNumber); + + store.Remove(matching); + store.Close(); + } + } + + private void RemoveCertificateFromTrustedRoots(X509Certificate2 certificate) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + using (var store = new X509Store(StoreName.Root, StoreLocation.CurrentUser)) + { + store.Open(OpenFlags.ReadWrite); + var matching = store.Certificates + .OfType() + .Single(c => c.SerialNumber == certificate.SerialNumber); + + store.Remove(matching); + store.Close(); + } + } + else + { + if (IsTrusted(certificate)) // On OSX this check just ensures its on the system keychain + { + try + { + RemoveCertificateTrustRule(certificate); + } + catch + { + // We don't care if we fail to remove the trust rule if + // for some reason the certificate became untrusted. + // The delete command will fail if the certificate is + // trusted. + } + RemoveCertificateFromKeyChain(MacOSSystemKeyChain, certificate); + } + } + } + + private static void RemoveCertificateTrustRule(X509Certificate2 certificate) + { + var certificatePath = Path.GetTempFileName(); + try + { + var certBytes = certificate.Export(X509ContentType.Cert); + File.WriteAllBytes(certificatePath, certBytes); + var processInfo = new ProcessStartInfo( + MacOSRemoveCertificateTrustCommandLine, + string.Format( + MacOSRemoveCertificateTrustCommandLineArgumentsFormat, + certificatePath + )); + using (var process = Process.Start(processInfo)) + { + process.WaitForExit(); + } + } + finally + { + try + { + if (File.Exists(certificatePath)) + { + File.Delete(certificatePath); + } + } + catch + { + // We don't care about failing to do clean-up on a temp file. + } + } + } + + private static void RemoveCertificateFromKeyChain(string keyChain, X509Certificate2 certificate) + { + var processInfo = new ProcessStartInfo( + MacOSDeleteCertificateCommandLine, + string.Format( + MacOSDeleteCertificateCommandLineArgumentsFormat, + certificate.Thumbprint.ToUpperInvariant(), + keyChain + )) + { + RedirectStandardOutput = true, + RedirectStandardError = true + }; + + using (var process = Process.Start(processInfo)) + { + var output = process.StandardOutput.ReadToEnd() + process.StandardError.ReadToEnd(); + process.WaitForExit(); + + if (process.ExitCode != 0) + { + throw new InvalidOperationException($@"There was an error removing the certificate with thumbprint '{certificate.Thumbprint}'. + +{output}"); + } + } + } + + public EnsureCertificateResult EnsureAspNetCoreHttpsDevelopmentCertificate( + DateTimeOffset notBefore, + DateTimeOffset notAfter, + string path = null, + bool trust = false, + bool includePrivateKey = false, + string password = null, + string subject = LocalhostHttpsDistinguishedName) + { + return EnsureValidCertificateExists(notBefore, notAfter, CertificatePurpose.HTTPS, path, trust, includePrivateKey, password, subject); + } + + public EnsureCertificateResult EnsureAspNetCoreApplicationTokensDevelopmentCertificate( + DateTimeOffset notBefore, + DateTimeOffset notAfter, + string path = null, + bool trust = false, + bool includePrivateKey = false, + string password = null, + string subject = IdentityDistinguishedName) + { + return EnsureValidCertificateExists(notBefore, notAfter, CertificatePurpose.Signing, path, trust, includePrivateKey, password, subject); + } + + public EnsureCertificateResult EnsureValidCertificateExists( + DateTimeOffset notBefore, + DateTimeOffset notAfter, + CertificatePurpose purpose, + string path = null, + bool trust = false, + bool includePrivateKey = false, + string password = null, + string subjectOverride = null) + { + if (purpose == CertificatePurpose.All) + { + throw new ArgumentException("The certificate must have a specific purpose."); + } + + var certificates = ListCertificates(purpose, StoreName.My, StoreLocation.CurrentUser, isValid: true).Concat( + ListCertificates(purpose, StoreName.My, StoreLocation.LocalMachine, isValid: true)); + + certificates = subjectOverride == null ? certificates : certificates.Where(c => c.Subject == subjectOverride); + + var result = EnsureCertificateResult.Succeeded; + + X509Certificate2 certificate = null; + if (certificates.Count() > 0) + { + certificate = certificates.FirstOrDefault(); + result = EnsureCertificateResult.ValidCertificatePresent; + } + else + { + try + { + switch (purpose) + { + case CertificatePurpose.All: + throw new InvalidOperationException("The certificate must have a specific purpose."); + case CertificatePurpose.HTTPS: + certificate = CreateAspNetCoreHttpsDevelopmentCertificate(notBefore, notAfter, subjectOverride); + break; + case CertificatePurpose.Signing: + certificate = CreateApplicationTokenSigningDevelopmentCertificate(notBefore, notAfter, subjectOverride); + break; + default: + throw new InvalidOperationException("The certificate must have a purpose."); + } + } + catch + { + return EnsureCertificateResult.ErrorCreatingTheCertificate; + } + + try + { + certificate = SaveCertificateInStore(certificate, StoreName.My, StoreLocation.CurrentUser); + } + catch + { + return EnsureCertificateResult.ErrorSavingTheCertificateIntoTheCurrentUserPersonalStore; + } + } + if (path != null) + { + try + { + ExportCertificate(certificate, path, includePrivateKey, password); + } + catch + { + return EnsureCertificateResult.ErrorExportingTheCertificate; + } + } + + if ((RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) && trust) + { + try + { + TrustCertificate(certificate); + } + catch (UserCancelledTrustException) + { + return EnsureCertificateResult.UserCancelledTrustStep; + } + catch + { + return EnsureCertificateResult.FailedToTrustTheCertificate; + } + } + + return result; + } + + private class UserCancelledTrustException : Exception + { + } + + private enum RemoveLocations + { + Undefined, + Local, + Trusted, + All + } +#endif + } +} \ No newline at end of file diff --git a/src/Shared/CertificateGeneration/CertificatePurpose.cs b/src/Shared/CertificateGeneration/CertificatePurpose.cs new file mode 100644 index 000000000000..1ad1a6d79b65 --- /dev/null +++ b/src/Shared/CertificateGeneration/CertificatePurpose.cs @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNetCore.Certificates.Generation +{ + internal enum CertificatePurpose + { + All, + HTTPS, + Signing + } +} \ No newline at end of file diff --git a/src/Shared/CertificateGeneration/Directory.Build.props b/src/Shared/CertificateGeneration/Directory.Build.props new file mode 100644 index 000000000000..63355f6faf94 --- /dev/null +++ b/src/Shared/CertificateGeneration/Directory.Build.props @@ -0,0 +1,8 @@ + + + + + + Microsoft.AspNetCore.Certificates.Generation.Sources + + diff --git a/src/Shared/CertificateGeneration/EnsureCertificateResult.cs b/src/Shared/CertificateGeneration/EnsureCertificateResult.cs new file mode 100644 index 000000000000..d3c86ce05d08 --- /dev/null +++ b/src/Shared/CertificateGeneration/EnsureCertificateResult.cs @@ -0,0 +1,20 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#if NETCOREAPP2_0 || NETCOREAPP2_1 + +namespace Microsoft.AspNetCore.Certificates.Generation +{ + internal enum EnsureCertificateResult + { + Succeeded = 1, + ValidCertificatePresent, + ErrorCreatingTheCertificate, + ErrorSavingTheCertificateIntoTheCurrentUserPersonalStore, + ErrorExportingTheCertificate, + FailedToTrustTheCertificate, + UserCancelledTrustStep + } +} + +#endif \ No newline at end of file diff --git a/src/Shared/ClosedGenericMatcher/ClosedGenericMatcher.cs b/src/Shared/ClosedGenericMatcher/ClosedGenericMatcher.cs new file mode 100644 index 000000000000..f234c2edbc40 --- /dev/null +++ b/src/Shared/ClosedGenericMatcher/ClosedGenericMatcher.cs @@ -0,0 +1,106 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Linq; +using System.Reflection; + +namespace Microsoft.Extensions.Internal +{ + /// + /// Helper related to generic interface definitions and implementing classes. + /// + internal static class ClosedGenericMatcher + { + /// + /// Determine whether is or implements a closed generic + /// created from . + /// + /// The of interest. + /// The open generic to match. Usually an interface. + /// + /// The closed generic created from that + /// is or implements. null if the two s have no such + /// relationship. + /// + /// + /// This method will return if is + /// typeof(KeyValuePair{,}), and is + /// typeof(KeyValuePair{string, object}). + /// + public static Type ExtractGenericInterface(Type queryType, Type interfaceType) + { + if (queryType == null) + { + throw new ArgumentNullException(nameof(queryType)); + } + + if (interfaceType == null) + { + throw new ArgumentNullException(nameof(interfaceType)); + } + + if (IsGenericInstantiation(queryType, interfaceType)) + { + // queryType matches (i.e. is a closed generic type created from) the open generic type. + return queryType; + } + + // Otherwise check all interfaces the type implements for a match. + // - If multiple different generic instantiations exists, we want the most derived one. + // - If that doesn't break the tie, then we sort alphabetically so that it's deterministic. + // + // We do this by looking at interfaces on the type, and recursing to the base type + // if we don't find any matches. + return GetGenericInstantiation(queryType, interfaceType); + } + + private static bool IsGenericInstantiation(Type candidate, Type interfaceType) + { + return + candidate.GetTypeInfo().IsGenericType && + candidate.GetGenericTypeDefinition() == interfaceType; + } + + private static Type GetGenericInstantiation(Type queryType, Type interfaceType) + { + Type bestMatch = null; + var interfaces = queryType.GetInterfaces(); + foreach (var @interface in interfaces) + { + if (IsGenericInstantiation(@interface, interfaceType)) + { + if (bestMatch == null) + { + bestMatch = @interface; + } + else if (StringComparer.Ordinal.Compare(@interface.FullName, bestMatch.FullName) < 0) + { + bestMatch = @interface; + } + else + { + // There are two matches at this level of the class hierarchy, but @interface is after + // bestMatch in the sort order. + } + } + } + + if (bestMatch != null) + { + return bestMatch; + } + + // BaseType will be null for object and interfaces, which means we've reached 'bottom'. + var baseType = queryType?.GetTypeInfo().BaseType; + if (baseType == null) + { + return null; + } + else + { + return GetGenericInstantiation(baseType, interfaceType); + } + } + } +} \ No newline at end of file diff --git a/src/Shared/CommandLineUtils/CommandLine/AnsiConsole.cs b/src/Shared/CommandLineUtils/CommandLine/AnsiConsole.cs new file mode 100644 index 000000000000..379235f27425 --- /dev/null +++ b/src/Shared/CommandLineUtils/CommandLine/AnsiConsole.cs @@ -0,0 +1,143 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; + +namespace Microsoft.Extensions.CommandLineUtils +{ + internal class AnsiConsole + { + private AnsiConsole(TextWriter writer, bool useConsoleColor) + { + Writer = writer; + + _useConsoleColor = useConsoleColor; + if (_useConsoleColor) + { + OriginalForegroundColor = Console.ForegroundColor; + } + } + + private int _boldRecursion; + private bool _useConsoleColor; + + public static AnsiConsole GetOutput(bool useConsoleColor) + { + return new AnsiConsole(Console.Out, useConsoleColor); + } + + public static AnsiConsole GetError(bool useConsoleColor) + { + return new AnsiConsole(Console.Error, useConsoleColor); + } + + public TextWriter Writer { get; } + + public ConsoleColor OriginalForegroundColor { get; } + + private void SetColor(ConsoleColor color) + { + Console.ForegroundColor = (ConsoleColor)(((int)Console.ForegroundColor & 0x08) | ((int)color & 0x07)); + } + + private void SetBold(bool bold) + { + _boldRecursion += bold ? 1 : -1; + if (_boldRecursion > 1 || (_boldRecursion == 1 && !bold)) + { + return; + } + + Console.ForegroundColor = (ConsoleColor)((int)Console.ForegroundColor ^ 0x08); + } + + public void WriteLine(string message) + { + if (!_useConsoleColor) + { + Writer.WriteLine(message); + return; + } + + var escapeScan = 0; + for (; ;) + { + var escapeIndex = message.IndexOf("\x1b[", escapeScan); + if (escapeIndex == -1) + { + var text = message.Substring(escapeScan); + Writer.Write(text); + break; + } + else + { + var startIndex = escapeIndex + 2; + var endIndex = startIndex; + while (endIndex != message.Length && + message[endIndex] >= 0x20 && + message[endIndex] <= 0x3f) + { + endIndex += 1; + } + + var text = message.Substring(escapeScan, escapeIndex - escapeScan); + Writer.Write(text); + if (endIndex == message.Length) + { + break; + } + + switch (message[endIndex]) + { + case 'm': + int value; + if (int.TryParse(message.Substring(startIndex, endIndex - startIndex), out value)) + { + switch (value) + { + case 1: + SetBold(true); + break; + case 22: + SetBold(false); + break; + case 30: + SetColor(ConsoleColor.Black); + break; + case 31: + SetColor(ConsoleColor.Red); + break; + case 32: + SetColor(ConsoleColor.Green); + break; + case 33: + SetColor(ConsoleColor.Yellow); + break; + case 34: + SetColor(ConsoleColor.Blue); + break; + case 35: + SetColor(ConsoleColor.Magenta); + break; + case 36: + SetColor(ConsoleColor.Cyan); + break; + case 37: + SetColor(ConsoleColor.Gray); + break; + case 39: + SetColor(OriginalForegroundColor); + break; + } + } + break; + } + + escapeScan = endIndex + 1; + } + } + Writer.WriteLine(); + } + } +} diff --git a/src/Shared/CommandLineUtils/CommandLine/CommandArgument.cs b/src/Shared/CommandLineUtils/CommandLine/CommandArgument.cs new file mode 100644 index 000000000000..4eac95982c20 --- /dev/null +++ b/src/Shared/CommandLineUtils/CommandLine/CommandArgument.cs @@ -0,0 +1,29 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.Extensions.CommandLineUtils +{ + internal class CommandArgument + { + public CommandArgument() + { + Values = new List(); + } + + public string Name { get; set; } + public bool ShowInHelpText { get; set; } = true; + public string Description { get; set; } + public List Values { get; private set; } + public bool MultipleValues { get; set; } + public string Value + { + get + { + return Values.FirstOrDefault(); + } + } + } +} diff --git a/src/Shared/CommandLineUtils/CommandLine/CommandLineApplication.cs b/src/Shared/CommandLineUtils/CommandLine/CommandLineApplication.cs new file mode 100644 index 000000000000..b7c62b5a0bea --- /dev/null +++ b/src/Shared/CommandLineUtils/CommandLine/CommandLineApplication.cs @@ -0,0 +1,555 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.Extensions.CommandLineUtils +{ + internal class CommandLineApplication + { + // Indicates whether the parser should throw an exception when it runs into an unexpected argument. + // If this field is set to false, the parser will stop parsing when it sees an unexpected argument, and all + // remaining arguments, including the first unexpected argument, will be stored in RemainingArguments property. + private readonly bool _throwOnUnexpectedArg; + + public CommandLineApplication(bool throwOnUnexpectedArg = true) + { + _throwOnUnexpectedArg = throwOnUnexpectedArg; + Options = new List(); + Arguments = new List(); + Commands = new List(); + RemainingArguments = new List(); + Invoke = () => 0; + } + + public CommandLineApplication Parent { get; set; } + public string Name { get; set; } + public string FullName { get; set; } + public string Syntax { get; set; } + public string Description { get; set; } + public bool ShowInHelpText { get; set; } = true; + public string ExtendedHelpText { get; set; } + public readonly List Options; + public CommandOption OptionHelp { get; private set; } + public CommandOption OptionVersion { get; private set; } + public readonly List Arguments; + public readonly List RemainingArguments; + public bool IsShowingInformation { get; protected set; } // Is showing help or version? + public Func Invoke { get; set; } + public Func LongVersionGetter { get; set; } + public Func ShortVersionGetter { get; set; } + public readonly List Commands; + public bool AllowArgumentSeparator { get; set; } + public TextWriter Out { get; set; } = Console.Out; + public TextWriter Error { get; set; } = Console.Error; + + public IEnumerable GetOptions() + { + var expr = Options.AsEnumerable(); + var rootNode = this; + while (rootNode.Parent != null) + { + rootNode = rootNode.Parent; + expr = expr.Concat(rootNode.Options.Where(o => o.Inherited)); + } + + return expr; + } + + public CommandLineApplication Command(string name, Action configuration, + bool throwOnUnexpectedArg = true) + { + var command = new CommandLineApplication(throwOnUnexpectedArg) { Name = name, Parent = this }; + Commands.Add(command); + configuration(command); + return command; + } + + public CommandOption Option(string template, string description, CommandOptionType optionType) + => Option(template, description, optionType, _ => { }, inherited: false); + + public CommandOption Option(string template, string description, CommandOptionType optionType, bool inherited) + => Option(template, description, optionType, _ => { }, inherited); + + public CommandOption Option(string template, string description, CommandOptionType optionType, Action configuration) + => Option(template, description, optionType, configuration, inherited: false); + + public CommandOption Option(string template, string description, CommandOptionType optionType, Action configuration, bool inherited) + { + var option = new CommandOption(template, optionType) + { + Description = description, + Inherited = inherited + }; + Options.Add(option); + configuration(option); + return option; + } + + public CommandArgument Argument(string name, string description, bool multipleValues = false) + { + return Argument(name, description, _ => { }, multipleValues); + } + + public CommandArgument Argument(string name, string description, Action configuration, bool multipleValues = false) + { + var lastArg = Arguments.LastOrDefault(); + if (lastArg != null && lastArg.MultipleValues) + { + var message = string.Format("The last argument '{0}' accepts multiple values. No more argument can be added.", + lastArg.Name); + throw new InvalidOperationException(message); + } + + var argument = new CommandArgument { Name = name, Description = description, MultipleValues = multipleValues }; + Arguments.Add(argument); + configuration(argument); + return argument; + } + + public void OnExecute(Func invoke) + { + Invoke = invoke; + } + + public void OnExecute(Func> invoke) + { + Invoke = () => invoke().Result; + } + public int Execute(params string[] args) + { + CommandLineApplication command = this; + CommandOption option = null; + IEnumerator arguments = null; + + for (var index = 0; index < args.Length; index++) + { + var arg = args[index]; + var processed = false; + if (!processed && option == null) + { + string[] longOption = null; + string[] shortOption = null; + + if (arg.StartsWith("--")) + { + longOption = arg.Substring(2).Split(new[] { ':', '=' }, 2); + } + else if (arg.StartsWith("-")) + { + shortOption = arg.Substring(1).Split(new[] { ':', '=' }, 2); + } + if (longOption != null) + { + processed = true; + var longOptionName = longOption[0]; + option = command.GetOptions().SingleOrDefault(opt => string.Equals(opt.LongName, longOptionName, StringComparison.Ordinal)); + + if (option == null) + { + if (string.IsNullOrEmpty(longOptionName) && !command._throwOnUnexpectedArg && AllowArgumentSeparator) + { + // skip over the '--' argument separator + index++; + } + + HandleUnexpectedArg(command, args, index, argTypeName: "option"); + break; + } + + // If we find a help/version option, show information and stop parsing + if (command.OptionHelp == option) + { + command.ShowHelp(); + return 0; + } + else if (command.OptionVersion == option) + { + command.ShowVersion(); + return 0; + } + + if (longOption.Length == 2) + { + if (!option.TryParse(longOption[1])) + { + command.ShowHint(); + throw new CommandParsingException(command, $"Unexpected value '{longOption[1]}' for option '{option.LongName}'"); + } + option = null; + } + else if (option.OptionType == CommandOptionType.NoValue) + { + // No value is needed for this option + option.TryParse(null); + option = null; + } + } + if (shortOption != null) + { + processed = true; + option = command.GetOptions().SingleOrDefault(opt => string.Equals(opt.ShortName, shortOption[0], StringComparison.Ordinal)); + + // If not a short option, try symbol option + if (option == null) + { + option = command.GetOptions().SingleOrDefault(opt => string.Equals(opt.SymbolName, shortOption[0], StringComparison.Ordinal)); + } + + if (option == null) + { + HandleUnexpectedArg(command, args, index, argTypeName: "option"); + break; + } + + // If we find a help/version option, show information and stop parsing + if (command.OptionHelp == option) + { + command.ShowHelp(); + return 0; + } + else if (command.OptionVersion == option) + { + command.ShowVersion(); + return 0; + } + + if (shortOption.Length == 2) + { + if (!option.TryParse(shortOption[1])) + { + command.ShowHint(); + throw new CommandParsingException(command, $"Unexpected value '{shortOption[1]}' for option '{option.LongName}'"); + } + option = null; + } + else if (option.OptionType == CommandOptionType.NoValue) + { + // No value is needed for this option + option.TryParse(null); + option = null; + } + } + } + + if (!processed && option != null) + { + processed = true; + if (!option.TryParse(arg)) + { + command.ShowHint(); + throw new CommandParsingException(command, $"Unexpected value '{arg}' for option '{option.LongName}'"); + } + option = null; + } + + if (!processed && arguments == null) + { + var currentCommand = command; + foreach (var subcommand in command.Commands) + { + if (string.Equals(subcommand.Name, arg, StringComparison.OrdinalIgnoreCase)) + { + processed = true; + command = subcommand; + break; + } + } + + // If we detect a subcommand + if (command != currentCommand) + { + processed = true; + } + } + if (!processed) + { + if (arguments == null) + { + arguments = new CommandArgumentEnumerator(command.Arguments.GetEnumerator()); + } + if (arguments.MoveNext()) + { + processed = true; + arguments.Current.Values.Add(arg); + } + } + if (!processed) + { + HandleUnexpectedArg(command, args, index, argTypeName: "command or argument"); + break; + } + } + + if (option != null) + { + command.ShowHint(); + throw new CommandParsingException(command, $"Missing value for option '{option.LongName}'"); + } + + return command.Invoke(); + } + + // Helper method that adds a help option + public CommandOption HelpOption(string template) + { + // Help option is special because we stop parsing once we see it + // So we store it separately for further use + OptionHelp = Option(template, "Show help information", CommandOptionType.NoValue); + + return OptionHelp; + } + + public CommandOption VersionOption(string template, + string shortFormVersion, + string longFormVersion = null) + { + if (longFormVersion == null) + { + return VersionOption(template, () => shortFormVersion); + } + else + { + return VersionOption(template, () => shortFormVersion, () => longFormVersion); + } + } + + // Helper method that adds a version option + public CommandOption VersionOption(string template, + Func shortFormVersionGetter, + Func longFormVersionGetter = null) + { + // Version option is special because we stop parsing once we see it + // So we store it separately for further use + OptionVersion = Option(template, "Show version information", CommandOptionType.NoValue); + ShortVersionGetter = shortFormVersionGetter; + LongVersionGetter = longFormVersionGetter ?? shortFormVersionGetter; + + return OptionVersion; + } + + // Show short hint that reminds users to use help option + public void ShowHint() + { + if (OptionHelp != null) + { + Out.WriteLine(string.Format("Specify --{0} for a list of available options and commands.", OptionHelp.LongName)); + } + } + + // Show full help + public void ShowHelp(string commandName = null) + { + for (var cmd = this; cmd != null; cmd = cmd.Parent) + { + cmd.IsShowingInformation = true; + } + + Out.WriteLine(GetHelpText(commandName)); + } + + public virtual string GetHelpText(string commandName = null) + { + var headerBuilder = new StringBuilder("Usage:"); + for (var cmd = this; cmd != null; cmd = cmd.Parent) + { + headerBuilder.Insert(6, string.Format(" {0}", cmd.Name)); + } + + CommandLineApplication target; + + if (commandName == null || string.Equals(Name, commandName, StringComparison.OrdinalIgnoreCase)) + { + target = this; + } + else + { + target = Commands.SingleOrDefault(cmd => string.Equals(cmd.Name, commandName, StringComparison.OrdinalIgnoreCase)); + + if (target != null) + { + headerBuilder.AppendFormat(" {0}", commandName); + } + else + { + // The command name is invalid so don't try to show help for something that doesn't exist + target = this; + } + + } + + var optionsBuilder = new StringBuilder(); + var commandsBuilder = new StringBuilder(); + var argumentsBuilder = new StringBuilder(); + + var arguments = target.Arguments.Where(a => a.ShowInHelpText).ToList(); + if (arguments.Any()) + { + headerBuilder.Append(" [arguments]"); + + argumentsBuilder.AppendLine(); + argumentsBuilder.AppendLine("Arguments:"); + var maxArgLen = arguments.Max(a => a.Name.Length); + var outputFormat = string.Format(" {{0, -{0}}}{{1}}", maxArgLen + 2); + foreach (var arg in arguments) + { + argumentsBuilder.AppendFormat(outputFormat, arg.Name, arg.Description); + argumentsBuilder.AppendLine(); + } + } + + var options = target.GetOptions().Where(o => o.ShowInHelpText).ToList(); + if (options.Any()) + { + headerBuilder.Append(" [options]"); + + optionsBuilder.AppendLine(); + optionsBuilder.AppendLine("Options:"); + var maxOptLen = options.Max(o => o.Template.Length); + var outputFormat = string.Format(" {{0, -{0}}}{{1}}", maxOptLen + 2); + foreach (var opt in options) + { + optionsBuilder.AppendFormat(outputFormat, opt.Template, opt.Description); + optionsBuilder.AppendLine(); + } + } + + var commands = target.Commands.Where(c => c.ShowInHelpText).ToList(); + if (commands.Any()) + { + headerBuilder.Append(" [command]"); + + commandsBuilder.AppendLine(); + commandsBuilder.AppendLine("Commands:"); + var maxCmdLen = commands.Max(c => c.Name.Length); + var outputFormat = string.Format(" {{0, -{0}}}{{1}}", maxCmdLen + 2); + foreach (var cmd in commands.OrderBy(c => c.Name)) + { + commandsBuilder.AppendFormat(outputFormat, cmd.Name, cmd.Description); + commandsBuilder.AppendLine(); + } + + if (OptionHelp != null) + { + commandsBuilder.AppendLine(); + commandsBuilder.AppendFormat($"Use \"{target.Name} [command] --{OptionHelp.LongName}\" for more information about a command."); + commandsBuilder.AppendLine(); + } + } + + if (target.AllowArgumentSeparator) + { + headerBuilder.Append(" [[--] ...]"); + } + + headerBuilder.AppendLine(); + + var nameAndVersion = new StringBuilder(); + nameAndVersion.AppendLine(GetFullNameAndVersion()); + nameAndVersion.AppendLine(); + + return nameAndVersion.ToString() + + headerBuilder.ToString() + + argumentsBuilder.ToString() + + optionsBuilder.ToString() + + commandsBuilder.ToString() + + target.ExtendedHelpText; + } + + public void ShowVersion() + { + for (var cmd = this; cmd != null; cmd = cmd.Parent) + { + cmd.IsShowingInformation = true; + } + + Out.WriteLine(FullName); + Out.WriteLine(LongVersionGetter()); + } + + public string GetFullNameAndVersion() + { + return ShortVersionGetter == null ? FullName : string.Format("{0} {1}", FullName, ShortVersionGetter()); + } + + public void ShowRootCommandFullNameAndVersion() + { + var rootCmd = this; + while (rootCmd.Parent != null) + { + rootCmd = rootCmd.Parent; + } + + Out.WriteLine(rootCmd.GetFullNameAndVersion()); + Out.WriteLine(); + } + + private void HandleUnexpectedArg(CommandLineApplication command, string[] args, int index, string argTypeName) + { + if (command._throwOnUnexpectedArg) + { + command.ShowHint(); + throw new CommandParsingException(command, $"Unrecognized {argTypeName} '{args[index]}'"); + } + else + { + // All remaining arguments are stored for further use + command.RemainingArguments.AddRange(new ArraySegment(args, index, args.Length - index)); + } + } + + private class CommandArgumentEnumerator : IEnumerator + { + private readonly IEnumerator _enumerator; + + public CommandArgumentEnumerator(IEnumerator enumerator) + { + _enumerator = enumerator; + } + + public CommandArgument Current + { + get + { + return _enumerator.Current; + } + } + + object IEnumerator.Current + { + get + { + return Current; + } + } + + public void Dispose() + { + _enumerator.Dispose(); + } + + public bool MoveNext() + { + if (Current == null || !Current.MultipleValues) + { + return _enumerator.MoveNext(); + } + + // If current argument allows multiple values, we don't move forward and + // all later values will be added to current CommandArgument.Values + return true; + } + + public void Reset() + { + _enumerator.Reset(); + } + } + } +} diff --git a/src/Shared/CommandLineUtils/CommandLine/CommandOption.cs b/src/Shared/CommandLineUtils/CommandLine/CommandOption.cs new file mode 100644 index 000000000000..4e663773cc38 --- /dev/null +++ b/src/Shared/CommandLineUtils/CommandLine/CommandOption.cs @@ -0,0 +1,108 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.Extensions.CommandLineUtils +{ + internal class CommandOption + { + public CommandOption(string template, CommandOptionType optionType) + { + Template = template; + OptionType = optionType; + Values = new List(); + + foreach (var part in Template.Split(new[] { ' ', '|' }, StringSplitOptions.RemoveEmptyEntries)) + { + if (part.StartsWith("--")) + { + LongName = part.Substring(2); + } + else if (part.StartsWith("-")) + { + var optName = part.Substring(1); + + // If there is only one char and it is not an English letter, it is a symbol option (e.g. "-?") + if (optName.Length == 1 && !IsEnglishLetter(optName[0])) + { + SymbolName = optName; + } + else + { + ShortName = optName; + } + } + else if (part.StartsWith("<") && part.EndsWith(">")) + { + ValueName = part.Substring(1, part.Length - 2); + } + else + { + throw new ArgumentException($"Invalid template pattern '{template}'", nameof(template)); + } + } + + if (string.IsNullOrEmpty(LongName) && string.IsNullOrEmpty(ShortName) && string.IsNullOrEmpty(SymbolName)) + { + throw new ArgumentException($"Invalid template pattern '{template}'", nameof(template)); + } + } + + public string Template { get; set; } + public string ShortName { get; set; } + public string LongName { get; set; } + public string SymbolName { get; set; } + public string ValueName { get; set; } + public string Description { get; set; } + public List Values { get; private set; } + public CommandOptionType OptionType { get; private set; } + public bool ShowInHelpText { get; set; } = true; + public bool Inherited { get; set; } + + public bool TryParse(string value) + { + switch (OptionType) + { + case CommandOptionType.MultipleValue: + Values.Add(value); + break; + case CommandOptionType.SingleValue: + if (Values.Any()) + { + return false; + } + Values.Add(value); + break; + case CommandOptionType.NoValue: + if (value != null) + { + return false; + } + // Add a value to indicate that this option was specified + Values.Add("on"); + break; + default: + break; + } + return true; + } + + public bool HasValue() + { + return Values.Any(); + } + + public string Value() + { + return HasValue() ? Values[0] : null; + } + + private bool IsEnglishLetter(char c) + { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); + } + } +} diff --git a/src/Shared/CommandLineUtils/CommandLine/CommandOptionType.cs b/src/Shared/CommandLineUtils/CommandLine/CommandOptionType.cs new file mode 100644 index 000000000000..76fdf38f5e29 --- /dev/null +++ b/src/Shared/CommandLineUtils/CommandLine/CommandOptionType.cs @@ -0,0 +1,13 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + + +namespace Microsoft.Extensions.CommandLineUtils +{ + internal enum CommandOptionType + { + MultipleValue, + SingleValue, + NoValue + } +} diff --git a/src/Shared/CommandLineUtils/CommandLine/CommandParsingException.cs b/src/Shared/CommandLineUtils/CommandLine/CommandParsingException.cs new file mode 100644 index 000000000000..2be62b87faad --- /dev/null +++ b/src/Shared/CommandLineUtils/CommandLine/CommandParsingException.cs @@ -0,0 +1,18 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.Extensions.CommandLineUtils +{ + internal class CommandParsingException : Exception + { + public CommandParsingException(CommandLineApplication command, string message) + : base(message) + { + Command = command; + } + + public CommandLineApplication Command { get; } + } +} diff --git a/src/Shared/CommandLineUtils/Microsoft.Extensions.CommandLineUtils.Sources.shproj b/src/Shared/CommandLineUtils/Microsoft.Extensions.CommandLineUtils.Sources.shproj new file mode 100644 index 000000000000..c728fe1012ce --- /dev/null +++ b/src/Shared/CommandLineUtils/Microsoft.Extensions.CommandLineUtils.Sources.shproj @@ -0,0 +1,13 @@ + + + + 00947d4a-c20e-46e3-90c3-6cd6bc87ee72 + 14.0 + + + + + + + + diff --git a/src/Shared/CommandLineUtils/Utilities/ArgumentEscaper.cs b/src/Shared/CommandLineUtils/Utilities/ArgumentEscaper.cs new file mode 100644 index 000000000000..7b696c5175d1 --- /dev/null +++ b/src/Shared/CommandLineUtils/Utilities/ArgumentEscaper.cs @@ -0,0 +1,109 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Microsoft.Extensions.CommandLineUtils +{ + /// + /// A utility for escaping arguments for new processes. + /// + internal static class ArgumentEscaper + { + /// + /// Undo the processing which took place to create string[] args in Main, so that the next process will + /// receive the same string[] args. + /// + /// + /// See https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/ + /// + /// + /// + public static string EscapeAndConcatenate(IEnumerable args) + => string.Join(" ", args.Select(EscapeSingleArg)); + + private static string EscapeSingleArg(string arg) + { + var sb = new StringBuilder(); + + var needsQuotes = ShouldSurroundWithQuotes(arg); + var isQuoted = needsQuotes || IsSurroundedWithQuotes(arg); + + if (needsQuotes) + { + sb.Append('"'); + } + + for (int i = 0; i < arg.Length; ++i) + { + var backslashes = 0; + + // Consume all backslashes + while (i < arg.Length && arg[i] == '\\') + { + backslashes++; + i++; + } + + if (i == arg.Length && isQuoted) + { + // Escape any backslashes at the end of the arg when the argument is also quoted. + // This ensures the outside quote is interpreted as an argument delimiter + sb.Append('\\', 2 * backslashes); + } + else if (i == arg.Length) + { + // At then end of the arg, which isn't quoted, + // just add the backslashes, no need to escape + sb.Append('\\', backslashes); + } + else if (arg[i] == '"') + { + // Escape any preceding backslashes and the quote + sb.Append('\\', (2 * backslashes) + 1); + sb.Append('"'); + } + else + { + // Output any consumed backslashes and the character + sb.Append('\\', backslashes); + sb.Append(arg[i]); + } + } + + if (needsQuotes) + { + sb.Append('"'); + } + + return sb.ToString(); + } + + private static bool ShouldSurroundWithQuotes(string argument) + { + // Don't quote already quoted strings + if (IsSurroundedWithQuotes(argument)) + { + return false; + } + + // Only quote if whitespace exists in the string + return ContainsWhitespace(argument); + } + + private static bool IsSurroundedWithQuotes(string argument) + { + if (argument.Length <= 1) + { + return false; + } + + return argument[0] == '"' && argument[argument.Length - 1] == '"'; + } + + private static bool ContainsWhitespace(string argument) + => argument.IndexOfAny(new [] { ' ', '\t', '\n' }) >= 0; + } +} diff --git a/src/Shared/CommandLineUtils/Utilities/DotNetMuxer.cs b/src/Shared/CommandLineUtils/Utilities/DotNetMuxer.cs new file mode 100644 index 000000000000..52c98b5eb251 --- /dev/null +++ b/src/Shared/CommandLineUtils/Utilities/DotNetMuxer.cs @@ -0,0 +1,58 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +// System.AppContext.GetData is not available in these frameworks +#if !NET451 && !NET452 && !NET46 && !NET461 + +using System; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; + +namespace Microsoft.Extensions.CommandLineUtils +{ + /// + /// Utilities for finding the "dotnet.exe" file from the currently running .NET Core application + /// + internal static class DotNetMuxer + { + private const string MuxerName = "dotnet"; + + static DotNetMuxer() + { + MuxerPath = TryFindMuxerPath(); + } + + /// + /// The full filepath to the .NET Core muxer. + /// + public static string MuxerPath { get; } + + /// + /// Finds the full filepath to the .NET Core muxer, + /// or returns a string containing the default name of the .NET Core muxer ('dotnet'). + /// + /// The path or a string named 'dotnet'. + public static string MuxerPathOrDefault() + => MuxerPath ?? MuxerName; + + private static string TryFindMuxerPath() + { + var fileName = MuxerName; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + fileName += ".exe"; + } + + var mainModule = Process.GetCurrentProcess().MainModule; + if (!string.IsNullOrEmpty(mainModule?.FileName) + && Path.GetFileName(mainModule.FileName).Equals(fileName, StringComparison.OrdinalIgnoreCase)) + { + return mainModule.FileName; + } + + return null; + } + } +} +#endif diff --git a/src/Shared/CopyOnWriteDictionary/CopyOnWriteDictionary.cs b/src/Shared/CopyOnWriteDictionary/CopyOnWriteDictionary.cs new file mode 100644 index 000000000000..1408059ad923 --- /dev/null +++ b/src/Shared/CopyOnWriteDictionary/CopyOnWriteDictionary.cs @@ -0,0 +1,155 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Microsoft.Extensions.Internal +{ + internal class CopyOnWriteDictionary : IDictionary + { + private readonly IDictionary _sourceDictionary; + private readonly IEqualityComparer _comparer; + private IDictionary _innerDictionary; + + public CopyOnWriteDictionary( + IDictionary sourceDictionary, + IEqualityComparer comparer) + { + if (sourceDictionary == null) + { + throw new ArgumentNullException(nameof(sourceDictionary)); + } + + if (comparer == null) + { + throw new ArgumentNullException(nameof(comparer)); + } + + _sourceDictionary = sourceDictionary; + _comparer = comparer; + } + + private IDictionary ReadDictionary + { + get + { + return _innerDictionary ?? _sourceDictionary; + } + } + + private IDictionary WriteDictionary + { + get + { + if (_innerDictionary == null) + { + _innerDictionary = new Dictionary(_sourceDictionary, + _comparer); + } + + return _innerDictionary; + } + } + + public virtual ICollection Keys + { + get + { + return ReadDictionary.Keys; + } + } + + public virtual ICollection Values + { + get + { + return ReadDictionary.Values; + } + } + + public virtual int Count + { + get + { + return ReadDictionary.Count; + } + } + + public virtual bool IsReadOnly + { + get + { + return false; + } + } + + public virtual TValue this[TKey key] + { + get + { + return ReadDictionary[key]; + } + set + { + WriteDictionary[key] = value; + } + } + + public virtual bool ContainsKey(TKey key) + { + return ReadDictionary.ContainsKey(key); + } + + public virtual void Add(TKey key, TValue value) + { + WriteDictionary.Add(key, value); + } + + public virtual bool Remove(TKey key) + { + return WriteDictionary.Remove(key); + } + + public virtual bool TryGetValue(TKey key, out TValue value) + { + return ReadDictionary.TryGetValue(key, out value); + } + + public virtual void Add(KeyValuePair item) + { + WriteDictionary.Add(item); + } + + public virtual void Clear() + { + WriteDictionary.Clear(); + } + + public virtual bool Contains(KeyValuePair item) + { + return ReadDictionary.Contains(item); + } + + public virtual void CopyTo(KeyValuePair[] array, int arrayIndex) + { + ReadDictionary.CopyTo(array, arrayIndex); + } + + public bool Remove(KeyValuePair item) + { + return WriteDictionary.Remove(item); + } + + public virtual IEnumerator> GetEnumerator() + { + return ReadDictionary.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/src/Shared/CopyOnWriteDictionary/CopyOnWriteDictionaryHolder.cs b/src/Shared/CopyOnWriteDictionary/CopyOnWriteDictionaryHolder.cs new file mode 100644 index 000000000000..7cd935e94018 --- /dev/null +++ b/src/Shared/CopyOnWriteDictionary/CopyOnWriteDictionaryHolder.cs @@ -0,0 +1,166 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; + +namespace Microsoft.Extensions.Internal +{ + internal struct CopyOnWriteDictionaryHolder + { + private readonly Dictionary _source; + private Dictionary _copy; + + public CopyOnWriteDictionaryHolder(Dictionary source) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + _source = source; + _copy = null; + } + + public CopyOnWriteDictionaryHolder(CopyOnWriteDictionaryHolder source) + { + _source = source._copy ?? source._source; + _copy = null; + } + + public bool HasBeenCopied => _copy != null; + + public Dictionary ReadDictionary + { + get + { + if (_copy != null) + { + return _copy; + } + else if (_source != null) + { + return _source; + } + else + { + // Default-Constructor case + _copy = new Dictionary(); + return _copy; + } + } + } + + public Dictionary WriteDictionary + { + get + { + if (_copy == null && _source == null) + { + // Default-Constructor case + _copy = new Dictionary(); + } + else if (_copy == null) + { + _copy = new Dictionary(_source, _source.Comparer); + } + + return _copy; + } + } + + public Dictionary.KeyCollection Keys + { + get + { + return ReadDictionary.Keys; + } + } + + public Dictionary.ValueCollection Values + { + get + { + return ReadDictionary.Values; + } + } + + public int Count + { + get + { + return ReadDictionary.Count; + } + } + + public bool IsReadOnly + { + get + { + return false; + } + } + + public TValue this[TKey key] + { + get + { + return ReadDictionary[key]; + } + set + { + WriteDictionary[key] = value; + } + } + + public bool ContainsKey(TKey key) + { + return ReadDictionary.ContainsKey(key); + } + + public void Add(TKey key, TValue value) + { + WriteDictionary.Add(key, value); + } + + public bool Remove(TKey key) + { + return WriteDictionary.Remove(key); + } + + public bool TryGetValue(TKey key, out TValue value) + { + return ReadDictionary.TryGetValue(key, out value); + } + + public void Add(KeyValuePair item) + { + ((ICollection>)WriteDictionary).Add(item); + } + + public void Clear() + { + WriteDictionary.Clear(); + } + + public bool Contains(KeyValuePair item) + { + return ((ICollection>)ReadDictionary).Contains(item); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + ((ICollection>)ReadDictionary).CopyTo(array, arrayIndex); + } + + public bool Remove(KeyValuePair item) + { + return ((ICollection>)WriteDictionary).Remove(item); + } + + public Dictionary.Enumerator GetEnumerator() + { + return ReadDictionary.GetEnumerator(); + } + } +} diff --git a/src/Shared/HashCodeCombiner/HashCodeCombiner.cs b/src/Shared/HashCodeCombiner/HashCodeCombiner.cs new file mode 100644 index 000000000000..4df8b46b058f --- /dev/null +++ b/src/Shared/HashCodeCombiner/HashCodeCombiner.cs @@ -0,0 +1,84 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace Microsoft.Extensions.Internal +{ + internal struct HashCodeCombiner + { + private long _combinedHash64; + + public int CombinedHash + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _combinedHash64.GetHashCode(); } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private HashCodeCombiner(long seed) + { + _combinedHash64 = seed; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Add(IEnumerable e) + { + if (e == null) + { + Add(0); + } + else + { + var count = 0; + foreach (object o in e) + { + Add(o); + count++; + } + Add(count); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator int(HashCodeCombiner self) + { + return self.CombinedHash; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Add(int i) + { + _combinedHash64 = ((_combinedHash64 << 5) + _combinedHash64) ^ i; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Add(string s) + { + var hashCode = (s != null) ? s.GetHashCode() : 0; + Add(hashCode); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Add(object o) + { + var hashCode = (o != null) ? o.GetHashCode() : 0; + Add(hashCode); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Add(TValue value, IEqualityComparer comparer) + { + var hashCode = value != null ? comparer.GetHashCode(value) : 0; + Add(hashCode); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HashCodeCombiner Start() + { + return new HashCodeCombiner(0x1505L); + } + } +} diff --git a/src/Shared/ObjectMethodExecutor/AwaitableInfo.cs b/src/Shared/ObjectMethodExecutor/AwaitableInfo.cs new file mode 100644 index 000000000000..431b83a6e561 --- /dev/null +++ b/src/Shared/ObjectMethodExecutor/AwaitableInfo.cs @@ -0,0 +1,127 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; + +namespace Microsoft.Extensions.Internal +{ + internal struct AwaitableInfo + { + public Type AwaiterType { get; } + public PropertyInfo AwaiterIsCompletedProperty { get; } + public MethodInfo AwaiterGetResultMethod { get; } + public MethodInfo AwaiterOnCompletedMethod { get; } + public MethodInfo AwaiterUnsafeOnCompletedMethod { get; } + public Type ResultType { get; } + public MethodInfo GetAwaiterMethod { get; } + + public AwaitableInfo( + Type awaiterType, + PropertyInfo awaiterIsCompletedProperty, + MethodInfo awaiterGetResultMethod, + MethodInfo awaiterOnCompletedMethod, + MethodInfo awaiterUnsafeOnCompletedMethod, + Type resultType, + MethodInfo getAwaiterMethod) + { + AwaiterType = awaiterType; + AwaiterIsCompletedProperty = awaiterIsCompletedProperty; + AwaiterGetResultMethod = awaiterGetResultMethod; + AwaiterOnCompletedMethod = awaiterOnCompletedMethod; + AwaiterUnsafeOnCompletedMethod = awaiterUnsafeOnCompletedMethod; + ResultType = resultType; + GetAwaiterMethod = getAwaiterMethod; + } + + public static bool IsTypeAwaitable(Type type, out AwaitableInfo awaitableInfo) + { + // Based on Roslyn code: http://source.roslyn.io/#Microsoft.CodeAnalysis.Workspaces/Shared/Extensions/ISymbolExtensions.cs,db4d48ba694b9347 + + // Awaitable must have method matching "object GetAwaiter()" + var getAwaiterMethod = type.GetRuntimeMethods().FirstOrDefault(m => + m.Name.Equals("GetAwaiter", StringComparison.OrdinalIgnoreCase) + && m.GetParameters().Length == 0 + && m.ReturnType != null); + if (getAwaiterMethod == null) + { + awaitableInfo = default(AwaitableInfo); + return false; + } + + var awaiterType = getAwaiterMethod.ReturnType; + + // Awaiter must have property matching "bool IsCompleted { get; }" + var isCompletedProperty = awaiterType.GetRuntimeProperties().FirstOrDefault(p => + p.Name.Equals("IsCompleted", StringComparison.OrdinalIgnoreCase) + && p.PropertyType == typeof(bool) + && p.GetMethod != null); + if (isCompletedProperty == null) + { + awaitableInfo = default(AwaitableInfo); + return false; + } + + // Awaiter must implement INotifyCompletion + var awaiterInterfaces = awaiterType.GetInterfaces(); + var implementsINotifyCompletion = awaiterInterfaces.Any(t => t == typeof(INotifyCompletion)); + if (!implementsINotifyCompletion) + { + awaitableInfo = default(AwaitableInfo); + return false; + } + + // INotifyCompletion supplies a method matching "void OnCompleted(Action action)" + var iNotifyCompletionMap = awaiterType + .GetTypeInfo() + .GetRuntimeInterfaceMap(typeof(INotifyCompletion)); + var onCompletedMethod = iNotifyCompletionMap.InterfaceMethods.Single(m => + m.Name.Equals("OnCompleted", StringComparison.OrdinalIgnoreCase) + && m.ReturnType == typeof(void) + && m.GetParameters().Length == 1 + && m.GetParameters()[0].ParameterType == typeof(Action)); + + // Awaiter optionally implements ICriticalNotifyCompletion + var implementsICriticalNotifyCompletion = awaiterInterfaces.Any(t => t == typeof(ICriticalNotifyCompletion)); + MethodInfo unsafeOnCompletedMethod; + if (implementsICriticalNotifyCompletion) + { + // ICriticalNotifyCompletion supplies a method matching "void UnsafeOnCompleted(Action action)" + var iCriticalNotifyCompletionMap = awaiterType + .GetTypeInfo() + .GetRuntimeInterfaceMap(typeof(ICriticalNotifyCompletion)); + unsafeOnCompletedMethod = iCriticalNotifyCompletionMap.InterfaceMethods.Single(m => + m.Name.Equals("UnsafeOnCompleted", StringComparison.OrdinalIgnoreCase) + && m.ReturnType == typeof(void) + && m.GetParameters().Length == 1 + && m.GetParameters()[0].ParameterType == typeof(Action)); + } + else + { + unsafeOnCompletedMethod = null; + } + + // Awaiter must have method matching "void GetResult" or "T GetResult()" + var getResultMethod = awaiterType.GetRuntimeMethods().FirstOrDefault(m => + m.Name.Equals("GetResult") + && m.GetParameters().Length == 0); + if (getResultMethod == null) + { + awaitableInfo = default(AwaitableInfo); + return false; + } + + awaitableInfo = new AwaitableInfo( + awaiterType, + isCompletedProperty, + getResultMethod, + onCompletedMethod, + unsafeOnCompletedMethod, + getResultMethod.ReturnType, + getAwaiterMethod); + return true; + } + } +} diff --git a/src/Shared/ObjectMethodExecutor/CoercedAwaitableInfo.cs b/src/Shared/ObjectMethodExecutor/CoercedAwaitableInfo.cs new file mode 100644 index 000000000000..4e48ef09a15e --- /dev/null +++ b/src/Shared/ObjectMethodExecutor/CoercedAwaitableInfo.cs @@ -0,0 +1,55 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Linq.Expressions; + +namespace Microsoft.Extensions.Internal +{ + internal struct CoercedAwaitableInfo + { + public AwaitableInfo AwaitableInfo { get; } + public Expression CoercerExpression { get; } + public Type CoercerResultType { get; } + public bool RequiresCoercion => CoercerExpression != null; + + public CoercedAwaitableInfo(AwaitableInfo awaitableInfo) + { + AwaitableInfo = awaitableInfo; + CoercerExpression = null; + CoercerResultType = null; + } + + public CoercedAwaitableInfo(Expression coercerExpression, Type coercerResultType, AwaitableInfo coercedAwaitableInfo) + { + CoercerExpression = coercerExpression; + CoercerResultType = coercerResultType; + AwaitableInfo = coercedAwaitableInfo; + } + + public static bool IsTypeAwaitable(Type type, out CoercedAwaitableInfo info) + { + if (AwaitableInfo.IsTypeAwaitable(type, out var directlyAwaitableInfo)) + { + info = new CoercedAwaitableInfo(directlyAwaitableInfo); + return true; + } + + // It's not directly awaitable, but maybe we can coerce it. + // Currently we support coercing FSharpAsync. + if (ObjectMethodExecutorFSharpSupport.TryBuildCoercerFromFSharpAsyncToAwaitable(type, + out var coercerExpression, + out var coercerResultType)) + { + if (AwaitableInfo.IsTypeAwaitable(coercerResultType, out var coercedAwaitableInfo)) + { + info = new CoercedAwaitableInfo(coercerExpression, coercerResultType, coercedAwaitableInfo); + return true; + } + } + + info = default(CoercedAwaitableInfo); + return false; + } + } +} diff --git a/src/Shared/ObjectMethodExecutor/ObjectMethodExecutor.cs b/src/Shared/ObjectMethodExecutor/ObjectMethodExecutor.cs new file mode 100644 index 000000000000..f8e5b70f0ddd --- /dev/null +++ b/src/Shared/ObjectMethodExecutor/ObjectMethodExecutor.cs @@ -0,0 +1,340 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Reflection; + +namespace Microsoft.Extensions.Internal +{ + internal class ObjectMethodExecutor + { + private readonly object[] _parameterDefaultValues; + private readonly MethodExecutorAsync _executorAsync; + private readonly MethodExecutor _executor; + + private static readonly ConstructorInfo _objectMethodExecutorAwaitableConstructor = + typeof(ObjectMethodExecutorAwaitable).GetConstructor(new[] { + typeof(object), // customAwaitable + typeof(Func), // getAwaiterMethod + typeof(Func), // isCompletedMethod + typeof(Func), // getResultMethod + typeof(Action), // onCompletedMethod + typeof(Action) // unsafeOnCompletedMethod + }); + + private ObjectMethodExecutor(MethodInfo methodInfo, TypeInfo targetTypeInfo, object[] parameterDefaultValues) + { + if (methodInfo == null) + { + throw new ArgumentNullException(nameof(methodInfo)); + } + + MethodInfo = methodInfo; + MethodParameters = methodInfo.GetParameters(); + TargetTypeInfo = targetTypeInfo; + MethodReturnType = methodInfo.ReturnType; + + var isAwaitable = CoercedAwaitableInfo.IsTypeAwaitable(MethodReturnType, out var coercedAwaitableInfo); + + IsMethodAsync = isAwaitable; + AsyncResultType = isAwaitable ? coercedAwaitableInfo.AwaitableInfo.ResultType : null; + + // Upstream code may prefer to use the sync-executor even for async methods, because if it knows + // that the result is a specific Task where T is known, then it can directly cast to that type + // and await it without the extra heap allocations involved in the _executorAsync code path. + _executor = GetExecutor(methodInfo, targetTypeInfo); + + if (IsMethodAsync) + { + _executorAsync = GetExecutorAsync(methodInfo, targetTypeInfo, coercedAwaitableInfo); + } + + _parameterDefaultValues = parameterDefaultValues; + } + + private delegate ObjectMethodExecutorAwaitable MethodExecutorAsync(object target, object[] parameters); + + private delegate object MethodExecutor(object target, object[] parameters); + + private delegate void VoidMethodExecutor(object target, object[] parameters); + + public MethodInfo MethodInfo { get; } + + public ParameterInfo[] MethodParameters { get; } + + public TypeInfo TargetTypeInfo { get; } + + public Type AsyncResultType { get; } + + // This field is made internal set because it is set in unit tests. + public Type MethodReturnType { get; internal set; } + + public bool IsMethodAsync { get; } + + public static ObjectMethodExecutor Create(MethodInfo methodInfo, TypeInfo targetTypeInfo) + { + return new ObjectMethodExecutor(methodInfo, targetTypeInfo, null); + } + + public static ObjectMethodExecutor Create(MethodInfo methodInfo, TypeInfo targetTypeInfo, object[] parameterDefaultValues) + { + if (parameterDefaultValues == null) + { + throw new ArgumentNullException(nameof(parameterDefaultValues)); + } + + return new ObjectMethodExecutor(methodInfo, targetTypeInfo, parameterDefaultValues); + } + + /// + /// Executes the configured method on . This can be used whether or not + /// the configured method is asynchronous. + /// + /// + /// Even if the target method is asynchronous, it's desirable to invoke it using Execute rather than + /// ExecuteAsync if you know at compile time what the return type is, because then you can directly + /// "await" that value (via a cast), and then the generated code will be able to reference the + /// resulting awaitable as a value-typed variable. If you use ExecuteAsync instead, the generated + /// code will have to treat the resulting awaitable as a boxed object, because it doesn't know at + /// compile time what type it would be. + /// + /// The object whose method is to be executed. + /// Parameters to pass to the method. + /// The method return value. + public object Execute(object target, object[] parameters) + { + return _executor(target, parameters); + } + + /// + /// Executes the configured method on . This can only be used if the configured + /// method is asynchronous. + /// + /// + /// If you don't know at compile time the type of the method's returned awaitable, you can use ExecuteAsync, + /// which supplies an awaitable-of-object. This always works, but can incur several extra heap allocations + /// as compared with using Execute and then using "await" on the result value typecasted to the known + /// awaitable type. The possible extra heap allocations are for: + /// + /// 1. The custom awaitable (though usually there's a heap allocation for this anyway, since normally + /// it's a reference type, and you normally create a new instance per call). + /// 2. The custom awaiter (whether or not it's a value type, since if it's not, you need a new instance + /// of it, and if it is, it will have to be boxed so the calling code can reference it as an object). + /// 3. The async result value, if it's a value type (it has to be boxed as an object, since the calling + /// code doesn't know what type it's going to be). + /// + /// The object whose method is to be executed. + /// Parameters to pass to the method. + /// An object that you can "await" to get the method return value. + public ObjectMethodExecutorAwaitable ExecuteAsync(object target, object[] parameters) + { + return _executorAsync(target, parameters); + } + + public object GetDefaultValueForParameter(int index) + { + if (_parameterDefaultValues == null) + { + throw new InvalidOperationException($"Cannot call {nameof(GetDefaultValueForParameter)}, because no parameter default values were supplied."); + } + + if (index < 0 || index > MethodParameters.Length - 1) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + + return _parameterDefaultValues[index]; + } + + private static MethodExecutor GetExecutor(MethodInfo methodInfo, TypeInfo targetTypeInfo) + { + // Parameters to executor + var targetParameter = Expression.Parameter(typeof(object), "target"); + var parametersParameter = Expression.Parameter(typeof(object[]), "parameters"); + + // Build parameter list + var parameters = new List(); + var paramInfos = methodInfo.GetParameters(); + for (int i = 0; i < paramInfos.Length; i++) + { + var paramInfo = paramInfos[i]; + var valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i)); + var valueCast = Expression.Convert(valueObj, paramInfo.ParameterType); + + // valueCast is "(Ti) parameters[i]" + parameters.Add(valueCast); + } + + // Call method + var instanceCast = Expression.Convert(targetParameter, targetTypeInfo.AsType()); + var methodCall = Expression.Call(instanceCast, methodInfo, parameters); + + // methodCall is "((Ttarget) target) method((T0) parameters[0], (T1) parameters[1], ...)" + // Create function + if (methodCall.Type == typeof(void)) + { + var lambda = Expression.Lambda(methodCall, targetParameter, parametersParameter); + var voidExecutor = lambda.Compile(); + return WrapVoidMethod(voidExecutor); + } + else + { + // must coerce methodCall to match ActionExecutor signature + var castMethodCall = Expression.Convert(methodCall, typeof(object)); + var lambda = Expression.Lambda(castMethodCall, targetParameter, parametersParameter); + return lambda.Compile(); + } + } + + private static MethodExecutor WrapVoidMethod(VoidMethodExecutor executor) + { + return delegate (object target, object[] parameters) + { + executor(target, parameters); + return null; + }; + } + + private static MethodExecutorAsync GetExecutorAsync( + MethodInfo methodInfo, + TypeInfo targetTypeInfo, + CoercedAwaitableInfo coercedAwaitableInfo) + { + // Parameters to executor + var targetParameter = Expression.Parameter(typeof(object), "target"); + var parametersParameter = Expression.Parameter(typeof(object[]), "parameters"); + + // Build parameter list + var parameters = new List(); + var paramInfos = methodInfo.GetParameters(); + for (int i = 0; i < paramInfos.Length; i++) + { + var paramInfo = paramInfos[i]; + var valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i)); + var valueCast = Expression.Convert(valueObj, paramInfo.ParameterType); + + // valueCast is "(Ti) parameters[i]" + parameters.Add(valueCast); + } + + // Call method + var instanceCast = Expression.Convert(targetParameter, targetTypeInfo.AsType()); + var methodCall = Expression.Call(instanceCast, methodInfo, parameters); + + // Using the method return value, construct an ObjectMethodExecutorAwaitable based on + // the info we have about its implementation of the awaitable pattern. Note that all + // the funcs/actions we construct here are precompiled, so that only one instance of + // each is preserved throughout the lifetime of the ObjectMethodExecutor. + + // var getAwaiterFunc = (object awaitable) => + // (object)((CustomAwaitableType)awaitable).GetAwaiter(); + var customAwaitableParam = Expression.Parameter(typeof(object), "awaitable"); + var awaitableInfo = coercedAwaitableInfo.AwaitableInfo; + var postCoercionMethodReturnType = coercedAwaitableInfo.CoercerResultType ?? methodInfo.ReturnType; + var getAwaiterFunc = Expression.Lambda>( + Expression.Convert( + Expression.Call( + Expression.Convert(customAwaitableParam, postCoercionMethodReturnType), + awaitableInfo.GetAwaiterMethod), + typeof(object)), + customAwaitableParam).Compile(); + + // var isCompletedFunc = (object awaiter) => + // ((CustomAwaiterType)awaiter).IsCompleted; + var isCompletedParam = Expression.Parameter(typeof(object), "awaiter"); + var isCompletedFunc = Expression.Lambda>( + Expression.MakeMemberAccess( + Expression.Convert(isCompletedParam, awaitableInfo.AwaiterType), + awaitableInfo.AwaiterIsCompletedProperty), + isCompletedParam).Compile(); + + var getResultParam = Expression.Parameter(typeof(object), "awaiter"); + Func getResultFunc; + if (awaitableInfo.ResultType == typeof(void)) + { + // var getResultFunc = (object awaiter) => + // { + // ((CustomAwaiterType)awaiter).GetResult(); // We need to invoke this to surface any exceptions + // return (object)null; + // }; + getResultFunc = Expression.Lambda>( + Expression.Block( + Expression.Call( + Expression.Convert(getResultParam, awaitableInfo.AwaiterType), + awaitableInfo.AwaiterGetResultMethod), + Expression.Constant(null) + ), + getResultParam).Compile(); + } + else + { + // var getResultFunc = (object awaiter) => + // (object)((CustomAwaiterType)awaiter).GetResult(); + getResultFunc = Expression.Lambda>( + Expression.Convert( + Expression.Call( + Expression.Convert(getResultParam, awaitableInfo.AwaiterType), + awaitableInfo.AwaiterGetResultMethod), + typeof(object)), + getResultParam).Compile(); + } + + // var onCompletedFunc = (object awaiter, Action continuation) => { + // ((CustomAwaiterType)awaiter).OnCompleted(continuation); + // }; + var onCompletedParam1 = Expression.Parameter(typeof(object), "awaiter"); + var onCompletedParam2 = Expression.Parameter(typeof(Action), "continuation"); + var onCompletedFunc = Expression.Lambda>( + Expression.Call( + Expression.Convert(onCompletedParam1, awaitableInfo.AwaiterType), + awaitableInfo.AwaiterOnCompletedMethod, + onCompletedParam2), + onCompletedParam1, + onCompletedParam2).Compile(); + + Action unsafeOnCompletedFunc = null; + if (awaitableInfo.AwaiterUnsafeOnCompletedMethod != null) + { + // var unsafeOnCompletedFunc = (object awaiter, Action continuation) => { + // ((CustomAwaiterType)awaiter).UnsafeOnCompleted(continuation); + // }; + var unsafeOnCompletedParam1 = Expression.Parameter(typeof(object), "awaiter"); + var unsafeOnCompletedParam2 = Expression.Parameter(typeof(Action), "continuation"); + unsafeOnCompletedFunc = Expression.Lambda>( + Expression.Call( + Expression.Convert(unsafeOnCompletedParam1, awaitableInfo.AwaiterType), + awaitableInfo.AwaiterUnsafeOnCompletedMethod, + unsafeOnCompletedParam2), + unsafeOnCompletedParam1, + unsafeOnCompletedParam2).Compile(); + } + + // If we need to pass the method call result through a coercer function to get an + // awaitable, then do so. + var coercedMethodCall = coercedAwaitableInfo.RequiresCoercion + ? Expression.Invoke(coercedAwaitableInfo.CoercerExpression, methodCall) + : (Expression)methodCall; + + // return new ObjectMethodExecutorAwaitable( + // (object)coercedMethodCall, + // getAwaiterFunc, + // isCompletedFunc, + // getResultFunc, + // onCompletedFunc, + // unsafeOnCompletedFunc); + var returnValueExpression = Expression.New( + _objectMethodExecutorAwaitableConstructor, + Expression.Convert(coercedMethodCall, typeof(object)), + Expression.Constant(getAwaiterFunc), + Expression.Constant(isCompletedFunc), + Expression.Constant(getResultFunc), + Expression.Constant(onCompletedFunc), + Expression.Constant(unsafeOnCompletedFunc, typeof(Action))); + + var lambda = Expression.Lambda(returnValueExpression, targetParameter, parametersParameter); + return lambda.Compile(); + } + } +} diff --git a/src/Shared/ObjectMethodExecutor/ObjectMethodExecutorAwaitable.cs b/src/Shared/ObjectMethodExecutor/ObjectMethodExecutorAwaitable.cs new file mode 100644 index 000000000000..7509b86b2ba5 --- /dev/null +++ b/src/Shared/ObjectMethodExecutor/ObjectMethodExecutorAwaitable.cs @@ -0,0 +1,114 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Runtime.CompilerServices; + +namespace Microsoft.Extensions.Internal +{ + /// + /// Provides a common awaitable structure that can + /// return, regardless of whether the underlying value is a System.Task, an FSharpAsync, or an + /// application-defined custom awaitable. + /// + internal struct ObjectMethodExecutorAwaitable + { + private readonly object _customAwaitable; + private readonly Func _getAwaiterMethod; + private readonly Func _isCompletedMethod; + private readonly Func _getResultMethod; + private readonly Action _onCompletedMethod; + private readonly Action _unsafeOnCompletedMethod; + + // Perf note: since we're requiring the customAwaitable to be supplied here as an object, + // this will trigger a further allocation if it was a value type (i.e., to box it). We can't + // fix this by making the customAwaitable type generic, because the calling code typically + // does not know the type of the awaitable/awaiter at compile-time anyway. + // + // However, we could fix it by not passing the customAwaitable here at all, and instead + // passing a func that maps directly from the target object (e.g., controller instance), + // target method (e.g., action method info), and params array to the custom awaiter in the + // GetAwaiter() method below. In effect, by delaying the actual method call until the + // upstream code calls GetAwaiter on this ObjectMethodExecutorAwaitable instance. + // This optimization is not currently implemented because: + // [1] It would make no difference when the awaitable was an object type, which is + // by far the most common scenario (e.g., System.Task). + // [2] It would be complex - we'd need some kind of object pool to track all the parameter + // arrays until we needed to use them in GetAwaiter(). + // We can reconsider this in the future if there's a need to optimize for ValueTask + // or other value-typed awaitables. + + public ObjectMethodExecutorAwaitable( + object customAwaitable, + Func getAwaiterMethod, + Func isCompletedMethod, + Func getResultMethod, + Action onCompletedMethod, + Action unsafeOnCompletedMethod) + { + _customAwaitable = customAwaitable; + _getAwaiterMethod = getAwaiterMethod; + _isCompletedMethod = isCompletedMethod; + _getResultMethod = getResultMethod; + _onCompletedMethod = onCompletedMethod; + _unsafeOnCompletedMethod = unsafeOnCompletedMethod; + } + + public Awaiter GetAwaiter() + { + var customAwaiter = _getAwaiterMethod(_customAwaitable); + return new Awaiter(customAwaiter, _isCompletedMethod, _getResultMethod, _onCompletedMethod, _unsafeOnCompletedMethod); + } + + public struct Awaiter : ICriticalNotifyCompletion + { + private readonly object _customAwaiter; + private readonly Func _isCompletedMethod; + private readonly Func _getResultMethod; + private readonly Action _onCompletedMethod; + private readonly Action _unsafeOnCompletedMethod; + + public Awaiter( + object customAwaiter, + Func isCompletedMethod, + Func getResultMethod, + Action onCompletedMethod, + Action unsafeOnCompletedMethod) + { + _customAwaiter = customAwaiter; + _isCompletedMethod = isCompletedMethod; + _getResultMethod = getResultMethod; + _onCompletedMethod = onCompletedMethod; + _unsafeOnCompletedMethod = unsafeOnCompletedMethod; + } + + public bool IsCompleted => _isCompletedMethod(_customAwaiter); + + public object GetResult() => _getResultMethod(_customAwaiter); + + public void OnCompleted(Action continuation) + { + _onCompletedMethod(_customAwaiter, continuation); + } + + public void UnsafeOnCompleted(Action continuation) + { + // If the underlying awaitable implements ICriticalNotifyCompletion, use its UnsafeOnCompleted. + // If not, fall back on using its OnCompleted. + // + // Why this is safe: + // - Implementing ICriticalNotifyCompletion is a way of saying the caller can choose whether it + // needs the execution context to be preserved (which it signals by calling OnCompleted), or + // that it doesn't (which it signals by calling UnsafeOnCompleted). Obviously it's faster *not* + // to preserve and restore the context, so we prefer that where possible. + // - If a caller doesn't need the execution context to be preserved and hence calls UnsafeOnCompleted, + // there's no harm in preserving it anyway - it's just a bit of wasted cost. That's what will happen + // if a caller sees that the proxy implements ICriticalNotifyCompletion but the proxy chooses to + // pass the call on to the underlying awaitable's OnCompleted method. + + var underlyingMethodToUse = _unsafeOnCompletedMethod ?? _onCompletedMethod; + underlyingMethodToUse(_customAwaiter, continuation); + } + } + } +} \ No newline at end of file diff --git a/src/Shared/ObjectMethodExecutor/ObjectMethodExecutorFSharpSupport.cs b/src/Shared/ObjectMethodExecutor/ObjectMethodExecutorFSharpSupport.cs new file mode 100644 index 000000000000..2198c0ce4506 --- /dev/null +++ b/src/Shared/ObjectMethodExecutor/ObjectMethodExecutorFSharpSupport.cs @@ -0,0 +1,151 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.Extensions.Internal +{ + /// + /// Helper for detecting whether a given type is FSharpAsync`1, and if so, supplying + /// an for mapping instances of that type to a C# awaitable. + /// + /// + /// The main design goal here is to avoid taking a compile-time dependency on + /// FSharp.Core.dll, because non-F# applications wouldn't use it. So all the references + /// to FSharp types have to be constructed dynamically at runtime. + /// + internal static class ObjectMethodExecutorFSharpSupport + { + private static object _fsharpValuesCacheLock = new object(); + private static Assembly _fsharpCoreAssembly; + private static MethodInfo _fsharpAsyncStartAsTaskGenericMethod; + private static PropertyInfo _fsharpOptionOfTaskCreationOptionsNoneProperty; + private static PropertyInfo _fsharpOptionOfCancellationTokenNoneProperty; + + public static bool TryBuildCoercerFromFSharpAsyncToAwaitable( + Type possibleFSharpAsyncType, + out Expression coerceToAwaitableExpression, + out Type awaitableType) + { + var methodReturnGenericType = possibleFSharpAsyncType.IsGenericType + ? possibleFSharpAsyncType.GetGenericTypeDefinition() + : null; + + if (!IsFSharpAsyncOpenGenericType(methodReturnGenericType)) + { + coerceToAwaitableExpression = null; + awaitableType = null; + return false; + } + + var awaiterResultType = possibleFSharpAsyncType.GetGenericArguments().Single(); + awaitableType = typeof(Task<>).MakeGenericType(awaiterResultType); + + // coerceToAwaitableExpression = (object fsharpAsync) => + // { + // return (object)FSharpAsync.StartAsTask( + // (Microsoft.FSharp.Control.FSharpAsync)fsharpAsync, + // FSharpOption.None, + // FSharpOption.None); + // }; + var startAsTaskClosedMethod = _fsharpAsyncStartAsTaskGenericMethod + .MakeGenericMethod(awaiterResultType); + var coerceToAwaitableParam = Expression.Parameter(typeof(object)); + coerceToAwaitableExpression = Expression.Lambda( + Expression.Convert( + Expression.Call( + startAsTaskClosedMethod, + Expression.Convert(coerceToAwaitableParam, possibleFSharpAsyncType), + Expression.MakeMemberAccess(null, _fsharpOptionOfTaskCreationOptionsNoneProperty), + Expression.MakeMemberAccess(null, _fsharpOptionOfCancellationTokenNoneProperty)), + typeof(object)), + coerceToAwaitableParam); + + return true; + } + + private static bool IsFSharpAsyncOpenGenericType(Type possibleFSharpAsyncGenericType) + { + var typeFullName = possibleFSharpAsyncGenericType?.FullName; + if (!string.Equals(typeFullName, "Microsoft.FSharp.Control.FSharpAsync`1", StringComparison.Ordinal)) + { + return false; + } + + lock (_fsharpValuesCacheLock) + { + if (_fsharpCoreAssembly != null) + { + // Since we've already found the real FSharpAsync.Core assembly, we just have + // to check that the supplied FSharpAsync`1 type is the one from that assembly. + return possibleFSharpAsyncGenericType.Assembly == _fsharpCoreAssembly; + } + else + { + // We'll keep trying to find the FSharp types/values each time any type called + // FSharpAsync`1 is supplied. + return TryPopulateFSharpValueCaches(possibleFSharpAsyncGenericType); + } + } + } + + private static bool TryPopulateFSharpValueCaches(Type possibleFSharpAsyncGenericType) + { + var assembly = possibleFSharpAsyncGenericType.Assembly; + var fsharpOptionType = assembly.GetType("Microsoft.FSharp.Core.FSharpOption`1"); + var fsharpAsyncType = assembly.GetType("Microsoft.FSharp.Control.FSharpAsync"); + + if (fsharpOptionType == null || fsharpAsyncType == null) + { + return false; + } + + // Get a reference to FSharpOption.None + var fsharpOptionOfTaskCreationOptionsType = fsharpOptionType + .MakeGenericType(typeof(TaskCreationOptions)); + _fsharpOptionOfTaskCreationOptionsNoneProperty = fsharpOptionOfTaskCreationOptionsType + .GetTypeInfo() + .GetRuntimeProperty("None"); + + // Get a reference to FSharpOption.None + var fsharpOptionOfCancellationTokenType = fsharpOptionType + .MakeGenericType(typeof(CancellationToken)); + _fsharpOptionOfCancellationTokenNoneProperty = fsharpOptionOfCancellationTokenType + .GetTypeInfo() + .GetRuntimeProperty("None"); + + // Get a reference to FSharpAsync.StartAsTask<> + var fsharpAsyncMethods = fsharpAsyncType + .GetRuntimeMethods() + .Where(m => m.Name.Equals("StartAsTask", StringComparison.Ordinal)); + foreach (var candidateMethodInfo in fsharpAsyncMethods) + { + var parameters = candidateMethodInfo.GetParameters(); + if (parameters.Length == 3 + && TypesHaveSameIdentity(parameters[0].ParameterType, possibleFSharpAsyncGenericType) + && parameters[1].ParameterType == fsharpOptionOfTaskCreationOptionsType + && parameters[2].ParameterType == fsharpOptionOfCancellationTokenType) + { + // This really does look like the correct method (and hence assembly). + _fsharpAsyncStartAsTaskGenericMethod = candidateMethodInfo; + _fsharpCoreAssembly = assembly; + break; + } + } + + return _fsharpCoreAssembly != null; + } + + private static bool TypesHaveSameIdentity(Type type1, Type type2) + { + return type1.Assembly == type2.Assembly + && string.Equals(type1.Namespace, type2.Namespace, StringComparison.Ordinal) + && string.Equals(type1.Name, type2.Name, StringComparison.Ordinal); + } + } +} diff --git a/src/Shared/Process/ProcessHelper.cs b/src/Shared/Process/ProcessHelper.cs new file mode 100644 index 000000000000..cf42a7e3a77e --- /dev/null +++ b/src/Shared/Process/ProcessHelper.cs @@ -0,0 +1,113 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; + +namespace Microsoft.Extensions.Internal +{ + internal static class ProcessExtensions + { + private static readonly bool _isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + private static readonly TimeSpan _defaultTimeout = TimeSpan.FromSeconds(30); + + public static void KillTree(this Process process) + { + process.KillTree(_defaultTimeout); + } + + public static void KillTree(this Process process, TimeSpan timeout) + { + string stdout; + if (_isWindows) + { + RunProcessAndWaitForExit( + "taskkill", + $"/T /F /PID {process.Id}", + timeout, + out stdout); + } + else + { + var children = new HashSet(); + GetAllChildIdsUnix(process.Id, children, timeout); + foreach (var childId in children) + { + KillProcessUnix(childId, timeout); + } + KillProcessUnix(process.Id, timeout); + } + } + + private static void GetAllChildIdsUnix(int parentId, ISet children, TimeSpan timeout) + { + string stdout; + var exitCode = RunProcessAndWaitForExit( + "pgrep", + $"-P {parentId}", + timeout, + out stdout); + + if (exitCode == 0 && !string.IsNullOrEmpty(stdout)) + { + using (var reader = new StringReader(stdout)) + { + while (true) + { + var text = reader.ReadLine(); + if (text == null) + { + return; + } + + int id; + if (int.TryParse(text, out id)) + { + children.Add(id); + // Recursively get the children + GetAllChildIdsUnix(id, children, timeout); + } + } + } + } + } + + private static void KillProcessUnix(int processId, TimeSpan timeout) + { + string stdout; + RunProcessAndWaitForExit( + "kill", + $"-TERM {processId}", + timeout, + out stdout); + } + + private static int RunProcessAndWaitForExit(string fileName, string arguments, TimeSpan timeout, out string stdout) + { + var startInfo = new ProcessStartInfo + { + FileName = fileName, + Arguments = arguments, + RedirectStandardOutput = true, + UseShellExecute = false + }; + + var process = Process.Start(startInfo); + + stdout = null; + if (process.WaitForExit((int)timeout.TotalMilliseconds)) + { + stdout = process.StandardOutput.ReadToEnd(); + } + else + { + process.Kill(); + } + + return process.ExitCode; + } + } +} diff --git a/src/Shared/PropertyActivator/PropertyActivator.cs b/src/Shared/PropertyActivator/PropertyActivator.cs new file mode 100644 index 000000000000..925f6a76ae0b --- /dev/null +++ b/src/Shared/PropertyActivator/PropertyActivator.cs @@ -0,0 +1,110 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Linq; +using System.Reflection; + +namespace Microsoft.Extensions.Internal +{ + internal class PropertyActivator + { + private readonly Func _valueAccessor; + private readonly Action _fastPropertySetter; + + public PropertyActivator( + PropertyInfo propertyInfo, + Func valueAccessor) + { + if (propertyInfo == null) + { + throw new ArgumentNullException(nameof(propertyInfo)); + } + + if (valueAccessor == null) + { + throw new ArgumentNullException(nameof(valueAccessor)); + } + + PropertyInfo = propertyInfo; + _valueAccessor = valueAccessor; + _fastPropertySetter = PropertyHelper.MakeFastPropertySetter(propertyInfo); + } + + public PropertyInfo PropertyInfo { get; private set; } + + public object Activate(object instance, TContext context) + { + if (instance == null) + { + throw new ArgumentNullException(nameof(instance)); + } + + var value = _valueAccessor(context); + _fastPropertySetter(instance, value); + return value; + } + + public static PropertyActivator[] GetPropertiesToActivate( + Type type, + Type activateAttributeType, + Func> createActivateInfo) + { + if (type == null) + { + throw new ArgumentNullException(nameof(type)); + } + + if (activateAttributeType == null) + { + throw new ArgumentNullException(nameof(activateAttributeType)); + } + + if (createActivateInfo == null) + { + throw new ArgumentNullException(nameof(createActivateInfo)); + } + + return GetPropertiesToActivate(type, activateAttributeType, createActivateInfo, includeNonPublic: false); + } + + public static PropertyActivator[] GetPropertiesToActivate( + Type type, + Type activateAttributeType, + Func> createActivateInfo, + bool includeNonPublic) + { + if (type == null) + { + throw new ArgumentNullException(nameof(type)); + } + + if (activateAttributeType == null) + { + throw new ArgumentNullException(nameof(activateAttributeType)); + } + + if (createActivateInfo == null) + { + throw new ArgumentNullException(nameof(createActivateInfo)); + } + + var properties = type.GetRuntimeProperties() + .Where((property) => + { + return + property.IsDefined(activateAttributeType) && + property.GetIndexParameters().Length == 0 && + property.SetMethod != null && + !property.SetMethod.IsStatic; + }); + + if (!includeNonPublic) + { + properties = properties.Where(property => property.SetMethod.IsPublic); + } + + return properties.Select(createActivateInfo).ToArray(); + } + } +} \ No newline at end of file diff --git a/src/Shared/PropertyHelper/PropertyHelper.cs b/src/Shared/PropertyHelper/PropertyHelper.cs new file mode 100644 index 000000000000..27ba5661a423 --- /dev/null +++ b/src/Shared/PropertyHelper/PropertyHelper.cs @@ -0,0 +1,526 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; + +namespace Microsoft.Extensions.Internal +{ + internal class PropertyHelper + { + // Delegate type for a by-ref property getter + private delegate TValue ByRefFunc(ref TDeclaringType arg); + + private static readonly MethodInfo CallPropertyGetterOpenGenericMethod = + typeof(PropertyHelper).GetTypeInfo().GetDeclaredMethod(nameof(CallPropertyGetter)); + + private static readonly MethodInfo CallPropertyGetterByReferenceOpenGenericMethod = + typeof(PropertyHelper).GetTypeInfo().GetDeclaredMethod(nameof(CallPropertyGetterByReference)); + + private static readonly MethodInfo CallNullSafePropertyGetterOpenGenericMethod = + typeof(PropertyHelper).GetTypeInfo().GetDeclaredMethod(nameof(CallNullSafePropertyGetter)); + + private static readonly MethodInfo CallNullSafePropertyGetterByReferenceOpenGenericMethod = + typeof(PropertyHelper).GetTypeInfo().GetDeclaredMethod(nameof(CallNullSafePropertyGetterByReference)); + + private static readonly MethodInfo CallPropertySetterOpenGenericMethod = + typeof(PropertyHelper).GetTypeInfo().GetDeclaredMethod(nameof(CallPropertySetter)); + + // Using an array rather than IEnumerable, as target will be called on the hot path numerous times. + private static readonly ConcurrentDictionary PropertiesCache = + new ConcurrentDictionary(); + + private static readonly ConcurrentDictionary VisiblePropertiesCache = + new ConcurrentDictionary(); + + private Action _valueSetter; + private Func _valueGetter; + + /// + /// Initializes a fast . + /// This constructor does not cache the helper. For caching, use . + /// + public PropertyHelper(PropertyInfo property) + { + if (property == null) + { + throw new ArgumentNullException(nameof(property)); + } + + Property = property; + Name = property.Name; + } + + /// + /// Gets the backing . + /// + public PropertyInfo Property { get; } + + /// + /// Gets (or sets in derived types) the property name. + /// + public virtual string Name { get; protected set; } + + /// + /// Gets the property value getter. + /// + public Func ValueGetter + { + get + { + if (_valueGetter == null) + { + _valueGetter = MakeFastPropertyGetter(Property); + } + + return _valueGetter; + } + } + + /// + /// Gets the property value setter. + /// + public Action ValueSetter + { + get + { + if (_valueSetter == null) + { + _valueSetter = MakeFastPropertySetter(Property); + } + + return _valueSetter; + } + } + + /// + /// Returns the property value for the specified . + /// + /// The object whose property value will be returned. + /// The property value. + public object GetValue(object instance) + { + return ValueGetter(instance); + } + + /// + /// Sets the property value for the specified . + /// + /// The object whose property value will be set. + /// The property value. + public void SetValue(object instance, object value) + { + ValueSetter(instance, value); + } + + /// + /// Creates and caches fast property helpers that expose getters for every public get property on the + /// underlying type. + /// + /// The type info to extract property accessors for. + /// A cached array of all public properties of the specified type. + /// + public static PropertyHelper[] GetProperties(TypeInfo typeInfo) + { + return GetProperties(typeInfo.AsType()); + } + + /// + /// Creates and caches fast property helpers that expose getters for every public get property on the + /// specified type. + /// + /// The type to extract property accessors for. + /// A cached array of all public properties of the specified type. + /// + public static PropertyHelper[] GetProperties(Type type) + { + return GetProperties(type, CreateInstance, PropertiesCache); + } + + /// + /// + /// Creates and caches fast property helpers that expose getters for every non-hidden get property + /// on the specified type. + /// + /// + /// excludes properties defined on base types that have been + /// hidden by definitions using the new keyword. + /// + /// + /// The type info to extract property accessors for. + /// + /// A cached array of all public properties of the specified type. + /// + public static PropertyHelper[] GetVisibleProperties(TypeInfo typeInfo) + { + return GetVisibleProperties(typeInfo.AsType(), CreateInstance, PropertiesCache, VisiblePropertiesCache); + } + + /// + /// + /// Creates and caches fast property helpers that expose getters for every non-hidden get property + /// on the specified type. + /// + /// + /// excludes properties defined on base types that have been + /// hidden by definitions using the new keyword. + /// + /// + /// The type to extract property accessors for. + /// + /// A cached array of all public properties of the specified type. + /// + public static PropertyHelper[] GetVisibleProperties(Type type) + { + return GetVisibleProperties(type, CreateInstance, PropertiesCache, VisiblePropertiesCache); + } + + /// + /// Creates a single fast property getter. The result is not cached. + /// + /// propertyInfo to extract the getter for. + /// a fast getter. + /// + /// This method is more memory efficient than a dynamically compiled lambda, and about the + /// same speed. + /// + public static Func MakeFastPropertyGetter(PropertyInfo propertyInfo) + { + Debug.Assert(propertyInfo != null); + + return MakeFastPropertyGetter( + propertyInfo, + CallPropertyGetterOpenGenericMethod, + CallPropertyGetterByReferenceOpenGenericMethod); + } + + /// + /// Creates a single fast property getter which is safe for a null input object. The result is not cached. + /// + /// propertyInfo to extract the getter for. + /// a fast getter. + /// + /// This method is more memory efficient than a dynamically compiled lambda, and about the + /// same speed. + /// + public static Func MakeNullSafeFastPropertyGetter(PropertyInfo propertyInfo) + { + Debug.Assert(propertyInfo != null); + + return MakeFastPropertyGetter( + propertyInfo, + CallNullSafePropertyGetterOpenGenericMethod, + CallNullSafePropertyGetterByReferenceOpenGenericMethod); + } + + private static Func MakeFastPropertyGetter( + PropertyInfo propertyInfo, + MethodInfo propertyGetterWrapperMethod, + MethodInfo propertyGetterByRefWrapperMethod) + { + Debug.Assert(propertyInfo != null); + + // Must be a generic method with a Func<,> parameter + Debug.Assert(propertyGetterWrapperMethod != null); + Debug.Assert(propertyGetterWrapperMethod.IsGenericMethodDefinition); + Debug.Assert(propertyGetterWrapperMethod.GetParameters().Length == 2); + + // Must be a generic method with a ByRefFunc<,> parameter + Debug.Assert(propertyGetterByRefWrapperMethod != null); + Debug.Assert(propertyGetterByRefWrapperMethod.IsGenericMethodDefinition); + Debug.Assert(propertyGetterByRefWrapperMethod.GetParameters().Length == 2); + + var getMethod = propertyInfo.GetMethod; + Debug.Assert(getMethod != null); + Debug.Assert(!getMethod.IsStatic); + Debug.Assert(getMethod.GetParameters().Length == 0); + + // Instance methods in the CLR can be turned into static methods where the first parameter + // is open over "target". This parameter is always passed by reference, so we have a code + // path for value types and a code path for reference types. + if (getMethod.DeclaringType.GetTypeInfo().IsValueType) + { + // Create a delegate (ref TDeclaringType) -> TValue + return MakeFastPropertyGetter( + typeof(ByRefFunc<,>), + getMethod, + propertyGetterByRefWrapperMethod); + } + else + { + // Create a delegate TDeclaringType -> TValue + return MakeFastPropertyGetter( + typeof(Func<,>), + getMethod, + propertyGetterWrapperMethod); + } + } + + private static Func MakeFastPropertyGetter( + Type openGenericDelegateType, + MethodInfo propertyGetMethod, + MethodInfo openGenericWrapperMethod) + { + var typeInput = propertyGetMethod.DeclaringType; + var typeOutput = propertyGetMethod.ReturnType; + + var delegateType = openGenericDelegateType.MakeGenericType(typeInput, typeOutput); + var propertyGetterDelegate = propertyGetMethod.CreateDelegate(delegateType); + + var wrapperDelegateMethod = openGenericWrapperMethod.MakeGenericMethod(typeInput, typeOutput); + var accessorDelegate = wrapperDelegateMethod.CreateDelegate( + typeof(Func), + propertyGetterDelegate); + + return (Func)accessorDelegate; + } + + /// + /// Creates a single fast property setter for reference types. The result is not cached. + /// + /// propertyInfo to extract the setter for. + /// a fast getter. + /// + /// This method is more memory efficient than a dynamically compiled lambda, and about the + /// same speed. This only works for reference types. + /// + public static Action MakeFastPropertySetter(PropertyInfo propertyInfo) + { + Debug.Assert(propertyInfo != null); + Debug.Assert(!propertyInfo.DeclaringType.GetTypeInfo().IsValueType); + + var setMethod = propertyInfo.SetMethod; + Debug.Assert(setMethod != null); + Debug.Assert(!setMethod.IsStatic); + Debug.Assert(setMethod.ReturnType == typeof(void)); + var parameters = setMethod.GetParameters(); + Debug.Assert(parameters.Length == 1); + + // Instance methods in the CLR can be turned into static methods where the first parameter + // is open over "target". This parameter is always passed by reference, so we have a code + // path for value types and a code path for reference types. + var typeInput = setMethod.DeclaringType; + var parameterType = parameters[0].ParameterType; + + // Create a delegate TDeclaringType -> { TDeclaringType.Property = TValue; } + var propertySetterAsAction = + setMethod.CreateDelegate(typeof(Action<,>).MakeGenericType(typeInput, parameterType)); + var callPropertySetterClosedGenericMethod = + CallPropertySetterOpenGenericMethod.MakeGenericMethod(typeInput, parameterType); + var callPropertySetterDelegate = + callPropertySetterClosedGenericMethod.CreateDelegate( + typeof(Action), propertySetterAsAction); + + return (Action)callPropertySetterDelegate; + } + + /// + /// Given an object, adds each instance property with a public get method as a key and its + /// associated value to a dictionary. + /// + /// If the object is already an instance, then a copy + /// is returned. + /// + /// + /// The implementation of PropertyHelper will cache the property accessors per-type. This is + /// faster when the same type is used multiple times with ObjectToDictionary. + /// + public static IDictionary ObjectToDictionary(object value) + { + var dictionary = value as IDictionary; + if (dictionary != null) + { + return new Dictionary(dictionary, StringComparer.OrdinalIgnoreCase); + } + + dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); + + if (value != null) + { + foreach (var helper in GetProperties(value.GetType())) + { + dictionary[helper.Name] = helper.GetValue(value); + } + } + + return dictionary; + } + + private static PropertyHelper CreateInstance(PropertyInfo property) + { + return new PropertyHelper(property); + } + + // Called via reflection + private static object CallPropertyGetter( + Func getter, + object target) + { + return getter((TDeclaringType)target); + } + + // Called via reflection + private static object CallPropertyGetterByReference( + ByRefFunc getter, + object target) + { + var unboxed = (TDeclaringType)target; + return getter(ref unboxed); + } + + // Called via reflection + private static object CallNullSafePropertyGetter( + Func getter, + object target) + { + if (target == null) + { + return null; + } + + return getter((TDeclaringType)target); + } + + // Called via reflection + private static object CallNullSafePropertyGetterByReference( + ByRefFunc getter, + object target) + { + if (target == null) + { + return null; + } + + var unboxed = (TDeclaringType)target; + return getter(ref unboxed); + } + + private static void CallPropertySetter( + Action setter, + object target, + object value) + { + setter((TDeclaringType)target, (TValue)value); + } + + protected static PropertyHelper[] GetVisibleProperties( + Type type, + Func createPropertyHelper, + ConcurrentDictionary allPropertiesCache, + ConcurrentDictionary visiblePropertiesCache) + { + PropertyHelper[] result; + if (visiblePropertiesCache.TryGetValue(type, out result)) + { + return result; + } + + // The simple and common case, this is normal POCO object - no need to allocate. + var allPropertiesDefinedOnType = true; + var allProperties = GetProperties(type, createPropertyHelper, allPropertiesCache); + foreach (var propertyHelper in allProperties) + { + if (propertyHelper.Property.DeclaringType != type) + { + allPropertiesDefinedOnType = false; + break; + } + } + + if (allPropertiesDefinedOnType) + { + result = allProperties; + visiblePropertiesCache.TryAdd(type, result); + return result; + } + + // There's some inherited properties here, so we need to check for hiding via 'new'. + var filteredProperties = new List(allProperties.Length); + foreach (var propertyHelper in allProperties) + { + var declaringType = propertyHelper.Property.DeclaringType; + if (declaringType == type) + { + filteredProperties.Add(propertyHelper); + continue; + } + + // If this property was declared on a base type then look for the definition closest to the + // the type to see if we should include it. + var ignoreProperty = false; + + // Walk up the hierarchy until we find the type that actually declares this + // PropertyInfo. + var currentTypeInfo = type.GetTypeInfo(); + var declaringTypeInfo = declaringType.GetTypeInfo(); + while (currentTypeInfo != null && currentTypeInfo != declaringTypeInfo) + { + // We've found a 'more proximal' public definition + var declaredProperty = currentTypeInfo.GetDeclaredProperty(propertyHelper.Name); + if (declaredProperty != null) + { + ignoreProperty = true; + break; + } + + currentTypeInfo = currentTypeInfo.BaseType?.GetTypeInfo(); + } + + if (!ignoreProperty) + { + filteredProperties.Add(propertyHelper); + } + } + + result = filteredProperties.ToArray(); + visiblePropertiesCache.TryAdd(type, result); + return result; + } + + protected static PropertyHelper[] GetProperties( + Type type, + Func createPropertyHelper, + ConcurrentDictionary cache) + { + // Unwrap nullable types. This means Nullable.Value and Nullable.HasValue will not be + // part of the sequence of properties returned by this method. + type = Nullable.GetUnderlyingType(type) ?? type; + + PropertyHelper[] helpers; + if (!cache.TryGetValue(type, out helpers)) + { + // We avoid loading indexed properties using the Where statement. + var properties = type.GetRuntimeProperties().Where(IsInterestingProperty); + + var typeInfo = type.GetTypeInfo(); + if (typeInfo.IsInterface) + { + // Reflection does not return information about inherited properties on the interface itself. + properties = properties.Concat(typeInfo.ImplementedInterfaces.SelectMany( + interfaceType => interfaceType.GetRuntimeProperties().Where(IsInterestingProperty))); + } + + helpers = properties.Select(p => createPropertyHelper(p)).ToArray(); + cache.TryAdd(type, helpers); + } + + return helpers; + } + + // Indexed properties are not useful (or valid) for grabbing properties off an object. + private static bool IsInterestingProperty(PropertyInfo property) + { + // For improving application startup time, do not use GetIndexParameters() api early in this check as it + // creates a copy of parameter array and also we would like to check for the presence of a get method + // and short circuit asap. + return property.GetMethod != null && + property.GetMethod.IsPublic && + !property.GetMethod.IsStatic && + property.GetMethod.GetParameters().Length == 0; + } + } +} diff --git a/src/Shared/RazorViews/AttributeValue.cs b/src/Shared/RazorViews/AttributeValue.cs new file mode 100644 index 000000000000..7a066a7040b3 --- /dev/null +++ b/src/Shared/RazorViews/AttributeValue.cs @@ -0,0 +1,38 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.Extensions.RazorViews +{ + internal class AttributeValue + { + public AttributeValue(string prefix, object value, bool literal) + { + Prefix = prefix; + Value = value; + Literal = literal; + } + + public string Prefix { get; } + + public object Value { get; } + + public bool Literal { get; } + + public static AttributeValue FromTuple(Tuple value) + { + return new AttributeValue(value.Item1, value.Item2, value.Item3); + } + + public static AttributeValue FromTuple(Tuple value) + { + return new AttributeValue(value.Item1, value.Item2, value.Item3); + } + + public static implicit operator AttributeValue(Tuple value) + { + return FromTuple(value); + } + } +} \ No newline at end of file diff --git a/src/Shared/RazorViews/BaseView.cs b/src/Shared/RazorViews/BaseView.cs new file mode 100644 index 000000000000..a171d8d1f215 --- /dev/null +++ b/src/Shared/RazorViews/BaseView.cs @@ -0,0 +1,279 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; + +namespace Microsoft.Extensions.RazorViews +{ + /// + /// Infrastructure + /// + internal abstract class BaseView + { + private static readonly Encoding UTF8NoBOM = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); + private readonly Stack _textWriterStack = new Stack(); + + /// + /// The request context + /// + protected HttpContext Context { get; private set; } + + /// + /// The request + /// + protected HttpRequest Request { get; private set; } + + /// + /// The response + /// + protected HttpResponse Response { get; private set; } + + /// + /// The output stream + /// + protected TextWriter Output { get; private set; } + + /// + /// Html encoder used to encode content. + /// + protected HtmlEncoder HtmlEncoder { get; set; } = HtmlEncoder.Default; + + /// + /// Url encoder used to encode content. + /// + protected UrlEncoder UrlEncoder { get; set; } = UrlEncoder.Default; + + /// + /// JavaScript encoder used to encode content. + /// + protected JavaScriptEncoder JavaScriptEncoder { get; set; } = JavaScriptEncoder.Default; + + /// + /// Execute an individual request + /// + /// + public async Task ExecuteAsync(HttpContext context) + { + Context = context; + Request = Context.Request; + Response = Context.Response; + Output = new StreamWriter(Response.Body, UTF8NoBOM, 4096, leaveOpen: true); + await ExecuteAsync(); + Output.Dispose(); + } + + /// + /// Execute an individual request + /// + public abstract Task ExecuteAsync(); + + protected virtual void PushWriter(TextWriter writer) + { + if (writer == null) + { + throw new ArgumentNullException(nameof(writer)); + } + + _textWriterStack.Push(Output); + Output = writer; + } + + protected virtual TextWriter PopWriter() + { + Output = _textWriterStack.Pop(); + return Output; + } + + /// + /// Write the given value without HTML encoding directly to . + /// + /// The to write. + protected void WriteLiteral(object value) + { + WriteLiteral(Convert.ToString(value, CultureInfo.InvariantCulture)); + } + + /// + /// Write the given value without HTML encoding directly to . + /// + /// The to write. + protected void WriteLiteral(string value) + { + if (!string.IsNullOrEmpty(value)) + { + Output.Write(value); + } + } + + private List AttributeValues { get; set; } + + protected void WriteAttributeValue(string thingy, int startPostion, object value, int endValue, int dealyo, bool yesno) + { + if (AttributeValues == null) + { + AttributeValues = new List(); + } + + AttributeValues.Add(value.ToString()); + } + + private string AttributeEnding { get; set; } + + protected void BeginWriteAttribute(string name, string begining, int startPosition, string ending, int endPosition, int thingy) + { + Debug.Assert(string.IsNullOrEmpty(AttributeEnding)); + + Output.Write(begining); + AttributeEnding = ending; + } + + protected void EndWriteAttribute() + { + Debug.Assert(!string.IsNullOrEmpty(AttributeEnding)); + + var attributes = string.Join(" ", AttributeValues); + Output.Write(attributes); + AttributeValues = null; + + Output.Write(AttributeEnding); + AttributeEnding = null; + } + + /// + /// Writes the given attribute to the given writer + /// + /// The name of the attribute to write + /// The value of the prefix + /// The value of the suffix + /// The s to write. + protected void WriteAttribute( + string name, + string leader, + string trailer, + params AttributeValue[] values) + { + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + + if (leader == null) + { + throw new ArgumentNullException(nameof(leader)); + } + + if (trailer == null) + { + throw new ArgumentNullException(nameof(trailer)); + } + + WriteLiteral(leader); + foreach (var value in values) + { + WriteLiteral(value.Prefix); + + // The special cases here are that the value we're writing might already be a string, or that the + // value might be a bool. If the value is the bool 'true' we want to write the attribute name + // instead of the string 'true'. If the value is the bool 'false' we don't want to write anything. + // Otherwise the value is another object (perhaps an HtmlString) and we'll ask it to format itself. + string stringValue; + if (value.Value is bool) + { + if ((bool)value.Value) + { + stringValue = name; + } + else + { + continue; + } + } + else + { + stringValue = value.Value as string; + } + + // Call the WriteTo(string) overload when possible + if (value.Literal && stringValue != null) + { + WriteLiteral(stringValue); + } + else if (value.Literal) + { + WriteLiteral(value.Value); + } + else if (stringValue != null) + { + Write(stringValue); + } + else + { + Write(value.Value); + } + } + WriteLiteral(trailer); + } + + /// + /// is invoked + /// + /// The to invoke + protected void Write(HelperResult result) + { + Write(result); + } + + /// + /// Writes the specified to . + /// + /// The to write. + /// + /// is invoked for types. + /// For all other types, the encoded result of is written to + /// . + /// + protected void Write(object value) + { + if (value is HelperResult helperResult) + { + helperResult.WriteTo(Output); + } + else + { + Write(Convert.ToString(value, CultureInfo.InvariantCulture)); + } + } + + /// + /// Writes the specified with HTML encoding to . + /// + /// The to write. + protected void Write(string value) + { + WriteLiteral(HtmlEncoder.Encode(value)); + } + + protected string HtmlEncodeAndReplaceLineBreaks(string input) + { + if (string.IsNullOrEmpty(input)) + { + return string.Empty; + } + + // Split on line breaks before passing it through the encoder. + return string.Join("
" + Environment.NewLine, + input.Split(new[] { "\r\n" }, StringSplitOptions.None) + .SelectMany(s => s.Split(new[] { '\r', '\n' }, StringSplitOptions.None)) + .Select(HtmlEncoder.Encode)); + } + } +} \ No newline at end of file diff --git a/src/Shared/RazorViews/HelperResult.cs b/src/Shared/RazorViews/HelperResult.cs new file mode 100644 index 000000000000..c79944aae646 --- /dev/null +++ b/src/Shared/RazorViews/HelperResult.cs @@ -0,0 +1,34 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; + +namespace Microsoft.Extensions.RazorViews +{ + /// + /// Represents a deferred write operation in a . + /// + internal class HelperResult + { + /// + /// Creates a new instance of . + /// + /// The delegate to invoke when is called. + public HelperResult(Action action) + { + WriteAction = action; + } + + public Action WriteAction { get; } + + /// + /// Method invoked to produce content from the . + /// + /// The instance to write to. + public void WriteTo(TextWriter writer) + { + WriteAction(writer); + } + } +} \ No newline at end of file diff --git a/src/Shared/SecurityHelper/SecurityHelper.cs b/src/Shared/SecurityHelper/SecurityHelper.cs new file mode 100644 index 000000000000..408ef6b22439 --- /dev/null +++ b/src/Shared/SecurityHelper/SecurityHelper.cs @@ -0,0 +1,40 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Linq; +using System.Security.Claims; + +namespace Microsoft.Extensions.Internal +{ + /// + /// Helper code used when implementing authentication middleware + /// + internal static class SecurityHelper + { + /// + /// Add all ClaimsIdentities from an additional ClaimPrincipal to the ClaimsPrincipal + /// Merges a new claims principal, placing all new identities first, and eliminating + /// any empty unauthenticated identities from context.User + /// + /// The containing existing . + /// The containing to be added. + public static ClaimsPrincipal MergeUserPrincipal(ClaimsPrincipal existingPrincipal, ClaimsPrincipal additionalPrincipal) + { + var newPrincipal = new ClaimsPrincipal(); + + // New principal identities go first + if (additionalPrincipal != null) + { + newPrincipal.AddIdentities(additionalPrincipal.Identities); + } + + // Then add any existing non empty or authenticated identities + if (existingPrincipal != null) + { + newPrincipal.AddIdentities(existingPrincipal.Identities.Where(i => i.IsAuthenticated || i.Claims.Any())); + } + return newPrincipal; + } + } +} diff --git a/src/Shared/StackTrace/ExceptionDetails/ExceptionDetails.cs b/src/Shared/StackTrace/ExceptionDetails/ExceptionDetails.cs new file mode 100644 index 000000000000..8862611136e7 --- /dev/null +++ b/src/Shared/StackTrace/ExceptionDetails/ExceptionDetails.cs @@ -0,0 +1,29 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; + +namespace Microsoft.Extensions.StackTrace.Sources +{ + /// + /// Contains details for individual exception messages. + /// + internal class ExceptionDetails + { + /// + /// An individual exception + /// + public Exception Error { get; set; } + + /// + /// The generated stack frames + /// + public IEnumerable StackFrames { get; set; } + + /// + /// Gets or sets the summary message. + /// + public string ErrorMessage { get; set; } + } +} diff --git a/src/Shared/StackTrace/ExceptionDetails/ExceptionDetailsProvider.cs b/src/Shared/StackTrace/ExceptionDetails/ExceptionDetailsProvider.cs new file mode 100644 index 000000000000..2d1dd20710ba --- /dev/null +++ b/src/Shared/StackTrace/ExceptionDetails/ExceptionDetailsProvider.cs @@ -0,0 +1,170 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using Microsoft.Extensions.FileProviders; + +namespace Microsoft.Extensions.StackTrace.Sources +{ + internal class ExceptionDetailsProvider + { + private readonly IFileProvider _fileProvider; + private readonly int _sourceCodeLineCount; + + public ExceptionDetailsProvider(IFileProvider fileProvider, int sourceCodeLineCount) + { + _fileProvider = fileProvider; + _sourceCodeLineCount = sourceCodeLineCount; + } + + public IEnumerable GetDetails(Exception exception) + { + var exceptions = FlattenAndReverseExceptionTree(exception); + + foreach (var ex in exceptions) + { + yield return new ExceptionDetails + { + Error = ex, + StackFrames = StackTraceHelper.GetFrames(ex) + .Select(frame => GetStackFrameSourceCodeInfo( + frame.MethodDisplayInfo.ToString(), + frame.FilePath, + frame.LineNumber)) + }; + } + } + + private static IEnumerable FlattenAndReverseExceptionTree(Exception ex) + { + // ReflectionTypeLoadException is special because the details are in + // the LoaderExceptions property + var typeLoadException = ex as ReflectionTypeLoadException; + if (typeLoadException != null) + { + var typeLoadExceptions = new List(); + foreach (var loadException in typeLoadException.LoaderExceptions) + { + typeLoadExceptions.AddRange(FlattenAndReverseExceptionTree(loadException)); + } + + typeLoadExceptions.Add(ex); + return typeLoadExceptions; + } + + var list = new List(); + if (ex is AggregateException aggregateException) + { + list.Add(ex); + foreach (var innerException in aggregateException.Flatten().InnerExceptions) + { + list.Add(innerException); + } + } + + else + { + while (ex != null) + { + list.Add(ex); + ex = ex.InnerException; + } + list.Reverse(); + } + + return list; + } + + // make it internal to enable unit testing + internal StackFrameSourceCodeInfo GetStackFrameSourceCodeInfo(string method, string filePath, int lineNumber) + { + var stackFrame = new StackFrameSourceCodeInfo + { + Function = method, + File = filePath, + Line = lineNumber + }; + + if (string.IsNullOrEmpty(stackFrame.File)) + { + return stackFrame; + } + + IEnumerable lines = null; + if (File.Exists(stackFrame.File)) + { + lines = File.ReadLines(stackFrame.File); + } + else + { + // Handle relative paths and embedded files + var fileInfo = _fileProvider.GetFileInfo(stackFrame.File); + if (fileInfo.Exists) + { + // ReadLines doesn't accept a stream. Use ReadLines as its more efficient + // relative to reading lines via stream reader + if (!string.IsNullOrEmpty(fileInfo.PhysicalPath)) + { + lines = File.ReadLines(fileInfo.PhysicalPath); + } + else + { + lines = ReadLines(fileInfo); + } + } + } + + if (lines != null) + { + ReadFrameContent(stackFrame, lines, stackFrame.Line, stackFrame.Line); + } + + return stackFrame; + } + + // make it internal to enable unit testing + internal void ReadFrameContent( + StackFrameSourceCodeInfo frame, + IEnumerable allLines, + int errorStartLineNumberInFile, + int errorEndLineNumberInFile) + { + // Get the line boundaries in the file to be read and read all these lines at once into an array. + var preErrorLineNumberInFile = Math.Max(errorStartLineNumberInFile - _sourceCodeLineCount, 1); + var postErrorLineNumberInFile = errorEndLineNumberInFile + _sourceCodeLineCount; + var codeBlock = allLines + .Skip(preErrorLineNumberInFile - 1) + .Take(postErrorLineNumberInFile - preErrorLineNumberInFile + 1) + .ToArray(); + + var numOfErrorLines = (errorEndLineNumberInFile - errorStartLineNumberInFile) + 1; + var errorStartLineNumberInArray = errorStartLineNumberInFile - preErrorLineNumberInFile; + + frame.PreContextLine = preErrorLineNumberInFile; + frame.PreContextCode = codeBlock.Take(errorStartLineNumberInArray).ToArray(); + frame.ContextCode = codeBlock + .Skip(errorStartLineNumberInArray) + .Take(numOfErrorLines) + .ToArray(); + frame.PostContextCode = codeBlock + .Skip(errorStartLineNumberInArray + numOfErrorLines) + .ToArray(); + } + + private static IEnumerable ReadLines(IFileInfo fileInfo) + { + using (var reader = new StreamReader(fileInfo.CreateReadStream())) + { + string line; + while ((line = reader.ReadLine()) != null) + { + yield return line; + } + } + } + } +} diff --git a/src/Shared/StackTrace/StackFrame/MethodDisplayInfo.cs b/src/Shared/StackTrace/StackFrame/MethodDisplayInfo.cs new file mode 100644 index 000000000000..b1c0ccc18871 --- /dev/null +++ b/src/Shared/StackTrace/StackFrame/MethodDisplayInfo.cs @@ -0,0 +1,49 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Microsoft.Extensions.StackTrace.Sources +{ + internal class MethodDisplayInfo + { + public string DeclaringTypeName { get; set; } + + public string Name { get; set; } + + public string GenericArguments { get; set; } + + public string SubMethod { get; set; } + + public IEnumerable Parameters { get; set; } + + public override string ToString() + { + var builder = new StringBuilder(); + if (!string.IsNullOrEmpty(DeclaringTypeName)) + { + builder + .Append(DeclaringTypeName) + .Append("."); + } + + builder.Append(Name); + builder.Append(GenericArguments); + + builder.Append("("); + builder.Append(string.Join(", ", Parameters.Select(p => p.ToString()))); + builder.Append(")"); + + if (!string.IsNullOrEmpty(SubMethod)) + { + builder.Append("+"); + builder.Append(SubMethod); + builder.Append("()"); + } + + return builder.ToString(); + } + } +} diff --git a/src/Shared/StackTrace/StackFrame/ParameterDisplayInfo.cs b/src/Shared/StackTrace/StackFrame/ParameterDisplayInfo.cs new file mode 100644 index 000000000000..1199a8386d2f --- /dev/null +++ b/src/Shared/StackTrace/StackFrame/ParameterDisplayInfo.cs @@ -0,0 +1,33 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Text; + +namespace Microsoft.Extensions.StackTrace.Sources +{ + internal class ParameterDisplayInfo + { + public string Name { get; set; } + + public string Type { get; set; } + + public string Prefix { get; set; } + + public override string ToString() + { + var builder = new StringBuilder(); + if (!string.IsNullOrEmpty(Prefix)) + { + builder + .Append(Prefix) + .Append(" "); + } + + builder.Append(Type); + builder.Append(" "); + builder.Append(Name); + + return builder.ToString(); + } + } +} diff --git a/src/Shared/StackTrace/StackFrame/PortablePdbReader.cs b/src/Shared/StackTrace/StackFrame/PortablePdbReader.cs new file mode 100644 index 000000000000..ff6a4947f80e --- /dev/null +++ b/src/Shared/StackTrace/StackFrame/PortablePdbReader.cs @@ -0,0 +1,135 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using System.Reflection.PortableExecutable; + +namespace Microsoft.Extensions.StackTrace.Sources +{ + internal class PortablePdbReader : IDisposable + { + private readonly Dictionary _cache = + new Dictionary(StringComparer.Ordinal); + + public void PopulateStackFrame(StackFrameInfo frameInfo, MethodBase method, int IlOffset) + { + if (method.Module.Assembly.IsDynamic) + { + return; + } + + var metadataReader = GetMetadataReader(method.Module.Assembly.Location); + + if (metadataReader == null) + { + return; + } + + var methodToken = MetadataTokens.Handle(method.MetadataToken); + + Debug.Assert(methodToken.Kind == HandleKind.MethodDefinition); + + var handle = ((MethodDefinitionHandle)methodToken).ToDebugInformationHandle(); + + if (!handle.IsNil) + { + var methodDebugInfo = metadataReader.GetMethodDebugInformation(handle); + var sequencePoints = methodDebugInfo.GetSequencePoints(); + SequencePoint? bestPointSoFar = null; + + foreach (var point in sequencePoints) + { + if (point.Offset > IlOffset) + { + break; + } + + if (point.StartLine != SequencePoint.HiddenLine) + { + bestPointSoFar = point; + } + } + + if (bestPointSoFar.HasValue) + { + frameInfo.LineNumber = bestPointSoFar.Value.StartLine; + frameInfo.FilePath = metadataReader.GetString(metadataReader.GetDocument(bestPointSoFar.Value.Document).Name); + } + } + } + + private MetadataReader GetMetadataReader(string assemblyPath) + { + MetadataReaderProvider provider = null; + if (!_cache.TryGetValue(assemblyPath, out provider)) + { + var pdbPath = GetPdbPath(assemblyPath); + + if (!string.IsNullOrEmpty(pdbPath) && File.Exists(pdbPath) && IsPortable(pdbPath)) + { + var pdbStream = File.OpenRead(pdbPath); + provider = MetadataReaderProvider.FromPortablePdbStream(pdbStream); + } + + _cache[assemblyPath] = provider; + } + + return provider?.GetMetadataReader(); + } + + private static string GetPdbPath(string assemblyPath) + { + if (string.IsNullOrEmpty(assemblyPath)) + { + return null; + } + + if (File.Exists(assemblyPath)) + { + var peStream = File.OpenRead(assemblyPath); + + using (var peReader = new PEReader(peStream)) + { + foreach (var entry in peReader.ReadDebugDirectory()) + { + if (entry.Type == DebugDirectoryEntryType.CodeView) + { + var codeViewData = peReader.ReadCodeViewDebugDirectoryData(entry); + var peDirectory = Path.GetDirectoryName(assemblyPath); + return Path.Combine(peDirectory, Path.GetFileName(codeViewData.Path)); + } + } + } + } + + return null; + } + + private static bool IsPortable(string pdbPath) + { + using (var pdbStream = File.OpenRead(pdbPath)) + { + return pdbStream.ReadByte() == 'B' && + pdbStream.ReadByte() == 'S' && + pdbStream.ReadByte() == 'J' && + pdbStream.ReadByte() == 'B'; + } + } + + public void Dispose() + { + foreach (var entry in _cache) + { + entry.Value?.Dispose(); + } + + _cache.Clear(); + } + } +} diff --git a/src/Shared/StackTrace/StackFrame/StackFrameInfo.cs b/src/Shared/StackTrace/StackFrame/StackFrameInfo.cs new file mode 100644 index 000000000000..ffd91f213c08 --- /dev/null +++ b/src/Shared/StackTrace/StackFrame/StackFrameInfo.cs @@ -0,0 +1,18 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Diagnostics; + +namespace Microsoft.Extensions.StackTrace.Sources +{ + internal class StackFrameInfo + { + public int LineNumber { get; set; } + + public string FilePath { get; set; } + + public StackFrame StackFrame { get; set; } + + public MethodDisplayInfo MethodDisplayInfo { get; set; } + } +} diff --git a/src/Shared/StackTrace/StackFrame/StackFrameSourceCodeInfo.cs b/src/Shared/StackTrace/StackFrame/StackFrameSourceCodeInfo.cs new file mode 100644 index 000000000000..2932e083b132 --- /dev/null +++ b/src/Shared/StackTrace/StackFrame/StackFrameSourceCodeInfo.cs @@ -0,0 +1,54 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.Extensions.StackTrace.Sources +{ + /// + /// Contains the source code where the exception occurred. + /// + internal class StackFrameSourceCodeInfo + { + /// + /// Function containing instruction + /// + public string Function { get; set; } + + /// + /// File containing the instruction + /// + public string File { get; set; } + + /// + /// The line number of the instruction + /// + public int Line { get; set; } + + /// + /// The line preceding the frame line + /// + public int PreContextLine { get; set; } + + /// + /// Lines of code before the actual error line(s). + /// + public IEnumerable PreContextCode { get; set; } = Enumerable.Empty(); + + /// + /// Line(s) of code responsible for the error. + /// + public IEnumerable ContextCode { get; set; } = Enumerable.Empty(); + + /// + /// Lines of code after the actual error line(s). + /// + public IEnumerable PostContextCode { get; set; } = Enumerable.Empty(); + + /// + /// Specific error details for this stack frame. + /// + public string ErrorDetails { get; set; } + } +} diff --git a/src/Shared/StackTrace/StackFrame/StackTraceHelper.cs b/src/Shared/StackTrace/StackFrame/StackTraceHelper.cs new file mode 100644 index 000000000000..5ce9a4090387 --- /dev/null +++ b/src/Shared/StackTrace/StackFrame/StackTraceHelper.cs @@ -0,0 +1,261 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.ExceptionServices; +using Microsoft.Extensions.Internal; + +namespace Microsoft.Extensions.StackTrace.Sources +{ + internal class StackTraceHelper + { + public static IList GetFrames(Exception exception) + { + var frames = new List(); + + if (exception == null) + { + return frames; + } + + using (var portablePdbReader = new PortablePdbReader()) + { + var needFileInfo = true; + var stackTrace = new System.Diagnostics.StackTrace(exception, needFileInfo); + var stackFrames = stackTrace.GetFrames(); + + if (stackFrames == null) + { + return frames; + } + + for (var i = 0; i < stackFrames.Length; i++) + { + var frame = stackFrames[i]; + var method = frame.GetMethod(); + + // Always show last stackFrame + if (!ShowInStackTrace(method) && i < stackFrames.Length - 1) + { + continue; + } + + var stackFrame = new StackFrameInfo + { + StackFrame = frame, + FilePath = frame.GetFileName(), + LineNumber = frame.GetFileLineNumber(), + MethodDisplayInfo = GetMethodDisplayString(frame.GetMethod()), + }; + + if (string.IsNullOrEmpty(stackFrame.FilePath)) + { + // .NET Framework and older versions of mono don't support portable PDBs + // so we read it manually to get file name and line information + portablePdbReader.PopulateStackFrame(stackFrame, method, frame.GetILOffset()); + } + + frames.Add(stackFrame); + } + + return frames; + } + } + + internal static MethodDisplayInfo GetMethodDisplayString(MethodBase method) + { + // Special case: no method available + if (method == null) + { + return null; + } + + var methodDisplayInfo = new MethodDisplayInfo(); + + // Type name + var type = method.DeclaringType; + + var methodName = method.Name; + + if (type != null && type.IsDefined(typeof(CompilerGeneratedAttribute)) && + (typeof(IAsyncStateMachine).IsAssignableFrom(type) || typeof(IEnumerator).IsAssignableFrom(type))) + { + // Convert StateMachine methods to correct overload +MoveNext() + if (TryResolveStateMachineMethod(ref method, out type)) + { + methodDisplayInfo.SubMethod = methodName; + } + } + // ResolveStateMachineMethod may have set declaringType to null + if (type != null) + { + methodDisplayInfo.DeclaringTypeName = TypeNameHelper.GetTypeDisplayName(type, includeGenericParameterNames: true); + } + + // Method name + methodDisplayInfo.Name = method.Name; + if (method.IsGenericMethod) + { + var genericArguments = string.Join(", ", method.GetGenericArguments() + .Select(arg => TypeNameHelper.GetTypeDisplayName(arg, fullName: false, includeGenericParameterNames: true))); + methodDisplayInfo.GenericArguments += "<" + genericArguments + ">"; + } + + // Method parameters + methodDisplayInfo.Parameters = method.GetParameters().Select(parameter => + { + var parameterType = parameter.ParameterType; + + var prefix = string.Empty; + if (parameter.IsOut) + { + prefix = "out"; + } + else if (parameterType != null && parameterType.IsByRef) + { + prefix = "ref"; + } + + var parameterTypeString = "?"; + if (parameterType != null) + { + if (parameterType.IsByRef) + { + parameterType = parameterType.GetElementType(); + } + + parameterTypeString = TypeNameHelper.GetTypeDisplayName(parameterType, fullName: false, includeGenericParameterNames: true); + } + + return new ParameterDisplayInfo + { + Prefix = prefix, + Name = parameter.Name, + Type = parameterTypeString, + }; + }); + + return methodDisplayInfo; + } + + private static bool ShowInStackTrace(MethodBase method) + { + Debug.Assert(method != null); + + // Don't show any methods marked with the StackTraceHiddenAttribute + // https://github.com/dotnet/coreclr/pull/14652 + if (HasStackTraceHiddenAttribute(method)) + { + return false; + } + + + var type = method.DeclaringType; + if (type == null) + { + return true; + } + + if (HasStackTraceHiddenAttribute(type)) + { + return false; + } + + // Fallbacks for runtime pre-StackTraceHiddenAttribute + if (type == typeof(ExceptionDispatchInfo) && method.Name == "Throw") + { + return false; + } + else if (type == typeof(TaskAwaiter) || + type == typeof(TaskAwaiter<>) || + type == typeof(ConfiguredTaskAwaitable.ConfiguredTaskAwaiter) || + type == typeof(ConfiguredTaskAwaitable<>.ConfiguredTaskAwaiter)) + { + switch (method.Name) + { + case "HandleNonSuccessAndDebuggerNotification": + case "ThrowForNonSuccess": + case "ValidateEnd": + case "GetResult": + return false; + } + } + + return true; + } + + private static bool TryResolveStateMachineMethod(ref MethodBase method, out Type declaringType) + { + Debug.Assert(method != null); + Debug.Assert(method.DeclaringType != null); + + declaringType = method.DeclaringType; + + var parentType = declaringType.DeclaringType; + if (parentType == null) + { + return false; + } + + var methods = parentType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly); + if (methods == null) + { + return false; + } + + foreach (var candidateMethod in methods) + { + var attributes = candidateMethod.GetCustomAttributes(); + if (attributes == null) + { + continue; + } + + foreach (var asma in attributes) + { + if (asma.StateMachineType == declaringType) + { + method = candidateMethod; + declaringType = candidateMethod.DeclaringType; + // Mark the iterator as changed; so it gets the + annotation of the original method + // async statemachines resolve directly to their builder methods so aren't marked as changed + return asma is IteratorStateMachineAttribute; + } + } + } + + return false; + } + + private static bool HasStackTraceHiddenAttribute(MemberInfo memberInfo) + { + IList attributes; + try + { + // Accessing MembmerInfo.GetCustomAttributesData throws for some types (such as types in dynamically generated assemblies). + // We'll skip looking up StackTraceHiddenAttributes on such types. + attributes = memberInfo.GetCustomAttributesData(); + } + catch + { + return false; + } + + for (var i = 0; i < attributes.Count; i++) + { + if (attributes[i].AttributeType.Name == "StackTraceHiddenAttribute") + { + return true; + } + } + + return false; + } + } +} diff --git a/src/Shared/TypeNameHelper/TypeNameHelper.cs b/src/Shared/TypeNameHelper/TypeNameHelper.cs new file mode 100644 index 000000000000..1cc746864658 --- /dev/null +++ b/src/Shared/TypeNameHelper/TypeNameHelper.cs @@ -0,0 +1,160 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Text; +using System.Collections.Generic; + +namespace Microsoft.Extensions.Internal +{ + internal class TypeNameHelper + { + private static readonly Dictionary _builtInTypeNames = new Dictionary + { + { typeof(void), "void" }, + { typeof(bool), "bool" }, + { typeof(byte), "byte" }, + { typeof(char), "char" }, + { typeof(decimal), "decimal" }, + { typeof(double), "double" }, + { typeof(float), "float" }, + { typeof(int), "int" }, + { typeof(long), "long" }, + { typeof(object), "object" }, + { typeof(sbyte), "sbyte" }, + { typeof(short), "short" }, + { typeof(string), "string" }, + { typeof(uint), "uint" }, + { typeof(ulong), "ulong" }, + { typeof(ushort), "ushort" } + }; + + public static string GetTypeDisplayName(object item, bool fullName = true) + { + return item == null ? null : GetTypeDisplayName(item.GetType(), fullName); + } + + /// + /// Pretty print a type name. + /// + /// The . + /// true to print a fully qualified name. + /// true to include generic parameter names. + /// The pretty printed type name. + public static string GetTypeDisplayName(Type type, bool fullName = true, bool includeGenericParameterNames = false) + { + var builder = new StringBuilder(); + ProcessType(builder, type, new DisplayNameOptions(fullName, includeGenericParameterNames)); + return builder.ToString(); + } + + private static void ProcessType(StringBuilder builder, Type type, DisplayNameOptions options) + { + if (type.IsGenericType) + { + var genericArguments = type.GetGenericArguments(); + ProcessGenericType(builder, type, genericArguments, genericArguments.Length, options); + } + else if (type.IsArray) + { + ProcessArrayType(builder, type, options); + } + else if (_builtInTypeNames.TryGetValue(type, out var builtInName)) + { + builder.Append(builtInName); + } + else if (type.IsGenericParameter) + { + if (options.IncludeGenericParameterNames) + { + builder.Append(type.Name); + } + } + else + { + builder.Append(options.FullName ? type.FullName : type.Name); + } + } + + private static void ProcessArrayType(StringBuilder builder, Type type, DisplayNameOptions options) + { + var innerType = type; + while (innerType.IsArray) + { + innerType = innerType.GetElementType(); + } + + ProcessType(builder, innerType, options); + + while (type.IsArray) + { + builder.Append('['); + builder.Append(',', type.GetArrayRank() - 1); + builder.Append(']'); + type = type.GetElementType(); + } + } + + private static void ProcessGenericType(StringBuilder builder, Type type, Type[] genericArguments, int length, DisplayNameOptions options) + { + var offset = 0; + if (type.IsNested) + { + offset = type.DeclaringType.GetGenericArguments().Length; + } + + if (options.FullName) + { + if (type.IsNested) + { + ProcessGenericType(builder, type.DeclaringType, genericArguments, offset, options); + builder.Append('+'); + } + else if (!string.IsNullOrEmpty(type.Namespace)) + { + builder.Append(type.Namespace); + builder.Append('.'); + } + } + + var genericPartIndex = type.Name.IndexOf('`'); + if (genericPartIndex <= 0) + { + builder.Append(type.Name); + return; + } + + builder.Append(type.Name, 0, genericPartIndex); + + builder.Append('<'); + for (var i = offset; i < length; i++) + { + ProcessType(builder, genericArguments[i], options); + if (i + 1 == length) + { + continue; + } + + builder.Append(','); + if (options.IncludeGenericParameterNames || !genericArguments[i + 1].IsGenericParameter) + { + builder.Append(' '); + } + } + builder.Append('>'); + } + + private struct DisplayNameOptions + { + public DisplayNameOptions(bool fullName, bool includeGenericParameterNames) + { + FullName = fullName; + IncludeGenericParameterNames = includeGenericParameterNames; + } + + public bool FullName { get; } + + public bool IncludeGenericParameterNames { get; } + } + } +} diff --git a/src/Shared/ValueStopwatch/ValueStopwatch.cs b/src/Shared/ValueStopwatch/ValueStopwatch.cs new file mode 100644 index 000000000000..f99a084aebe0 --- /dev/null +++ b/src/Shared/ValueStopwatch/ValueStopwatch.cs @@ -0,0 +1,39 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Diagnostics; + +namespace Microsoft.Extensions.Internal +{ + internal struct ValueStopwatch + { + private static readonly double TimestampToTicks = TimeSpan.TicksPerSecond / (double)Stopwatch.Frequency; + + private long _startTimestamp; + + public bool IsActive => _startTimestamp != 0; + + private ValueStopwatch(long startTimestamp) + { + _startTimestamp = startTimestamp; + } + + public static ValueStopwatch StartNew() => new ValueStopwatch(Stopwatch.GetTimestamp()); + + public TimeSpan GetElapsedTime() + { + // Start timestamp can't be zero in an initialized ValueStopwatch. It would have to be literally the first thing executed when the machine boots to be 0. + // So it being 0 is a clear indication of default(ValueStopwatch) + if (!IsActive) + { + throw new InvalidOperationException("An uninitialized, or 'default', ValueStopwatch cannot be used to get elapsed time."); + } + + var end = Stopwatch.GetTimestamp(); + var timestampDelta = end - _startTimestamp; + var ticks = (long)(TimestampToTicks * timestampDelta); + return new TimeSpan(ticks); + } + } +} diff --git a/src/Shared/WebEncoders/Properties/EncoderResources.cs b/src/Shared/WebEncoders/Properties/EncoderResources.cs new file mode 100644 index 000000000000..3474ae82c5b7 --- /dev/null +++ b/src/Shared/WebEncoders/Properties/EncoderResources.cs @@ -0,0 +1,38 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Globalization; + +namespace Microsoft.Extensions.WebEncoders.Sources +{ + // TODO using a resx file. project.json, unfortunately, fails to embed resx files when there are also compile items + // in the contentFiles section. Revisit once we convert repos to MSBuild + internal static class EncoderResources + { + /// + /// Invalid {0}, {1} or {2} length. + /// + internal static readonly string WebEncoders_InvalidCountOffsetOrLength = "Invalid {0}, {1} or {2} length."; + + /// + /// Malformed input: {0} is an invalid input length. + /// + internal static readonly string WebEncoders_MalformedInput = "Malformed input: {0} is an invalid input length."; + + /// + /// Invalid {0}, {1} or {2} length. + /// + internal static string FormatWebEncoders_InvalidCountOffsetOrLength(object p0, object p1, object p2) + { + return string.Format(CultureInfo.CurrentCulture, WebEncoders_InvalidCountOffsetOrLength, p0, p1, p2); + } + + /// + /// Malformed input: {0} is an invalid input length. + /// + internal static string FormatWebEncoders_MalformedInput(object p0) + { + return string.Format(CultureInfo.CurrentCulture, WebEncoders_MalformedInput, p0); + } + } +} diff --git a/src/Shared/WebEncoders/WebEncoders.cs b/src/Shared/WebEncoders/WebEncoders.cs new file mode 100644 index 000000000000..17068ae67a55 --- /dev/null +++ b/src/Shared/WebEncoders/WebEncoders.cs @@ -0,0 +1,388 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Diagnostics; +using System.Globalization; +using Microsoft.Extensions.WebEncoders.Sources; + +#if WebEncoders_In_WebUtilities +namespace Microsoft.AspNetCore.WebUtilities +#else +namespace Microsoft.Extensions.Internal +#endif +{ + /// + /// Contains utility APIs to assist with common encoding and decoding operations. + /// +#if WebEncoders_In_WebUtilities + public +#else + internal +#endif + static class WebEncoders + { + private static readonly byte[] EmptyBytes = new byte[0]; + + /// + /// Decodes a base64url-encoded string. + /// + /// The base64url-encoded input to decode. + /// The base64url-decoded form of the input. + /// + /// The input must not contain any whitespace or padding characters. + /// Throws if the input is malformed. + /// + public static byte[] Base64UrlDecode(string input) + { + if (input == null) + { + throw new ArgumentNullException(nameof(input)); + } + + return Base64UrlDecode(input, offset: 0, count: input.Length); + } + + /// + /// Decodes a base64url-encoded substring of a given string. + /// + /// A string containing the base64url-encoded input to decode. + /// The position in at which decoding should begin. + /// The number of characters in to decode. + /// The base64url-decoded form of the input. + /// + /// The input must not contain any whitespace or padding characters. + /// Throws if the input is malformed. + /// + public static byte[] Base64UrlDecode(string input, int offset, int count) + { + if (input == null) + { + throw new ArgumentNullException(nameof(input)); + } + + ValidateParameters(input.Length, nameof(input), offset, count); + + // Special-case empty input + if (count == 0) + { + return EmptyBytes; + } + + // Create array large enough for the Base64 characters, not just shorter Base64-URL-encoded form. + var buffer = new char[GetArraySizeRequiredToDecode(count)]; + + return Base64UrlDecode(input, offset, buffer, bufferOffset: 0, count: count); + } + + /// + /// Decodes a base64url-encoded into a byte[]. + /// + /// A string containing the base64url-encoded input to decode. + /// The position in at which decoding should begin. + /// + /// Scratch buffer to hold the s to decode. Array must be large enough to hold + /// and characters as well as Base64 padding + /// characters. Content is not preserved. + /// + /// + /// The offset into at which to begin writing the s to decode. + /// + /// The number of characters in to decode. + /// The base64url-decoded form of the . + /// + /// The input must not contain any whitespace or padding characters. + /// Throws if the input is malformed. + /// + public static byte[] Base64UrlDecode(string input, int offset, char[] buffer, int bufferOffset, int count) + { + if (input == null) + { + throw new ArgumentNullException(nameof(input)); + } + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer)); + } + + ValidateParameters(input.Length, nameof(input), offset, count); + if (bufferOffset < 0) + { + throw new ArgumentOutOfRangeException(nameof(bufferOffset)); + } + + if (count == 0) + { + return EmptyBytes; + } + + // Assumption: input is base64url encoded without padding and contains no whitespace. + + var paddingCharsToAdd = GetNumBase64PaddingCharsToAddForDecode(count); + var arraySizeRequired = checked(count + paddingCharsToAdd); + Debug.Assert(arraySizeRequired % 4 == 0, "Invariant: Array length must be a multiple of 4."); + + if (buffer.Length - bufferOffset < arraySizeRequired) + { + throw new ArgumentException( + string.Format( + CultureInfo.CurrentCulture, + EncoderResources.WebEncoders_InvalidCountOffsetOrLength, + nameof(count), + nameof(bufferOffset), + nameof(input)), + nameof(count)); + } + + // Copy input into buffer, fixing up '-' -> '+' and '_' -> '/'. + var i = bufferOffset; + for (var j = offset; i - bufferOffset < count; i++, j++) + { + var ch = input[j]; + if (ch == '-') + { + buffer[i] = '+'; + } + else if (ch == '_') + { + buffer[i] = '/'; + } + else + { + buffer[i] = ch; + } + } + + // Add the padding characters back. + for (; paddingCharsToAdd > 0; i++, paddingCharsToAdd--) + { + buffer[i] = '='; + } + + // Decode. + // If the caller provided invalid base64 chars, they'll be caught here. + return Convert.FromBase64CharArray(buffer, bufferOffset, arraySizeRequired); + } + + /// + /// Gets the minimum char[] size required for decoding of characters + /// with the method. + /// + /// The number of characters to decode. + /// + /// The minimum char[] size required for decoding of characters. + /// + public static int GetArraySizeRequiredToDecode(int count) + { + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + + if (count == 0) + { + return 0; + } + + var numPaddingCharsToAdd = GetNumBase64PaddingCharsToAddForDecode(count); + + return checked(count + numPaddingCharsToAdd); + } + + /// + /// Encodes using base64url encoding. + /// + /// The binary input to encode. + /// The base64url-encoded form of . + public static string Base64UrlEncode(byte[] input) + { + if (input == null) + { + throw new ArgumentNullException(nameof(input)); + } + + return Base64UrlEncode(input, offset: 0, count: input.Length); + } + + /// + /// Encodes using base64url encoding. + /// + /// The binary input to encode. + /// The offset into at which to begin encoding. + /// The number of bytes from to encode. + /// The base64url-encoded form of . + public static string Base64UrlEncode(byte[] input, int offset, int count) + { + if (input == null) + { + throw new ArgumentNullException(nameof(input)); + } + + ValidateParameters(input.Length, nameof(input), offset, count); + + // Special-case empty input + if (count == 0) + { + return string.Empty; + } + + var buffer = new char[GetArraySizeRequiredToEncode(count)]; + var numBase64Chars = Base64UrlEncode(input, offset, buffer, outputOffset: 0, count: count); + + return new String(buffer, startIndex: 0, length: numBase64Chars); + } + + /// + /// Encodes using base64url encoding. + /// + /// The binary input to encode. + /// The offset into at which to begin encoding. + /// + /// Buffer to receive the base64url-encoded form of . Array must be large enough to + /// hold characters and the full base64-encoded form of + /// , including padding characters. + /// + /// + /// The offset into at which to begin writing the base64url-encoded form of + /// . + /// + /// The number of bytes from to encode. + /// + /// The number of characters written to , less any padding characters. + /// + public static int Base64UrlEncode(byte[] input, int offset, char[] output, int outputOffset, int count) + { + if (input == null) + { + throw new ArgumentNullException(nameof(input)); + } + if (output == null) + { + throw new ArgumentNullException(nameof(output)); + } + + ValidateParameters(input.Length, nameof(input), offset, count); + if (outputOffset < 0) + { + throw new ArgumentOutOfRangeException(nameof(outputOffset)); + } + + var arraySizeRequired = GetArraySizeRequiredToEncode(count); + if (output.Length - outputOffset < arraySizeRequired) + { + throw new ArgumentException( + string.Format( + CultureInfo.CurrentCulture, + EncoderResources.WebEncoders_InvalidCountOffsetOrLength, + nameof(count), + nameof(outputOffset), + nameof(output)), + nameof(count)); + } + + // Special-case empty input. + if (count == 0) + { + return 0; + } + + // Use base64url encoding with no padding characters. See RFC 4648, Sec. 5. + + // Start with default Base64 encoding. + var numBase64Chars = Convert.ToBase64CharArray(input, offset, count, output, outputOffset); + + // Fix up '+' -> '-' and '/' -> '_'. Drop padding characters. + for (var i = outputOffset; i - outputOffset < numBase64Chars; i++) + { + var ch = output[i]; + if (ch == '+') + { + output[i] = '-'; + } + else if (ch == '/') + { + output[i] = '_'; + } + else if (ch == '=') + { + // We've reached a padding character; truncate the remainder. + return i - outputOffset; + } + } + + return numBase64Chars; + } + + /// + /// Get the minimum output char[] size required for encoding + /// s with the method. + /// + /// The number of characters to encode. + /// + /// The minimum output char[] size required for encoding s. + /// + public static int GetArraySizeRequiredToEncode(int count) + { + var numWholeOrPartialInputBlocks = checked(count + 2) / 3; + return checked(numWholeOrPartialInputBlocks * 4); + } + + private static int GetNumBase64PaddingCharsInString(string str) + { + // Assumption: input contains a well-formed base64 string with no whitespace. + + // base64 guaranteed have 0 - 2 padding characters. + if (str[str.Length - 1] == '=') + { + if (str[str.Length - 2] == '=') + { + return 2; + } + return 1; + } + return 0; + } + + private static int GetNumBase64PaddingCharsToAddForDecode(int inputLength) + { + switch (inputLength % 4) + { + case 0: + return 0; + case 2: + return 2; + case 3: + return 1; + default: + throw new FormatException( + string.Format( + CultureInfo.CurrentCulture, + EncoderResources.WebEncoders_MalformedInput, + inputLength)); + } + } + + private static void ValidateParameters(int bufferLength, string inputName, int offset, int count) + { + if (offset < 0) + { + throw new ArgumentOutOfRangeException(nameof(offset)); + } + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + if (bufferLength - offset < count) + { + throw new ArgumentException( + string.Format( + CultureInfo.CurrentCulture, + EncoderResources.WebEncoders_InvalidCountOffsetOrLength, + nameof(count), + nameof(offset), + inputName), + nameof(count)); + } + } + } +} diff --git a/src/Shared/test/Shared.Tests/ArgumentEscaperTests.cs b/src/Shared/test/Shared.Tests/ArgumentEscaperTests.cs new file mode 100644 index 000000000000..a706b05bfcb9 --- /dev/null +++ b/src/Shared/test/Shared.Tests/ArgumentEscaperTests.cs @@ -0,0 +1,24 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Xunit; + +namespace Microsoft.Extensions.CommandLineUtils +{ + public class ArgumentEscaperTests + { + [Theory] + [InlineData(new[] { "one", "two", "three" }, "one two three")] + [InlineData(new[] { "line1\nline2", "word1\tword2" }, "\"line1\nline2\" \"word1\tword2\"")] + [InlineData(new[] { "with spaces" }, "\"with spaces\"")] + [InlineData(new[] { @"with\backslash" }, @"with\backslash")] + [InlineData(new[] { @"""quotedwith\backslash""" }, @"\""quotedwith\backslash\""")] + [InlineData(new[] { @"C:\Users\" }, @"C:\Users\")] + [InlineData(new[] { @"C:\Program Files\dotnet\" }, @"""C:\Program Files\dotnet\\""")] + [InlineData(new[] { @"backslash\""preceedingquote" }, @"backslash\\\""preceedingquote")] + public void EscapesArguments(string[] args, string expected) + { + Assert.Equal(expected, ArgumentEscaper.EscapeAndConcatenate(args)); + } + } +} diff --git a/src/Shared/test/Shared.Tests/CertificateManagerTests.cs b/src/Shared/test/Shared.Tests/CertificateManagerTests.cs new file mode 100644 index 000000000000..613f5c966f15 --- /dev/null +++ b/src/Shared/test/Shared.Tests/CertificateManagerTests.cs @@ -0,0 +1,304 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#if NETCOREAPP2_0 || NETCOREAPP2_1 + +using System; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.AspNetCore.Certificates.Generation.Tests +{ + public class CertificateManagerTests + { + public CertificateManagerTests(ITestOutputHelper output) + { + Output = output; + } + + public const string TestCertificateSubject = "CN=aspnet.test"; + + public ITestOutputHelper Output { get; } + + [Fact] + public void EnsureCreateHttpsCertificate_CreatesACertificate_WhenThereAreNoHttpsCertificates() + { + try + { + // Arrange + const string CertificateName = nameof(EnsureCreateHttpsCertificate_CreatesACertificate_WhenThereAreNoHttpsCertificates) + ".cer"; + var manager = new CertificateManager(); + + manager.RemoveAllCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, TestCertificateSubject); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + manager.RemoveAllCertificates(CertificatePurpose.HTTPS, StoreName.Root, StoreLocation.CurrentUser, TestCertificateSubject); + } + + // Act + DateTimeOffset now = DateTimeOffset.UtcNow; + now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset); + var result = manager.EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1), CertificateName, trust: false, subject: TestCertificateSubject); + + // Assert + Assert.Equal(EnsureCertificateResult.Succeeded, result); + Assert.True(File.Exists(CertificateName)); + + var exportedCertificate = new X509Certificate2(File.ReadAllBytes(CertificateName)); + Assert.NotNull(exportedCertificate); + Assert.False(exportedCertificate.HasPrivateKey); + + var httpsCertificates = manager.ListCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, isValid: false); + var httpsCertificate = Assert.Single(httpsCertificates, c => c.Subject == TestCertificateSubject); + Assert.True(httpsCertificate.HasPrivateKey); + Assert.Equal(TestCertificateSubject, httpsCertificate.Subject); + Assert.Equal(TestCertificateSubject, httpsCertificate.Issuer); + Assert.Equal("sha256RSA", httpsCertificate.SignatureAlgorithm.FriendlyName); + Assert.Equal("1.2.840.113549.1.1.11", httpsCertificate.SignatureAlgorithm.Value); + + Assert.Equal(now.LocalDateTime, httpsCertificate.NotBefore); + Assert.Equal(now.AddYears(1).LocalDateTime, httpsCertificate.NotAfter); + Assert.Contains( + httpsCertificate.Extensions.OfType(), + e => e is X509BasicConstraintsExtension basicConstraints && + basicConstraints.Critical == true && + basicConstraints.CertificateAuthority == false && + basicConstraints.HasPathLengthConstraint == false && + basicConstraints.PathLengthConstraint == 0); + + Assert.Contains( + httpsCertificate.Extensions.OfType(), + e => e is X509KeyUsageExtension keyUsage && + keyUsage.Critical == true && + keyUsage.KeyUsages == X509KeyUsageFlags.KeyEncipherment); + + Assert.Contains( + httpsCertificate.Extensions.OfType(), + e => e is X509EnhancedKeyUsageExtension enhancedKeyUsage && + enhancedKeyUsage.Critical == true && + enhancedKeyUsage.EnhancedKeyUsages.OfType().Single() is Oid keyUsage && + keyUsage.Value == "1.3.6.1.5.5.7.3.1"); + + // Subject alternative name + Assert.Contains( + httpsCertificate.Extensions.OfType(), + e => e.Critical == true && + e.Oid.Value == "2.5.29.17"); + + // ASP.NET HTTPS Development certificate extension + Assert.Contains( + httpsCertificate.Extensions.OfType(), + e => e.Critical == false && + e.Oid.Value == "1.3.6.1.4.1.311.84.1.1" && + Encoding.ASCII.GetString(e.RawData) == "ASP.NET Core HTTPS development certificate"); + + Assert.Equal(httpsCertificate.GetCertHashString(), exportedCertificate.GetCertHashString()); + + } + catch (Exception e) + { + Output.WriteLine(e.Message); + ListCertificates(Output); + throw; + } + } + + private void ListCertificates(ITestOutputHelper output) + { + using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser)) + { + store.Open(OpenFlags.ReadOnly); + var certificates = store.Certificates; + foreach (var certificate in certificates) + { + Output.WriteLine($"Certificate: '{Convert.ToBase64String(certificate.Export(X509ContentType.Cert))}'."); + certificate.Dispose(); + } + + store.Close(); + } + } + + [Fact] + public void EnsureCreateHttpsCertificate_DoesNotCreateACertificate_WhenThereIsAnExistingHttpsCertificates() + { + // Arrange + const string CertificateName = nameof(EnsureCreateHttpsCertificate_DoesNotCreateACertificate_WhenThereIsAnExistingHttpsCertificates) + ".pfx"; + var certificatePassword = Guid.NewGuid().ToString(); + + var manager = new CertificateManager(); + + manager.RemoveAllCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, TestCertificateSubject); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + manager.RemoveAllCertificates(CertificatePurpose.HTTPS, StoreName.Root, StoreLocation.CurrentUser, TestCertificateSubject); + } + + DateTimeOffset now = DateTimeOffset.UtcNow; + now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset); + manager.EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1), path: null, trust: false, subject: TestCertificateSubject); + + var httpsCertificate = manager.ListCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, isValid: false).Single(c => c.Subject == TestCertificateSubject); + + // Act + var result = manager.EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1), CertificateName, trust: false, includePrivateKey: true, password: certificatePassword, subject: TestCertificateSubject); + + // Assert + Assert.Equal(EnsureCertificateResult.ValidCertificatePresent, result); + Assert.True(File.Exists(CertificateName)); + + var exportedCertificate = new X509Certificate2(File.ReadAllBytes(CertificateName), certificatePassword); + Assert.NotNull(exportedCertificate); + Assert.True(exportedCertificate.HasPrivateKey); + + + Assert.Equal(httpsCertificate.GetCertHashString(), exportedCertificate.GetCertHashString()); + } + + [Fact(Skip = "Requires user interaction")] + public void EnsureAspNetCoreHttpsDevelopmentCertificate_ReturnsCorrectResult_WhenUserCancelsTrustStepOnWindows() + { + var manager = new CertificateManager(); + + manager.RemoveAllCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, TestCertificateSubject); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + manager.RemoveAllCertificates(CertificatePurpose.HTTPS, StoreName.Root, StoreLocation.CurrentUser, TestCertificateSubject); + } + + DateTimeOffset now = DateTimeOffset.UtcNow; + now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset); + var trustFailed = manager.EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1), path: null, trust: true, subject: TestCertificateSubject); + + Assert.Equal(EnsureCertificateResult.UserCancelledTrustStep, trustFailed); + } + + [Fact(Skip = "Requires user interaction")] + public void EnsureAspNetCoreHttpsDevelopmentCertificate_CanRemoveCertificates() + { + var manager = new CertificateManager(); + + DateTimeOffset now = DateTimeOffset.UtcNow; + now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset); + manager.EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1), path: null, trust: true, subject: TestCertificateSubject); + + manager.CleanupHttpsCertificates(TestCertificateSubject); + + Assert.Empty(manager.ListCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, isValid: false).Where(c => c.Subject == TestCertificateSubject)); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + Assert.Empty(manager.ListCertificates(CertificatePurpose.HTTPS, StoreName.Root, StoreLocation.CurrentUser, isValid: false).Where(c => c.Subject == TestCertificateSubject)); + } + } + + [Fact] + public void EnsureCreateIdentityTokenSigningCertificate_CreatesACertificate_WhenThereAreNoHttpsCertificates() + { + // Arrange + const string CertificateName = nameof(EnsureCreateIdentityTokenSigningCertificate_CreatesACertificate_WhenThereAreNoHttpsCertificates) + ".cer"; + var manager = new CertificateManager(); + + manager.RemoveAllCertificates(CertificatePurpose.Signing, StoreName.My, StoreLocation.CurrentUser, TestCertificateSubject); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + manager.RemoveAllCertificates(CertificatePurpose.Signing, StoreName.Root, StoreLocation.CurrentUser, TestCertificateSubject); + } + + // Act + DateTimeOffset now = DateTimeOffset.UtcNow; + now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset); + var result = manager.EnsureAspNetCoreApplicationTokensDevelopmentCertificate(now, now.AddYears(1), CertificateName, trust: false, subject: TestCertificateSubject); + + // Assert + Assert.Equal(EnsureCertificateResult.Succeeded, result); + Assert.True(File.Exists(CertificateName)); + + var exportedCertificate = new X509Certificate2(File.ReadAllBytes(CertificateName)); + Assert.NotNull(exportedCertificate); + Assert.False(exportedCertificate.HasPrivateKey); + + var identityCertificates = manager.ListCertificates(CertificatePurpose.Signing, StoreName.My, StoreLocation.CurrentUser, isValid: false); + var identityCertificate = Assert.Single(identityCertificates, i => i.Subject == TestCertificateSubject); + Assert.True(identityCertificate.HasPrivateKey); + Assert.Equal(TestCertificateSubject, identityCertificate.Subject); + Assert.Equal(TestCertificateSubject, identityCertificate.Issuer); + Assert.Equal("sha256RSA", identityCertificate.SignatureAlgorithm.FriendlyName); + Assert.Equal("1.2.840.113549.1.1.11", identityCertificate.SignatureAlgorithm.Value); + + Assert.Equal(now.LocalDateTime, identityCertificate.NotBefore); + Assert.Equal(now.AddYears(1).LocalDateTime, identityCertificate.NotAfter); + Assert.Contains( + identityCertificate.Extensions.OfType(), + e => e is X509BasicConstraintsExtension basicConstraints && + basicConstraints.Critical == true && + basicConstraints.CertificateAuthority == false && + basicConstraints.HasPathLengthConstraint == false && + basicConstraints.PathLengthConstraint == 0); + + Assert.Contains( + identityCertificate.Extensions.OfType(), + e => e is X509KeyUsageExtension keyUsage && + keyUsage.Critical == true && + keyUsage.KeyUsages == X509KeyUsageFlags.DigitalSignature); + + Assert.Contains( + identityCertificate.Extensions.OfType(), + e => e is X509EnhancedKeyUsageExtension enhancedKeyUsage && + enhancedKeyUsage.Critical == true && + enhancedKeyUsage.EnhancedKeyUsages.OfType().Single() is Oid keyUsage && + keyUsage.Value == "1.3.6.1.5.5.7.3.1"); + + // ASP.NET Core Identity Json Web Token signing development certificate + Assert.Contains( + identityCertificate.Extensions.OfType(), + e => e.Critical == false && + e.Oid.Value == "1.3.6.1.4.1.311.84.1.2" && + Encoding.ASCII.GetString(e.RawData) == "ASP.NET Core Identity Json Web Token signing development certificate"); + + Assert.Equal(identityCertificate.GetCertHashString(), exportedCertificate.GetCertHashString()); + } + + [Fact] + public void EnsureCreateIdentityTokenSigningCertificate_DoesNotCreateACertificate_WhenThereIsAnExistingHttpsCertificates() + { + // Arrange + const string CertificateName = nameof(EnsureCreateIdentityTokenSigningCertificate_DoesNotCreateACertificate_WhenThereIsAnExistingHttpsCertificates) + ".pfx"; + var certificatePassword = Guid.NewGuid().ToString(); + + var manager = new CertificateManager(); + + manager.RemoveAllCertificates(CertificatePurpose.Signing, StoreName.My, StoreLocation.CurrentUser, TestCertificateSubject); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + manager.RemoveAllCertificates(CertificatePurpose.Signing, StoreName.Root, StoreLocation.CurrentUser, TestCertificateSubject); + } + + DateTimeOffset now = DateTimeOffset.UtcNow; + now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset); + manager.EnsureAspNetCoreApplicationTokensDevelopmentCertificate(now, now.AddYears(1), path: null, trust: false, subject: TestCertificateSubject); + + var identityTokenSigningCertificates = manager.ListCertificates(CertificatePurpose.Signing, StoreName.My, StoreLocation.CurrentUser, isValid: false).Single(c => c.Subject == TestCertificateSubject); + + // Act + var result = manager.EnsureAspNetCoreApplicationTokensDevelopmentCertificate(now, now.AddYears(1), CertificateName, trust: false, includePrivateKey: true, password: certificatePassword, subject: TestCertificateSubject); + + // Assert + Assert.Equal(EnsureCertificateResult.ValidCertificatePresent, result); + Assert.True(File.Exists(CertificateName)); + + var exportedCertificate = new X509Certificate2(File.ReadAllBytes(CertificateName), certificatePassword); + Assert.NotNull(exportedCertificate); + Assert.True(exportedCertificate.HasPrivateKey); + + Assert.Equal(identityTokenSigningCertificates.GetCertHashString(), exportedCertificate.GetCertHashString()); + } + } +} + +#endif \ No newline at end of file diff --git a/src/Shared/test/Shared.Tests/ClosedGenericMatcherTest.cs b/src/Shared/test/Shared.Tests/ClosedGenericMatcherTest.cs new file mode 100644 index 000000000000..e71a7926921a --- /dev/null +++ b/src/Shared/test/Shared.Tests/ClosedGenericMatcherTest.cs @@ -0,0 +1,360 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using Xunit; + +namespace Microsoft.Extensions.Internal +{ + public class ClosedGenericMatcherTest + { + // queryType, interfaceType, expectedResult + public static TheoryData ExtractGenericInterfaceDataSet + { + get + { + return new TheoryData + { + // Closed generic types that match given open generic type. + { + typeof(IEnumerable), + typeof(IEnumerable<>), + typeof(IEnumerable) + }, + { + typeof(IReadOnlyList), + typeof(IReadOnlyList<>), + typeof(IReadOnlyList) + }, + { + typeof(KeyValuePair), + typeof(KeyValuePair<,>), + typeof(KeyValuePair) + }, + // Closed generic interfaces that implement sub-interface of given open generic type. + { + typeof(ICollection), + typeof(IEnumerable<>), + typeof(IEnumerable) + }, + { + typeof(IReadOnlyList), + typeof(IEnumerable<>), + typeof(IEnumerable) + }, + { + typeof(IDictionary), + typeof(IEnumerable<>), + typeof(IEnumerable>) + }, + // Class that implements closed generic based on given open generic interface. + { + typeof(BaseClass), + typeof(IDictionary<,>), + typeof(IDictionary) + }, + { + typeof(BaseClass), + typeof(IEquatable<>), + typeof(IEquatable) + }, + { + typeof(BaseClass), + typeof(ICollection<>), + typeof(ICollection>) + }, + // Derived class that implements closed generic based on given open generic interface. + { + typeof(DerivedClass), + typeof(IDictionary<,>), + typeof(IDictionary) + }, + { + typeof(DerivedClass), + typeof(IEquatable<>), + typeof(IEquatable) + }, + { + typeof(DerivedClass), + typeof(ICollection<>), + typeof(ICollection>) + }, + // Derived class that also implements another interface. + { + typeof(DerivedClassWithComparable), + typeof(IDictionary<,>), + typeof(IDictionary) + }, + { + typeof(DerivedClassWithComparable), + typeof(IEquatable<>), + typeof(IEquatable) + }, + { + typeof(DerivedClassWithComparable), + typeof(ICollection<>), + typeof(ICollection>) + }, + { + typeof(DerivedClassWithComparable), + typeof(IComparable<>), + typeof(IComparable) + }, + // Derived class using system implementation. + { + typeof(DerivedClassFromSystemImplementation), + typeof(ICollection<>), + typeof(ICollection) + }, + { + typeof(DerivedClassFromSystemImplementation), + typeof(IReadOnlyList<>), + typeof(IReadOnlyList) + }, + { + typeof(DerivedClassFromSystemImplementation), + typeof(IEnumerable<>), + typeof(IEnumerable) + }, + // Not given an open generic type. + { + typeof(IEnumerable), + typeof(IEnumerable), + null + }, + { + typeof(IEnumerable), + typeof(IEnumerable), + null + }, + { + typeof(IReadOnlyList), + typeof(BaseClass), + null + }, + { + typeof(KeyValuePair<,>), + typeof(KeyValuePair), + null + }, + // Not a match. + { + typeof(IEnumerable), + typeof(IReadOnlyList<>), + null + }, + { + typeof(IList), + typeof(IReadOnlyList<>), + null + }, + { + typeof(IDictionary), + typeof(KeyValuePair<,>), + null + }, + }; + } + } + + [Theory] + [MemberData(nameof(ExtractGenericInterfaceDataSet))] + public void ExtractGenericInterface_ReturnsExpectedType( + Type queryType, + Type interfaceType, + Type expectedResult) + { + // Arrange & Act + var result = ClosedGenericMatcher.ExtractGenericInterface(queryType, interfaceType); + + // Assert + Assert.Equal(expectedResult, result); + } + + // IEnumerable is preferred because it is defined on the more-derived type. + [Fact] + public void ExtractGenericInterface_MultipleDefinitionsInherited() + { + // Arrange + var type = typeof(TwoIEnumerableImplementationsInherited); + + // Act + var result = ClosedGenericMatcher.ExtractGenericInterface(type, typeof(IEnumerable<>)); + + // Sort + Assert.Equal(typeof(IEnumerable), result); + } + + // IEnumerable is preferred because we sort by Ordinal on the full name. + [Fact] + public void ExtractGenericInterface_MultipleDefinitionsOnSameType() + { + // Arrange + var type = typeof(TwoIEnumerableImplementationsOnSameClass); + + // Act + var result = ClosedGenericMatcher.ExtractGenericInterface(type, typeof(IEnumerable<>)); + + // Sort + Assert.Equal(typeof(IEnumerable), result); + } + + private class TwoIEnumerableImplementationsOnSameClass : IEnumerable, IEnumerable + { + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + } + + private class TwoIEnumerableImplementationsInherited : List, IEnumerable + { + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + } + + private class BaseClass : IDictionary, IEquatable + { + object IDictionary.this[string key] + { + get + { + throw new NotImplementedException(); + } + + set + { + throw new NotImplementedException(); + } + } + + int ICollection>.Count + { + get + { + throw new NotImplementedException(); + } + } + + bool ICollection>.IsReadOnly + { + get + { + throw new NotImplementedException(); + } + } + + ICollection IDictionary.Keys + { + get + { + throw new NotImplementedException(); + } + } + + ICollection IDictionary.Values + { + get + { + throw new NotImplementedException(); + } + } + + public bool Equals(BaseClass other) + { + throw new NotImplementedException(); + } + + void ICollection>.Add(KeyValuePair item) + { + throw new NotImplementedException(); + } + + void IDictionary.Add(string key, object value) + { + throw new NotImplementedException(); + } + + void ICollection>.Clear() + { + throw new NotImplementedException(); + } + + bool ICollection>.Contains(KeyValuePair item) + { + throw new NotImplementedException(); + } + + bool IDictionary.ContainsKey(string key) + { + throw new NotImplementedException(); + } + + void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) + { + throw new NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + + IEnumerator> IEnumerable>.GetEnumerator() + { + throw new NotImplementedException(); + } + + bool ICollection>.Remove(KeyValuePair item) + { + throw new NotImplementedException(); + } + + bool IDictionary.Remove(string key) + { + throw new NotImplementedException(); + } + + bool IDictionary.TryGetValue(string key, out object value) + { + throw new NotImplementedException(); + } + } + + private class DerivedClass : BaseClass + { + } + + private class DerivedClassWithComparable : DerivedClass, IComparable + { + public int CompareTo(DerivedClassWithComparable other) + { + throw new NotImplementedException(); + } + } + + private class DerivedClassFromSystemImplementation : Collection + { + } + } +} \ No newline at end of file diff --git a/src/Shared/test/Shared.Tests/CommandLineApplicationTests.cs b/src/Shared/test/Shared.Tests/CommandLineApplicationTests.cs new file mode 100644 index 000000000000..44b7ebdaa819 --- /dev/null +++ b/src/Shared/test/Shared.Tests/CommandLineApplicationTests.cs @@ -0,0 +1,693 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; +using System.Linq; +using System.Text; +using Microsoft.Extensions.CommandLineUtils; +using Xunit; + +namespace Microsoft.Extensions.Internal +{ + public class CommandLineApplicationTests + { + [Fact] + public void CommandNameCanBeMatched() + { + var called = false; + + var app = new CommandLineApplication(); + app.Command("test", c => + { + c.OnExecute(() => + { + called = true; + return 5; + }); + }); + + var result = app.Execute("test"); + Assert.Equal(5, result); + Assert.True(called); + } + + [Fact] + public void RemainingArgsArePassed() + { + CommandArgument first = null; + CommandArgument second = null; + + var app = new CommandLineApplication(); + + app.Command("test", c => + { + first = c.Argument("first", "First argument"); + second = c.Argument("second", "Second argument"); + c.OnExecute(() => 0); + }); + + app.Execute("test", "one", "two"); + + Assert.Equal("one", first.Value); + Assert.Equal("two", second.Value); + } + + [Fact] + public void ExtraArgumentCausesException() + { + CommandArgument first = null; + CommandArgument second = null; + + var app = new CommandLineApplication(); + + app.Command("test", c => + { + first = c.Argument("first", "First argument"); + second = c.Argument("second", "Second argument"); + c.OnExecute(() => 0); + }); + + var ex = Assert.Throws(() => app.Execute("test", "one", "two", "three")); + + Assert.Contains("three", ex.Message); + } + + [Fact] + public void UnknownCommandCausesException() + { + var app = new CommandLineApplication(); + + app.Command("test", c => + { + c.Argument("first", "First argument"); + c.Argument("second", "Second argument"); + c.OnExecute(() => 0); + }); + + var ex = Assert.Throws(() => app.Execute("test2", "one", "two", "three")); + + Assert.Contains("test2", ex.Message); + } + + [Fact] + public void MultipleValuesArgumentConsumesAllArgumentValues() + { + CommandArgument argument = null; + + var app = new CommandLineApplication(); + + app.Command("test", c => + { + argument = c.Argument("arg", "Argument that allows multiple values", multipleValues: true); + c.OnExecute(() => 0); + }); + + app.Execute("test", "one", "two", "three", "four", "five"); + + Assert.Equal(new[] { "one", "two", "three", "four", "five" }, argument.Values); + } + + [Fact] + public void MultipleValuesArgumentConsumesAllRemainingArgumentValues() + { + CommandArgument first = null; + CommandArgument second = null; + CommandArgument third = null; + + var app = new CommandLineApplication(); + + app.Command("test", c => + { + first = c.Argument("first", "First argument"); + second = c.Argument("second", "Second argument"); + third = c.Argument("third", "Third argument that allows multiple values", multipleValues: true); + c.OnExecute(() => 0); + }); + + app.Execute("test", "one", "two", "three", "four", "five"); + + Assert.Equal("one", first.Value); + Assert.Equal("two", second.Value); + Assert.Equal(new[] { "three", "four", "five" }, third.Values); + } + + [Fact] + public void MultipleValuesArgumentMustBeTheLastArgument() + { + var app = new CommandLineApplication(); + app.Argument("first", "First argument", multipleValues: true); + var ex = Assert.Throws(() => app.Argument("second", "Second argument")); + + Assert.Contains($"The last argument 'first' accepts multiple values. No more argument can be added.", + ex.Message); + } + + [Fact] + public void OptionSwitchMayBeProvided() + { + CommandOption first = null; + CommandOption second = null; + + var app = new CommandLineApplication(); + + app.Command("test", c => + { + first = c.Option("--first ", "First argument", CommandOptionType.SingleValue); + second = c.Option("--second ", "Second argument", CommandOptionType.SingleValue); + c.OnExecute(() => 0); + }); + + app.Execute("test", "--first", "one", "--second", "two"); + + Assert.Equal("one", first.Values[0]); + Assert.Equal("two", second.Values[0]); + } + + [Fact] + public void OptionValueMustBeProvided() + { + CommandOption first = null; + + var app = new CommandLineApplication(); + + app.Command("test", c => + { + first = c.Option("--first ", "First argument", CommandOptionType.SingleValue); + c.OnExecute(() => 0); + }); + + var ex = Assert.Throws(() => app.Execute("test", "--first")); + + Assert.Contains($"Missing value for option '{first.LongName}'", ex.Message); + } + + [Fact] + public void ValuesMayBeAttachedToSwitch() + { + CommandOption first = null; + CommandOption second = null; + + var app = new CommandLineApplication(); + + app.Command("test", c => + { + first = c.Option("--first ", "First argument", CommandOptionType.SingleValue); + second = c.Option("--second ", "Second argument", CommandOptionType.SingleValue); + c.OnExecute(() => 0); + }); + + app.Execute("test", "--first=one", "--second:two"); + + Assert.Equal("one", first.Values[0]); + Assert.Equal("two", second.Values[0]); + } + + [Fact] + public void ShortNamesMayBeDefined() + { + CommandOption first = null; + CommandOption second = null; + + var app = new CommandLineApplication(); + + app.Command("test", c => + { + first = c.Option("-1 --first ", "First argument", CommandOptionType.SingleValue); + second = c.Option("-2 --second ", "Second argument", CommandOptionType.SingleValue); + c.OnExecute(() => 0); + }); + + app.Execute("test", "-1=one", "-2", "two"); + + Assert.Equal("one", first.Values[0]); + Assert.Equal("two", second.Values[0]); + } + + [Fact] + public void ThrowsExceptionOnUnexpectedCommandOrArgumentByDefault() + { + var unexpectedArg = "UnexpectedArg"; + var app = new CommandLineApplication(); + + app.Command("test", c => + { + c.OnExecute(() => 0); + }); + + var exception = Assert.Throws(() => app.Execute("test", unexpectedArg)); + Assert.Equal($"Unrecognized command or argument '{unexpectedArg}'", exception.Message); + } + + [Fact] + public void AllowNoThrowBehaviorOnUnexpectedArgument() + { + var unexpectedArg = "UnexpectedArg"; + var app = new CommandLineApplication(); + + var testCmd = app.Command("test", c => + { + c.OnExecute(() => 0); + }, + throwOnUnexpectedArg: false); + + // (does not throw) + app.Execute("test", unexpectedArg); + var arg = Assert.Single(testCmd.RemainingArguments); + Assert.Equal(unexpectedArg, arg); + } + + [Fact] + public void ThrowsExceptionOnUnexpectedLongOptionByDefault() + { + var unexpectedOption = "--UnexpectedOption"; + var app = new CommandLineApplication(); + + app.Command("test", c => + { + c.OnExecute(() => 0); + }); + + var exception = Assert.Throws(() => app.Execute("test", unexpectedOption)); + Assert.Equal($"Unrecognized option '{unexpectedOption}'", exception.Message); + } + + [Fact] + public void AllowNoThrowBehaviorOnUnexpectedLongOption() + { + var unexpectedOption = "--UnexpectedOption"; + var app = new CommandLineApplication(); + + var testCmd = app.Command("test", c => + { + c.OnExecute(() => 0); + }, + throwOnUnexpectedArg: false); + + // (does not throw) + app.Execute("test", unexpectedOption); + var arg = Assert.Single(testCmd.RemainingArguments); + Assert.Equal(unexpectedOption, arg); + } + + [Fact] + public void ThrowsExceptionOnUnexpectedShortOptionByDefault() + { + var unexpectedOption = "-uexp"; + var app = new CommandLineApplication(); + + app.Command("test", c => + { + c.OnExecute(() => 0); + }); + + var exception = Assert.Throws(() => app.Execute("test", unexpectedOption)); + Assert.Equal($"Unrecognized option '{unexpectedOption}'", exception.Message); + } + + [Fact] + public void AllowNoThrowBehaviorOnUnexpectedShortOption() + { + var unexpectedOption = "-uexp"; + var app = new CommandLineApplication(); + + var testCmd = app.Command("test", c => + { + c.OnExecute(() => 0); + }, + throwOnUnexpectedArg: false); + + // (does not throw) + app.Execute("test", unexpectedOption); + var arg = Assert.Single(testCmd.RemainingArguments); + Assert.Equal(unexpectedOption, arg); + } + + [Fact] + public void ThrowsExceptionOnUnexpectedSymbolOptionByDefault() + { + var unexpectedOption = "-?"; + var app = new CommandLineApplication(); + + app.Command("test", c => + { + c.OnExecute(() => 0); + }); + + var exception = Assert.Throws(() => app.Execute("test", unexpectedOption)); + Assert.Equal($"Unrecognized option '{unexpectedOption}'", exception.Message); + } + + [Fact] + public void AllowNoThrowBehaviorOnUnexpectedSymbolOption() + { + var unexpectedOption = "-?"; + var app = new CommandLineApplication(); + + var testCmd = app.Command("test", c => + { + c.OnExecute(() => 0); + }, + throwOnUnexpectedArg: false); + + // (does not throw) + app.Execute("test", unexpectedOption); + var arg = Assert.Single(testCmd.RemainingArguments); + Assert.Equal(unexpectedOption, arg); + } + + [Fact] + public void ThrowsExceptionOnUnexpectedOptionBeforeValidSubcommandByDefault() + { + var unexpectedOption = "--unexpected"; + CommandLineApplication subCmd = null; + var app = new CommandLineApplication(); + + app.Command("k", c => + { + subCmd = c.Command("run", _ => { }); + c.OnExecute(() => 0); + }); + + var exception = Assert.Throws(() => app.Execute("k", unexpectedOption, "run")); + Assert.Equal($"Unrecognized option '{unexpectedOption}'", exception.Message); + } + + [Fact] + public void AllowNoThrowBehaviorOnUnexpectedOptionAfterSubcommand() + { + var unexpectedOption = "--unexpected"; + CommandLineApplication subCmd = null; + var app = new CommandLineApplication(); + + var testCmd = app.Command("k", c => + { + subCmd = c.Command("run", _ => { }, throwOnUnexpectedArg: false); + c.OnExecute(() => 0); + }); + + // (does not throw) + app.Execute("k", "run", unexpectedOption); + Assert.Empty(testCmd.RemainingArguments); + var arg = Assert.Single(subCmd.RemainingArguments); + Assert.Equal(unexpectedOption, arg); + } + + [Fact] + public void OptionsCanBeInherited() + { + var app = new CommandLineApplication(); + var optionA = app.Option("-a|--option-a", "", CommandOptionType.SingleValue, inherited: true); + string optionAValue = null; + + var optionB = app.Option("-b", "", CommandOptionType.SingleValue, inherited: false); + + var subcmd = app.Command("subcmd", c => + { + c.OnExecute(() => + { + optionAValue = optionA.Value(); + return 0; + }); + }); + + Assert.Equal(2, app.GetOptions().Count()); + Assert.Single(subcmd.GetOptions()); + + app.Execute("-a", "A1", "subcmd"); + Assert.Equal("A1", optionAValue); + + Assert.Throws(() => app.Execute("subcmd", "-b", "B")); + + Assert.Contains("-a|--option-a", subcmd.GetHelpText()); + } + + [Fact] + public void NestedOptionConflictThrows() + { + var app = new CommandLineApplication(); + app.Option("-a|--always", "Top-level", CommandOptionType.SingleValue, inherited: true); + app.Command("subcmd", c => + { + c.Option("-a|--ask", "Nested", CommandOptionType.SingleValue); + }); + + Assert.Throws(() => app.Execute("subcmd", "-a", "b")); + } + + [Fact] + public void OptionsWithSameName() + { + var app = new CommandLineApplication(); + var top = app.Option("-a|--always", "Top-level", CommandOptionType.SingleValue, inherited: false); + CommandOption nested = null; + app.Command("subcmd", c => + { + nested = c.Option("-a|--ask", "Nested", CommandOptionType.SingleValue); + }); + + app.Execute("-a", "top"); + Assert.Equal("top", top.Value()); + Assert.Null(nested.Value()); + + top.Values.Clear(); + + app.Execute("subcmd", "-a", "nested"); + Assert.Null(top.Value()); + Assert.Equal("nested", nested.Value()); + } + + [Fact] + public void NestedInheritedOptions() + { + string globalOptionValue = null, nest1OptionValue = null, nest2OptionValue = null; + + var app = new CommandLineApplication(); + CommandLineApplication subcmd2 = null; + var g = app.Option("-g|--global", "Global option", CommandOptionType.SingleValue, inherited: true); + var subcmd1 = app.Command("lvl1", s1 => + { + var n1 = s1.Option("--nest1", "Nested one level down", CommandOptionType.SingleValue, inherited: true); + subcmd2 = s1.Command("lvl2", s2 => + { + var n2 = s2.Option("--nest2", "Nested one level down", CommandOptionType.SingleValue, inherited: true); + s2.HelpOption("-h|--help"); + s2.OnExecute(() => + { + globalOptionValue = g.Value(); + nest1OptionValue = n1.Value(); + nest2OptionValue = n2.Value(); + return 0; + }); + }); + }); + + Assert.DoesNotContain(app.GetOptions(), o => o.LongName == "nest2"); + Assert.DoesNotContain(app.GetOptions(), o => o.LongName == "nest1"); + Assert.Contains(app.GetOptions(), o => o.LongName == "global"); + + Assert.DoesNotContain(subcmd1.GetOptions(), o => o.LongName == "nest2"); + Assert.Contains(subcmd1.GetOptions(), o => o.LongName == "nest1"); + Assert.Contains(subcmd1.GetOptions(), o => o.LongName == "global"); + + Assert.Contains(subcmd2.GetOptions(), o => o.LongName == "nest2"); + Assert.Contains(subcmd2.GetOptions(), o => o.LongName == "nest1"); + Assert.Contains(subcmd2.GetOptions(), o => o.LongName == "global"); + + Assert.Throws(() => app.Execute("--nest2", "N2", "--nest1", "N1", "-g", "G")); + Assert.Throws(() => app.Execute("lvl1", "--nest2", "N2", "--nest1", "N1", "-g", "G")); + + app.Execute("lvl1", "lvl2", "--nest2", "N2", "-g", "G", "--nest1", "N1"); + Assert.Equal("G", globalOptionValue); + Assert.Equal("N1", nest1OptionValue); + Assert.Equal("N2", nest2OptionValue); + } + + [Theory] + [InlineData(new string[0], new string[0], null)] + [InlineData(new[] { "--" }, new string[0], null)] + [InlineData(new[] { "-t", "val" }, new string[0], "val")] + [InlineData(new[] { "-t", "val", "--" }, new string[0], "val")] + [InlineData(new[] { "--top", "val", "--", "a" }, new[] { "a" }, "val")] + [InlineData(new[] { "--", "a", "--top", "val" }, new[] { "a", "--top", "val" }, null)] + [InlineData(new[] { "-t", "val", "--", "a", "--", "b" }, new[] { "a", "--", "b" }, "val")] + [InlineData(new[] { "--", "--help" }, new[] { "--help" }, null)] + [InlineData(new[] { "--", "--version" }, new[] { "--version" }, null)] + public void ArgumentSeparator(string[] input, string[] expectedRemaining, string topLevelValue) + { + var app = new CommandLineApplication(throwOnUnexpectedArg: false) + { + AllowArgumentSeparator = true + }; + var optHelp = app.HelpOption("--help"); + var optVersion = app.VersionOption("--version", "1", "1.0"); + var optTop = app.Option("-t|--top ", "arg for command", CommandOptionType.SingleValue); + app.Execute(input); + + Assert.Equal(topLevelValue, optTop.Value()); + Assert.False(optHelp.HasValue()); + Assert.False(optVersion.HasValue()); + Assert.Equal(expectedRemaining, app.RemainingArguments.ToArray()); + } + + [Fact] + public void HelpTextIgnoresHiddenItems() + { + var app = new CommandLineApplication() + { + Name = "ninja-app", + Description = "You can't see it until it is too late" + }; + + app.Command("star", c => + { + c.Option("--points

", "How many", CommandOptionType.MultipleValue); + c.ShowInHelpText = false; + }); + app.Option("--smile", "Be a nice ninja", CommandOptionType.NoValue, o => { o.ShowInHelpText = false; }); + + var a = app.Argument("name", "Pseudonym, of course"); + a.ShowInHelpText = false; + + var help = app.GetHelpText(); + + Assert.Contains("ninja-app", help); + Assert.DoesNotContain("--points", help); + Assert.DoesNotContain("--smile", help); + Assert.DoesNotContain("name", help); + } + + [Fact] + public void HelpTextUsesHelpOptionName() + { + var app = new CommandLineApplication + { + Name = "superhombre" + }; + + app.HelpOption("--ayuda-me"); + var help = app.GetHelpText(); + Assert.Contains("--ayuda-me", help); + } + + [Fact] + public void HelpTextShowsArgSeparator() + { + var app = new CommandLineApplication(throwOnUnexpectedArg: false) + { + Name = "proxy-command", + AllowArgumentSeparator = true + }; + app.HelpOption("-h|--help"); + Assert.Contains("Usage: proxy-command [options] [[--] ...]", app.GetHelpText()); + } + + [Fact] + public void HelpTextShowsExtendedHelp() + { + var app = new CommandLineApplication() + { + Name = "befuddle", + ExtendedHelpText = @" +Remarks: + This command is so confusing that I want to include examples in the help text. + +Examples: + dotnet befuddle -- I Can Haz Confusion Arguments +" + }; + + Assert.Contains(app.ExtendedHelpText, app.GetHelpText()); + } + + [Theory] + [InlineData(new[] { "--version", "--flag" }, "1.0")] + [InlineData(new[] { "-V", "-f" }, "1.0")] + [InlineData(new[] { "--help", "--flag" }, "some flag")] + [InlineData(new[] { "-h", "-f" }, "some flag")] + public void HelpAndVersionOptionStopProcessing(string[] input, string expectedOutData) + { + using (var outWriter = new StringWriter()) + { + var app = new CommandLineApplication { Out = outWriter }; + app.HelpOption("-h --help"); + app.VersionOption("-V --version", "1", "1.0"); + var optFlag = app.Option("-f |--flag", "some flag", CommandOptionType.NoValue); + + app.Execute(input); + + outWriter.Flush(); + var outData = outWriter.ToString(); + Assert.Contains(expectedOutData, outData); + Assert.False(optFlag.HasValue()); + } + } + + // disable inaccurate analyzer error https://github.com/xunit/xunit/issues/1274 +#pragma warning disable xUnit1010 +#pragma warning disable xUnit1011 + [Theory] + [InlineData("-f:File1", "-f:File2")] + [InlineData("--file:File1", "--file:File2")] + [InlineData("--file", "File1", "--file", "File2")] +#pragma warning restore xUnit1010 +#pragma warning restore xUnit1011 + public void ThrowsExceptionOnSingleValueOptionHavingTwoValues(params string[] inputOptions) + { + var app = new CommandLineApplication(); + app.Option("-f |--file", "some file", CommandOptionType.SingleValue); + + var exception = Assert.Throws(() => app.Execute(inputOptions)); + + Assert.Equal("Unexpected value 'File2' for option 'file'", exception.Message); + } + + [Theory] + [InlineData("-v")] + [InlineData("--verbose")] + public void NoValueOptionCanBeSet(string input) + { + var app = new CommandLineApplication(); + var optVerbose = app.Option("-v |--verbose", "be verbose", CommandOptionType.NoValue); + + app.Execute(input); + + Assert.True(optVerbose.HasValue()); + } + + [Theory] + [InlineData("-v:true")] + [InlineData("--verbose:true")] + public void ThrowsExceptionOnNoValueOptionHavingValue(string inputOption) + { + var app = new CommandLineApplication(); + app.Option("-v |--verbose", "be verbose", CommandOptionType.NoValue); + + var exception = Assert.Throws(() => app.Execute(inputOption)); + + Assert.Equal("Unexpected value 'true' for option 'verbose'", exception.Message); + } + + [Fact] + public void ThrowsExceptionOnEmptyCommandOrArgument() + { + var inputOption = String.Empty; + var app = new CommandLineApplication(); + + var exception = Assert.Throws(() => app.Execute(inputOption)); + + Assert.Equal($"Unrecognized command or argument '{inputOption}'", exception.Message); + } + + [Fact] + public void ThrowsExceptionOnInvalidOption() + { + var inputOption = "-"; + var app = new CommandLineApplication(); + + var exception = Assert.Throws(() => app.Execute(inputOption)); + + Assert.Equal($"Unrecognized option '{inputOption}'", exception.Message); + } + } +} diff --git a/src/Shared/test/Shared.Tests/CopyOnWriteDictionaryHolderTest.cs b/src/Shared/test/Shared.Tests/CopyOnWriteDictionaryHolderTest.cs new file mode 100644 index 000000000000..9a0951eb271a --- /dev/null +++ b/src/Shared/test/Shared.Tests/CopyOnWriteDictionaryHolderTest.cs @@ -0,0 +1,91 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace Microsoft.Extensions.Internal +{ + public class CopyOnWriteDictionaryHolderTest + { + [Fact] + public void ReadOperation_DelegatesToSourceDictionary_IfNoMutationsArePerformed() + { + // Arrange + var source = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "test-key", "test-value" }, + { "key2", "key2-value" } + }; + + var holder = new CopyOnWriteDictionaryHolder(source); + + // Act and Assert + Assert.Equal("key2-value", holder["key2"]); + Assert.Equal(2, holder.Count); + Assert.Equal(new string[] { "test-key", "key2" }, holder.Keys.ToArray()); + Assert.Equal(new object[] { "test-value", "key2-value" }, holder.Values.ToArray()); + Assert.True(holder.ContainsKey("test-key")); + + object value; + Assert.False(holder.TryGetValue("different-key", out value)); + + Assert.False(holder.HasBeenCopied); + Assert.Same(source, holder.ReadDictionary); + } + + [Fact] + public void ReadOperation_DoesNotDelegateToSourceDictionary_OnceAValueIsChanged() + { + // Arrange + var source = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "key1", "value1" }, + { "key2", "value2" } + }; + + var holder = new CopyOnWriteDictionaryHolder(source); + + // Act + holder["key2"] = "value3"; + + // Assert + Assert.Equal("value2", source["key2"]); + Assert.Equal(2, holder.Count); + Assert.Equal("value1", holder["key1"]); + Assert.Equal("value3", holder["key2"]); + + Assert.True(holder.HasBeenCopied); + Assert.NotSame(source, holder.ReadDictionary); + } + + [Fact] + public void ReadOperation_DoesNotDelegateToSourceDictionary_OnceValueIsAdded() + { + // Arrange + var source = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "key1", "value1" }, + { "key2", "value2" } + }; + + var holder = new CopyOnWriteDictionaryHolder(source); + + // Act + holder.Add("key3", "value3"); + holder.Remove("key1"); + + // Assert + Assert.Equal(2, source.Count); + Assert.Equal("value1", source["key1"]); + Assert.Equal(2, holder.Count); + Assert.Equal("value2", holder["KeY2"]); + Assert.Equal("value3", holder["key3"]); + + Assert.True(holder.HasBeenCopied); + Assert.NotSame(source, holder.ReadDictionary); + } + } +} diff --git a/src/Shared/test/Shared.Tests/CopyOnWriteDictionaryTest.cs b/src/Shared/test/Shared.Tests/CopyOnWriteDictionaryTest.cs new file mode 100644 index 000000000000..c1b54036d4f9 --- /dev/null +++ b/src/Shared/test/Shared.Tests/CopyOnWriteDictionaryTest.cs @@ -0,0 +1,109 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using Moq; +using Xunit; + +namespace Microsoft.Extensions.Internal +{ + public class CopyOnWriteDictionaryTest + { + [Fact] + public void ReadOperation_DelegatesToSourceDictionary_IfNoMutationsArePerformed() + { + // Arrange + var values = new List(); + var enumerator = Mock.Of>>(); + var sourceDictionary = new Mock>(MockBehavior.Strict); + sourceDictionary + .SetupGet(d => d.Count) + .Returns(100) + .Verifiable(); + sourceDictionary + .SetupGet(d => d.Values) + .Returns(values) + .Verifiable(); + sourceDictionary + .Setup(d => d.ContainsKey("test-key")) + .Returns(value: true) + .Verifiable(); + sourceDictionary + .Setup(d => d.GetEnumerator()) + .Returns(enumerator) + .Verifiable(); + sourceDictionary + .Setup(d => d["key2"]) + .Returns("key2-value") + .Verifiable(); + object value; + sourceDictionary.Setup(d => d.TryGetValue("different-key", out value)) + .Returns(false) + .Verifiable(); + + var copyOnWriteDictionary = new CopyOnWriteDictionary(sourceDictionary.Object, + StringComparer.OrdinalIgnoreCase); + + // Act and Assert + Assert.Equal("key2-value", copyOnWriteDictionary["key2"]); + Assert.Equal(100, copyOnWriteDictionary.Count); + Assert.Same(values, copyOnWriteDictionary.Values); + Assert.True(copyOnWriteDictionary.ContainsKey("test-key")); + Assert.Same(enumerator, copyOnWriteDictionary.GetEnumerator()); + Assert.False(copyOnWriteDictionary.TryGetValue("different-key", out value)); + sourceDictionary.Verify(); + } + + [Fact] + public void ReadOperation_DoesNotDelegateToSourceDictionary_OnceAValueIsChanged() + { + // Arrange + var values = new List(); + var sourceDictionary = new Dictionary + { + { "key1", "value1" }, + { "key2", "value2" } + }; + var copyOnWriteDictionary = new CopyOnWriteDictionary( + sourceDictionary, + StringComparer.OrdinalIgnoreCase); + + // Act + copyOnWriteDictionary["key2"] = "value3"; + + // Assert + Assert.Equal("value2", sourceDictionary["key2"]); + Assert.Equal(2, copyOnWriteDictionary.Count); + Assert.Equal("value1", copyOnWriteDictionary["key1"]); + Assert.Equal("value3", copyOnWriteDictionary["key2"]); + } + + [Fact] + public void ReadOperation_DoesNotDelegateToSourceDictionary_OnceDictionaryIsModified() + { + // Arrange + var values = new List(); + var sourceDictionary = new Dictionary + { + { "key1", "value1" }, + { "key2", "value2" } + }; + var copyOnWriteDictionary = new CopyOnWriteDictionary( + sourceDictionary, + StringComparer.OrdinalIgnoreCase); + + // Act + copyOnWriteDictionary.Add("key3", "value3"); + copyOnWriteDictionary.Remove("key1"); + + + // Assert + Assert.Equal(2, sourceDictionary.Count); + Assert.Equal("value1", sourceDictionary["key1"]); + Assert.Equal(2, copyOnWriteDictionary.Count); + Assert.Equal("value2", copyOnWriteDictionary["KeY2"]); + Assert.Equal("value3", copyOnWriteDictionary["key3"]); + } + } +} diff --git a/src/Shared/test/Shared.Tests/DotNetMuxerTests.cs b/src/Shared/test/Shared.Tests/DotNetMuxerTests.cs new file mode 100644 index 000000000000..ba1dd0651104 --- /dev/null +++ b/src/Shared/test/Shared.Tests/DotNetMuxerTests.cs @@ -0,0 +1,24 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#if NETCOREAPP2_0 || NETCOREAPP2_1 +using System.IO; +using System.Runtime.InteropServices; +using Xunit; + +namespace Microsoft.Extensions.CommandLineUtils +{ + public class DotNetMuxerTests + { + [Fact] + public void FindsTheMuxer() + { + var muxerPath = DotNetMuxer.MuxerPath; + Assert.NotNull(muxerPath); + Assert.True(File.Exists(muxerPath), "The file did not exist"); + Assert.True(Path.IsPathRooted(muxerPath), "The path should be rooted"); + Assert.Equal("dotnet", Path.GetFileNameWithoutExtension(muxerPath), ignoreCase: true); + } + } +} +#endif diff --git a/src/Shared/test/Shared.Tests/HashCodeCombinerTest.cs b/src/Shared/test/Shared.Tests/HashCodeCombinerTest.cs new file mode 100644 index 000000000000..fab3e309791e --- /dev/null +++ b/src/Shared/test/Shared.Tests/HashCodeCombinerTest.cs @@ -0,0 +1,39 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Xunit; + +namespace Microsoft.Extensions.Internal +{ + public class HashCodeCombinerTest + { + [Fact] + public void GivenTheSameInputs_ItProducesTheSameOutput() + { + var hashCode1 = new HashCodeCombiner(); + var hashCode2 = new HashCodeCombiner(); + + hashCode1.Add(42); + hashCode1.Add("foo"); + hashCode2.Add(42); + hashCode2.Add("foo"); + + Assert.Equal(hashCode1.CombinedHash, hashCode2.CombinedHash); + } + + [Fact] + public void HashCode_Is_OrderSensitive() + { + var hashCode1 = HashCodeCombiner.Start(); + var hashCode2 = HashCodeCombiner.Start(); + + hashCode1.Add(42); + hashCode1.Add("foo"); + + hashCode2.Add("foo"); + hashCode2.Add(42); + + Assert.NotEqual(hashCode1.CombinedHash, hashCode2.CombinedHash); + } + } +} diff --git a/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj b/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj new file mode 100644 index 000000000000..05b321a6d60d --- /dev/null +++ b/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj @@ -0,0 +1,32 @@ + + + + $(StandardTestTfms) + portable + true + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Shared/test/Shared.Tests/ObjectMethodExecutorTest.cs b/src/Shared/test/Shared.Tests/ObjectMethodExecutorTest.cs new file mode 100644 index 000000000000..1c26ef1de157 --- /dev/null +++ b/src/Shared/test/Shared.Tests/ObjectMethodExecutorTest.cs @@ -0,0 +1,634 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.FSharp.Control; +using Microsoft.FSharp.Core; +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace Microsoft.Extensions.Internal +{ + public class ObjectMethodExecutorTest + { + private TestObject _targetObject = new TestObject(); + private TypeInfo targetTypeInfo = typeof(TestObject).GetTypeInfo(); + + [Fact] + public void ExecuteValueMethod() + { + var executor = GetExecutorForMethod("ValueMethod"); + var result = executor.Execute( + _targetObject, + new object[] { 10, 20 }); + Assert.False(executor.IsMethodAsync); + Assert.Equal(30, (int)result); + } + + [Fact] + public void ExecuteVoidValueMethod() + { + var executor = GetExecutorForMethod("VoidValueMethod"); + var result = executor.Execute( + _targetObject, + new object[] { 10 }); + Assert.False(executor.IsMethodAsync); + Assert.Null(result); + } + + [Fact] + public void ExecuteValueMethodWithReturnType() + { + var executor = GetExecutorForMethod("ValueMethodWithReturnType"); + var result = executor.Execute( + _targetObject, + new object[] { 10 }); + var resultObject = Assert.IsType(result); + Assert.False(executor.IsMethodAsync); + Assert.Equal("Hello", resultObject.value); + } + + [Fact] + public void ExecuteValueMethodUpdateValue() + { + var executor = GetExecutorForMethod("ValueMethodUpdateValue"); + var parameter = new TestObject(); + var result = executor.Execute( + _targetObject, + new object[] { parameter }); + var resultObject = Assert.IsType(result); + Assert.False(executor.IsMethodAsync); + Assert.Equal("HelloWorld", resultObject.value); + } + + [Fact] + public void ExecuteValueMethodWithReturnTypeThrowsException() + { + var executor = GetExecutorForMethod("ValueMethodWithReturnTypeThrowsException"); + var parameter = new TestObject(); + Assert.False(executor.IsMethodAsync); + Assert.Throws( + () => executor.Execute( + _targetObject, + new object[] { parameter })); + } + + [Fact] + public async Task ExecuteValueMethodAsync() + { + var executor = GetExecutorForMethod("ValueMethodAsync"); + var result = await executor.ExecuteAsync( + _targetObject, + new object[] { 10, 20 }); + Assert.True(executor.IsMethodAsync); + Assert.Equal(30, (int)result); + } + + [Fact] + public async Task ExecuteValueMethodWithReturnTypeAsync() + { + var executor = GetExecutorForMethod("ValueMethodWithReturnTypeAsync"); + var result = await executor.ExecuteAsync( + _targetObject, + new object[] { 10 }); + var resultObject = Assert.IsType(result); + Assert.True(executor.IsMethodAsync); + Assert.Equal("Hello", resultObject.value); + } + + [Fact] + public async Task ExecuteValueMethodUpdateValueAsync() + { + var executor = GetExecutorForMethod("ValueMethodUpdateValueAsync"); + var parameter = new TestObject(); + var result = await executor.ExecuteAsync( + _targetObject, + new object[] { parameter }); + var resultObject = Assert.IsType(result); + Assert.True(executor.IsMethodAsync); + Assert.Equal("HelloWorld", resultObject.value); + } + + [Fact] + public async Task ExecuteValueMethodWithReturnTypeThrowsExceptionAsync() + { + var executor = GetExecutorForMethod("ValueMethodWithReturnTypeThrowsExceptionAsync"); + var parameter = new TestObject(); + Assert.True(executor.IsMethodAsync); + await Assert.ThrowsAsync( + async () => await executor.ExecuteAsync( + _targetObject, + new object[] { parameter })); + } + + [Fact] + public async Task ExecuteValueMethodWithReturnVoidThrowsExceptionAsync() + { + var executor = GetExecutorForMethod("ValueMethodWithReturnVoidThrowsExceptionAsync"); + var parameter = new TestObject(); + Assert.True(executor.IsMethodAsync); + await Assert.ThrowsAsync( + async () => await executor.ExecuteAsync( + _targetObject, + new object[] { parameter })); + } + + [Fact] + public void GetDefaultValueForParameters_ReturnsSuppliedValues() + { + var suppliedDefaultValues = new object[] { 123, "test value" }; + var executor = GetExecutorForMethod("MethodWithMultipleParameters", suppliedDefaultValues); + Assert.Equal(suppliedDefaultValues[0], executor.GetDefaultValueForParameter(0)); + Assert.Equal(suppliedDefaultValues[1], executor.GetDefaultValueForParameter(1)); + Assert.Throws(() => executor.GetDefaultValueForParameter(2)); + } + + [Fact] + public void GetDefaultValueForParameters_ThrowsIfNoneWereSupplied() + { + var executor = GetExecutorForMethod("MethodWithMultipleParameters"); + Assert.Throws(() => executor.GetDefaultValueForParameter(0)); + } + + [Fact] + public async void TargetMethodReturningCustomAwaitableOfReferenceType_CanInvokeViaExecute() + { + // Arrange + var executor = GetExecutorForMethod("CustomAwaitableOfReferenceTypeAsync"); + + // Act + var result = await (TestAwaitable)executor.Execute(_targetObject, new object[] { "Hello", 123 }); + + // Assert + Assert.True(executor.IsMethodAsync); + Assert.Same(typeof(TestObject), executor.AsyncResultType); + Assert.NotNull(result); + Assert.Equal("Hello 123", result.value); + } + + [Fact] + public async void TargetMethodReturningCustomAwaitableOfValueType_CanInvokeViaExecute() + { + // Arrange + var executor = GetExecutorForMethod("CustomAwaitableOfValueTypeAsync"); + + // Act + var result = await (TestAwaitable)executor.Execute(_targetObject, new object[] { 123, 456 }); + + // Assert + Assert.True(executor.IsMethodAsync); + Assert.Same(typeof(int), executor.AsyncResultType); + Assert.Equal(579, result); + } + + [Fact] + public async void TargetMethodReturningCustomAwaitableOfReferenceType_CanInvokeViaExecuteAsync() + { + // Arrange + var executor = GetExecutorForMethod("CustomAwaitableOfReferenceTypeAsync"); + + // Act + var result = await executor.ExecuteAsync(_targetObject, new object[] { "Hello", 123 }); + + // Assert + Assert.True(executor.IsMethodAsync); + Assert.Same(typeof(TestObject), executor.AsyncResultType); + Assert.NotNull(result); + Assert.IsType(result); + Assert.Equal("Hello 123", ((TestObject)result).value); + } + + [Fact] + public async void TargetMethodReturningCustomAwaitableOfValueType_CanInvokeViaExecuteAsync() + { + // Arrange + var executor = GetExecutorForMethod("CustomAwaitableOfValueTypeAsync"); + + // Act + var result = await executor.ExecuteAsync(_targetObject, new object[] { 123, 456 }); + + // Assert + Assert.True(executor.IsMethodAsync); + Assert.Same(typeof(int), executor.AsyncResultType); + Assert.NotNull(result); + Assert.IsType(result); + Assert.Equal(579, (int)result); + } + + [Fact] + public async void TargetMethodReturningAwaitableOfVoidType_CanInvokeViaExecuteAsync() + { + // Arrange + var executor = GetExecutorForMethod("VoidValueMethodAsync"); + + // Act + var result = await executor.ExecuteAsync(_targetObject, new object[] { 123 }); + + // Assert + Assert.True(executor.IsMethodAsync); + Assert.Same(typeof(void), executor.AsyncResultType); + Assert.Null(result); + } + + [Fact] + public async void TargetMethodReturningAwaitableWithICriticalNotifyCompletion_UsesUnsafeOnCompleted() + { + // Arrange + var executor = GetExecutorForMethod("CustomAwaitableWithICriticalNotifyCompletion"); + + // Act + var result = await executor.ExecuteAsync(_targetObject, new object[0]); + + // Assert + Assert.True(executor.IsMethodAsync); + Assert.Same(typeof(string), executor.AsyncResultType); + Assert.Equal("Used UnsafeOnCompleted", (string)result); + } + + [Fact] + public async void TargetMethodReturningAwaitableWithoutICriticalNotifyCompletion_UsesOnCompleted() + { + // Arrange + var executor = GetExecutorForMethod("CustomAwaitableWithoutICriticalNotifyCompletion"); + + // Act + var result = await executor.ExecuteAsync(_targetObject, new object[0]); + + // Assert + Assert.True(executor.IsMethodAsync); + Assert.Same(typeof(string), executor.AsyncResultType); + Assert.Equal("Used OnCompleted", (string)result); + } + + [Fact] + public async void TargetMethodReturningValueTaskOfValueType_CanBeInvokedViaExecute() + { + // Arrange + var executor = GetExecutorForMethod("ValueTaskOfValueType"); + + // Act + var result = await (ValueTask)executor.Execute(_targetObject, new object[] { 123 }); + + // Assert + Assert.True(executor.IsMethodAsync); + Assert.Same(typeof(int), executor.AsyncResultType); + Assert.Equal(123, result); + } + + [Fact] + public async void TargetMethodReturningValueTaskOfReferenceType_CanBeInvokedViaExecute() + { + // Arrange + var executor = GetExecutorForMethod("ValueTaskOfReferenceType"); + + // Act + var result = await (ValueTask)executor.Execute(_targetObject, new object[] { "test result" }); + + // Assert + Assert.True(executor.IsMethodAsync); + Assert.Same(typeof(string), executor.AsyncResultType); + Assert.Equal("test result", result); + } + + [Fact] + public async void TargetMethodReturningValueTaskOfValueType_CanBeInvokedViaExecuteAsync() + { + // Arrange + var executor = GetExecutorForMethod("ValueTaskOfValueType"); + + // Act + var result = await executor.ExecuteAsync(_targetObject, new object[] { 123 }); + + // Assert + Assert.True(executor.IsMethodAsync); + Assert.Same(typeof(int), executor.AsyncResultType); + Assert.NotNull(result); + Assert.Equal(123, (int)result); + } + + [Fact] + public async void TargetMethodReturningValueTaskOfReferenceType_CanBeInvokedViaExecuteAsync() + { + // Arrange + var executor = GetExecutorForMethod("ValueTaskOfReferenceType"); + + // Act + var result = await executor.ExecuteAsync(_targetObject, new object[] { "test result" }); + + // Assert + Assert.True(executor.IsMethodAsync); + Assert.Same(typeof(string), executor.AsyncResultType); + Assert.Equal("test result", result); + } + + [Fact] + public async void TargetMethodReturningFSharpAsync_CanBeInvokedViaExecute() + { + // Arrange + var executor = GetExecutorForMethod("FSharpAsyncMethod"); + + // Act + var fsharpAsync = (FSharpAsync)executor.Execute(_targetObject, new object[] { "test result" }); + var result = await FSharpAsync.StartAsTask(fsharpAsync, + FSharpOption.None, + FSharpOption.None); + + // Assert + Assert.True(executor.IsMethodAsync); + Assert.Same(typeof(string), executor.AsyncResultType); + Assert.Equal("test result", result); + } + + [Fact] + public async void TargetMethodReturningFailingFSharpAsync_CanBeInvokedViaExecute() + { + // Arrange + var executor = GetExecutorForMethod("FSharpAsyncFailureMethod"); + + // Act + var fsharpAsync = (FSharpAsync)executor.Execute(_targetObject, new object[] { "test result" }); + var resultTask = FSharpAsync.StartAsTask(fsharpAsync, + FSharpOption.None, + FSharpOption.None); + + // Assert + Assert.True(executor.IsMethodAsync); + Assert.Same(typeof(string), executor.AsyncResultType); + + var exception = await Assert.ThrowsAsync(async () => await resultTask); + Assert.IsType(exception.InnerException); + Assert.Equal("Test exception", exception.InnerException.Message); + } + + [Fact] + public async void TargetMethodReturningFSharpAsync_CanBeInvokedViaExecuteAsync() + { + // Arrange + var executor = GetExecutorForMethod("FSharpAsyncMethod"); + + // Act + var result = await executor.ExecuteAsync(_targetObject, new object[] { "test result" }); + + // Assert + Assert.True(executor.IsMethodAsync); + Assert.Same(typeof(string), executor.AsyncResultType); + Assert.Equal("test result", result); + } + + [Fact] + public async void TargetMethodReturningFailingFSharpAsync_CanBeInvokedViaExecuteAsync() + { + // Arrange + var executor = GetExecutorForMethod("FSharpAsyncFailureMethod"); + + // Act + var resultTask = executor.ExecuteAsync(_targetObject, new object[] { "test result" }); + + // Assert + Assert.True(executor.IsMethodAsync); + Assert.Same(typeof(string), executor.AsyncResultType); + + var exception = await Assert.ThrowsAsync(async () => await resultTask); + Assert.IsType(exception.InnerException); + Assert.Equal("Test exception", exception.InnerException.Message); + } + + private ObjectMethodExecutor GetExecutorForMethod(string methodName) + { + var method = typeof(TestObject).GetMethod(methodName); + return ObjectMethodExecutor.Create(method, targetTypeInfo); + } + + private ObjectMethodExecutor GetExecutorForMethod(string methodName, object[] parameterDefaultValues) + { + var method = typeof(TestObject).GetMethod(methodName); + return ObjectMethodExecutor.Create(method, targetTypeInfo, parameterDefaultValues); + } + + public class TestObject + { + public string value; + public int ValueMethod(int i, int j) + { + return i + j; + } + + public void VoidValueMethod(int i) + { + + } + + public TestObject ValueMethodWithReturnType(int i) + { + return new TestObject() { value = "Hello" }; ; + } + + public TestObject ValueMethodWithReturnTypeThrowsException(TestObject i) + { + throw new NotImplementedException("Not Implemented Exception"); + } + + public TestObject ValueMethodUpdateValue(TestObject parameter) + { + parameter.value = "HelloWorld"; + return parameter; + } + + public Task ValueMethodAsync(int i, int j) + { + return Task.FromResult(i + j); + } + + public async Task VoidValueMethodAsync(int i) + { + await ValueMethodAsync(3, 4); + } + public Task ValueMethodWithReturnTypeAsync(int i) + { + return Task.FromResult(new TestObject() { value = "Hello" }); + } + + public async Task ValueMethodWithReturnVoidThrowsExceptionAsync(TestObject i) + { + await Task.CompletedTask; + throw new NotImplementedException("Not Implemented Exception"); + } + + public async Task ValueMethodWithReturnTypeThrowsExceptionAsync(TestObject i) + { + await Task.CompletedTask; + throw new NotImplementedException("Not Implemented Exception"); + } + + public Task ValueMethodUpdateValueAsync(TestObject parameter) + { + parameter.value = "HelloWorld"; + return Task.FromResult(parameter); + } + + public TestAwaitable CustomAwaitableOfReferenceTypeAsync( + string input1, + int input2) + { + return new TestAwaitable(new TestObject + { + value = $"{input1} {input2}" + }); + } + + public TestAwaitable CustomAwaitableOfValueTypeAsync( + int input1, + int input2) + { + return new TestAwaitable(input1 + input2); + } + + public TestAwaitableWithICriticalNotifyCompletion CustomAwaitableWithICriticalNotifyCompletion() + { + return new TestAwaitableWithICriticalNotifyCompletion(); + } + + public TestAwaitableWithoutICriticalNotifyCompletion CustomAwaitableWithoutICriticalNotifyCompletion() + { + return new TestAwaitableWithoutICriticalNotifyCompletion(); + } + + public ValueTask ValueTaskOfValueType(int result) + { + return new ValueTask(result); + } + + public ValueTask ValueTaskOfReferenceType(string result) + { + return new ValueTask(result); + } + + public void MethodWithMultipleParameters(int valueTypeParam, string referenceTypeParam) + { + } + + public FSharpAsync FSharpAsyncMethod(string parameter) + { + return FSharpAsync.AwaitTask(Task.FromResult(parameter)); + } + + public FSharpAsync FSharpAsyncFailureMethod(string parameter) + { + return FSharpAsync.AwaitTask( + Task.FromException(new InvalidOperationException("Test exception"))); + } + } + + public class TestAwaitable + { + private T _result; + private bool _isCompleted; + private List _onCompletedCallbacks = new List(); + + public TestAwaitable(T result) + { + _result = result; + + // Simulate a brief delay before completion + ThreadPool.QueueUserWorkItem(_ => + { + Thread.Sleep(100); + SetCompleted(); + }); + } + + private void SetCompleted() + { + _isCompleted = true; + + foreach (var callback in _onCompletedCallbacks) + { + callback(); + } + } + + public TestAwaiter GetAwaiter() + { + return new TestAwaiter(this); + } + + public struct TestAwaiter : INotifyCompletion + { + private TestAwaitable _owner; + + public TestAwaiter(TestAwaitable owner) : this() + { + _owner = owner; + } + + public bool IsCompleted => _owner._isCompleted; + + public void OnCompleted(Action continuation) + { + if (_owner._isCompleted) + { + continuation(); + } + else + { + _owner._onCompletedCallbacks.Add(continuation); + } + } + + public T GetResult() + { + return _owner._result; + } + } + } + + public class TestAwaitableWithICriticalNotifyCompletion + { + public TestAwaiterWithICriticalNotifyCompletion GetAwaiter() + => new TestAwaiterWithICriticalNotifyCompletion(); + } + + public class TestAwaitableWithoutICriticalNotifyCompletion + { + public TestAwaiterWithoutICriticalNotifyCompletion GetAwaiter() + => new TestAwaiterWithoutICriticalNotifyCompletion(); + } + + public class TestAwaiterWithICriticalNotifyCompletion + : CompletionTrackingAwaiterBase, ICriticalNotifyCompletion + { + } + + public class TestAwaiterWithoutICriticalNotifyCompletion + : CompletionTrackingAwaiterBase, INotifyCompletion + { + } + + public class CompletionTrackingAwaiterBase + { + private string _result; + + public bool IsCompleted { get; private set; } + + public string GetResult() => _result; + + public void OnCompleted(Action continuation) + { + _result = "Used OnCompleted"; + IsCompleted = true; + continuation(); + } + + public void UnsafeOnCompleted(Action continuation) + { + _result = "Used UnsafeOnCompleted"; + IsCompleted = true; + continuation(); + } + } + } +} diff --git a/src/Shared/test/Shared.Tests/PropertyActivatorTest.cs b/src/Shared/test/Shared.Tests/PropertyActivatorTest.cs new file mode 100644 index 000000000000..a5cb1605b38c --- /dev/null +++ b/src/Shared/test/Shared.Tests/PropertyActivatorTest.cs @@ -0,0 +1,187 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Reflection; +using Xunit; + +namespace Microsoft.Extensions.Internal +{ + public class PropertyActivatorTest + { + [Fact] + public void Activate_InvokesValueAccessorWithExpectedValue() + { + // Arrange + var instance = new TestClass(); + var typeInfo = instance.GetType().GetTypeInfo(); + var property = typeInfo.GetDeclaredProperty("IntProperty"); + var invokedWith = -1; + var activator = new PropertyActivator( + property, + valueAccessor: (val) => + { + invokedWith = val; + return val; + }); + + // Act + activator.Activate(instance, 123); + + // Assert + Assert.Equal(123, invokedWith); + } + + [Fact] + public void Activate_SetsPropertyValue() + { + // Arrange + var instance = new TestClass(); + var typeInfo = instance.GetType().GetTypeInfo(); + var property = typeInfo.GetDeclaredProperty("IntProperty"); + var activator = new PropertyActivator(property, valueAccessor: (val) => val + 1); + + // Act + activator.Activate(instance, 123); + + // Assert + Assert.Equal(124, instance.IntProperty); + } + + [Fact] + public void GetPropertiesToActivate_RestrictsActivatableProperties() + { + // Arrange + var instance = new TestClass(); + var typeInfo = instance.GetType().GetTypeInfo(); + var expectedPropertyInfo = typeInfo.GetDeclaredProperty("ActivatableProperty"); + + // Act + var propertiesToActivate = PropertyActivator.GetPropertiesToActivate( + type: typeof(TestClass), + activateAttributeType: typeof(TestActivateAttribute), + createActivateInfo: + (propertyInfo) => new PropertyActivator(propertyInfo, valueAccessor: (val) => val + 1)); + + // Assert + Assert.Collection( + propertiesToActivate, + (activator) => + { + Assert.Equal(expectedPropertyInfo, activator.PropertyInfo); + }); + } + + [Fact] + public void GetPropertiesToActivate_CanCreateCustomPropertyActivators() + { + // Arrange + var instance = new TestClass(); + var typeInfo = instance.GetType().GetTypeInfo(); + var expectedPropertyInfo = typeInfo.GetDeclaredProperty("IntProperty"); + + // Act + var propertiesToActivate = PropertyActivator.GetPropertiesToActivate( + type: typeof(TestClass), + activateAttributeType: typeof(TestActivateAttribute), + createActivateInfo: + (propertyInfo) => new PropertyActivator(expectedPropertyInfo, valueAccessor: (val) => val + 1)); + + // Assert + Assert.Collection( + propertiesToActivate, + (activator) => + { + Assert.Equal(expectedPropertyInfo, activator.PropertyInfo); + }); + } + + [Fact] + public void GetPropertiesToActivate_ExcludesNonPublic() + { + // Arrange + var instance = new TestClassWithPropertyVisiblity(); + var typeInfo = instance.GetType().GetTypeInfo(); + var expectedPropertyInfo = typeInfo.GetDeclaredProperty("Public"); + + // Act + var propertiesToActivate = PropertyActivator.GetPropertiesToActivate( + typeof(TestClassWithPropertyVisiblity), + typeof(TestActivateAttribute), + (propertyInfo) => new PropertyActivator(propertyInfo, valueAccessor: (val) => val)); + + // Assert + Assert.Single(propertiesToActivate); + Assert.Single(propertiesToActivate, p => p.PropertyInfo == expectedPropertyInfo); + } + + [Fact] + public void GetPropertiesToActivate_IncludesNonPublic() + { + // Arrange + var instance = new TestClassWithPropertyVisiblity(); + var typeInfo = instance.GetType().GetTypeInfo(); + + // Act + var propertiesToActivate = PropertyActivator.GetPropertiesToActivate( + typeof(TestClassWithPropertyVisiblity), + typeof(TestActivateAttribute), + (propertyInfo) => new PropertyActivator(propertyInfo, valueAccessor: (val) => val), + includeNonPublic: true); + + // Assert + Assert.Equal(5, propertiesToActivate.Length); + } + + private class TestClass + { + public int IntProperty { get; set; } + + [TestActivate] + public int ActivatableProperty { get; set; } + + [TestActivate] + public int NoSetterActivatableProperty { get; } + + [TestActivate] + public int this[int something] // Not activatable + { + get + { + return 0; + } + } + + [TestActivate] + public static int StaticActivatablProperty { get; set; } + } + + private class TestClassWithPropertyVisiblity + { + [TestActivate] + public int Public { get; set; } + + [TestActivate] + protected int Protected { get; set; } + + [TestActivate] + internal int Internal { get; set; } + + [TestActivate] + protected internal int ProtectedInternal {get; set; } + + [TestActivate] + private int Private { get; set; } + } + + [AttributeUsage(AttributeTargets.Property)] + private class TestActivateAttribute : Attribute + { + } + + private class ActivationInfo + { + public string Name { get; set; } + } + } +} diff --git a/src/Shared/test/Shared.Tests/PropertyHelperTest.cs b/src/Shared/test/Shared.Tests/PropertyHelperTest.cs new file mode 100644 index 000000000000..19cf08b3705a --- /dev/null +++ b/src/Shared/test/Shared.Tests/PropertyHelperTest.cs @@ -0,0 +1,831 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Xunit; + +namespace Microsoft.Extensions.Internal +{ + public class PropertyHelperTest + { + [Fact] + public void PropertyHelper_ReturnsNameCorrectly() + { + // Arrange + var anonymous = new { foo = "bar" }; + var property = PropertyHelper.GetProperties(anonymous.GetType()).First().Property; + + // Act + var helper = new PropertyHelper(property); + + // Assert + Assert.Equal("foo", property.Name); + Assert.Equal("foo", helper.Name); + } + + [Fact] + public void PropertyHelper_ReturnsValueCorrectly() + { + // Arrange + var anonymous = new { bar = "baz" }; + var property = PropertyHelper.GetProperties(anonymous.GetType()).First().Property; + + // Act + var helper = new PropertyHelper(property); + + // Assert + Assert.Equal("bar", helper.Name); + Assert.Equal("baz", helper.GetValue(anonymous)); + } + + [Fact] + public void PropertyHelper_ReturnsGetterDelegate() + { + // Arrange + var anonymous = new { bar = "baz" }; + var property = PropertyHelper.GetProperties(anonymous.GetType()).First().Property; + + // Act + var helper = new PropertyHelper(property); + + // Assert + Assert.NotNull(helper.ValueGetter); + Assert.Equal("baz", helper.ValueGetter(anonymous)); + } + + [Fact] + public void SetValue_SetsPropertyValue() + { + // Arrange + var expected = "new value"; + var instance = new BaseClass { PropA = "old value" }; + var helper = PropertyHelper.GetProperties( + instance.GetType()).First(prop => prop.Name == "PropA"); + + // Act + helper.SetValue(instance, expected); + + // Assert + Assert.Equal(expected, instance.PropA); + } + + [Fact] + public void PropertyHelper_ReturnsSetterDelegate() + { + // Arrange + var expected = "new value"; + var instance = new BaseClass { PropA = "old value" }; + var helper = PropertyHelper.GetProperties( + instance.GetType()).First(prop => prop.Name == "PropA"); + + // Act and Assert + Assert.NotNull(helper.ValueSetter); + helper.ValueSetter(instance, expected); + + // Assert + Assert.Equal(expected, instance.PropA); + } + + [Fact] + public void PropertyHelper_ReturnsValueCorrectly_ForValueTypes() + { + // Arrange + var anonymous = new { foo = 32 }; + var property = PropertyHelper.GetProperties(anonymous.GetType()).First().Property; + + // Act + var helper = new PropertyHelper(property); + + // Assert + Assert.Equal("foo", helper.Name); + Assert.Equal(32, helper.GetValue(anonymous)); + } + + [Fact] + public void PropertyHelper_ReturnsCachedPropertyHelper() + { + // Arrange + var anonymous = new { foo = "bar" }; + + // Act + var helpers1 = PropertyHelper.GetProperties(anonymous.GetType().GetTypeInfo()); + var helpers2 = PropertyHelper.GetProperties(anonymous.GetType().GetTypeInfo()); + + // Assert + Assert.Single(helpers1); + Assert.Same(helpers1, helpers2); + Assert.Same(helpers1[0], helpers2[0]); + } + + [Fact] + public void PropertyHelper_DoesNotChangeUnderscores() + { + // Arrange + var anonymous = new { bar_baz2 = "foo" }; + + // Act + Assert + var helper = Assert.Single(PropertyHelper.GetProperties(anonymous.GetType().GetTypeInfo())); + Assert.Equal("bar_baz2", helper.Name); + } + + [Fact] + public void PropertyHelper_DoesNotFindPrivateProperties() + { + // Arrange + var anonymous = new PrivateProperties(); + + // Act + Assert + var helper = Assert.Single(PropertyHelper.GetProperties(anonymous.GetType().GetTypeInfo())); + Assert.Equal("Prop1", helper.Name); + } + + [Fact] + public void PropertyHelper_DoesNotFindStaticProperties() + { + // Arrange + var anonymous = new Static(); + + // Act + Assert + var helper = Assert.Single(PropertyHelper.GetProperties(anonymous.GetType().GetTypeInfo())); + Assert.Equal("Prop5", helper.Name); + } + + [Fact] + public void PropertyHelper_DoesNotFindSetOnlyProperties() + { + // Arrange + var anonymous = new SetOnly(); + + // Act + Assert + var helper = Assert.Single(PropertyHelper.GetProperties(anonymous.GetType().GetTypeInfo())); + Assert.Equal("Prop6", helper.Name); + } + + [Theory] + [InlineData(typeof(int?))] + [InlineData(typeof(DayOfWeek?))] + public void PropertyHelper_WorksForNullablePrimitiveAndEnumTypes(Type nullableType) + { + // Act + var properties = PropertyHelper.GetProperties(nullableType); + + // Assert + Assert.Empty(properties); + } + + [Fact] + public void PropertyHelper_UnwrapsNullableTypes() + { + // Arrange + var myType = typeof(MyStruct?); + + // Act + var properties = PropertyHelper.GetProperties(myType); + + // Assert + var property = Assert.Single(properties); + Assert.Equal("Foo", property.Name); + } + + [Fact] + public void PropertyHelper_WorksForStruct() + { + // Arrange + var anonymous = new MyProperties(); + + anonymous.IntProp = 3; + anonymous.StringProp = "Five"; + + // Act + Assert + var helper1 = Assert.Single(PropertyHelper.GetProperties(anonymous.GetType().GetTypeInfo()).Where(prop => prop.Name == "IntProp")); + var helper2 = Assert.Single(PropertyHelper.GetProperties(anonymous.GetType().GetTypeInfo()).Where(prop => prop.Name == "StringProp")); + Assert.Equal(3, helper1.GetValue(anonymous)); + Assert.Equal("Five", helper2.GetValue(anonymous)); + } + + [Fact] + public void PropertyHelper_ForDerivedClass() + { + // Arrange + var derived = new DerivedClass { PropA = "propAValue", PropB = "propBValue" }; + + // Act + var helpers = PropertyHelper.GetProperties(derived.GetType().GetTypeInfo()).ToArray(); + + // Assert + Assert.NotNull(helpers); + Assert.Equal(2, helpers.Length); + + var propAHelper = Assert.Single(helpers.Where(h => h.Name == "PropA")); + var propBHelper = Assert.Single(helpers.Where(h => h.Name == "PropB")); + + Assert.Equal("propAValue", propAHelper.GetValue(derived)); + Assert.Equal("propBValue", propBHelper.GetValue(derived)); + } + + [Fact] + public void PropertyHelper_ForDerivedClass_WithNew() + { + // Arrange + var derived = new DerivedClassWithNew { PropA = "propAValue" }; + + // Act + var helpers = PropertyHelper.GetProperties(derived.GetType().GetTypeInfo()).ToArray(); + + // Assert + Assert.NotNull(helpers); + Assert.Equal(2, helpers.Length); + + var propAHelper = Assert.Single(helpers.Where(h => h.Name == "PropA")); + var propBHelper = Assert.Single(helpers.Where(h => h.Name == "PropB")); + + Assert.Equal("propAValue", propAHelper.GetValue(derived)); + Assert.Equal("Newed", propBHelper.GetValue(derived)); + } + + [Fact] + public void PropertyHelper_ForDerived_WithVirtual() + { + // Arrange + var derived = new DerivedClassWithOverride { PropA = "propAValue", PropB = "propBValue" }; + + // Act + var helpers = PropertyHelper.GetProperties(derived.GetType().GetTypeInfo()).ToArray(); + + // Assert + Assert.NotNull(helpers); + Assert.Equal(2, helpers.Length); + + var propAHelper = Assert.Single(helpers.Where(h => h.Name == "PropA")); + var propBHelper = Assert.Single(helpers.Where(h => h.Name == "PropB")); + + Assert.Equal("OverridenpropAValue", propAHelper.GetValue(derived)); + Assert.Equal("propBValue", propBHelper.GetValue(derived)); + } + + [Fact] + public void PropertyHelper_ForInterface_ReturnsExpectedProperties() + { + // Arrange + var expectedNames = new[] { "Count", "IsReadOnly" }; + + // Act + var helpers = PropertyHelper.GetProperties(typeof(ICollection)); + + // Assert + Assert.Collection( + helpers.OrderBy(helper => helper.Name, StringComparer.Ordinal), + helper => { Assert.Equal(expectedNames[0], helper.Name, StringComparer.Ordinal); }, + helper => { Assert.Equal(expectedNames[1], helper.Name, StringComparer.Ordinal); }); + } + + [Fact] + public void PropertyHelper_ForDerivedInterface_ReturnsAllProperties() + { + // Arrange + var expectedNames = new[] { "Count", "IsReadOnly", "Keys", "Values" }; + + // Act + var helpers = PropertyHelper.GetProperties(typeof(IDictionary)); + + // Assert + Assert.Collection( + helpers.OrderBy(helper => helper.Name, StringComparer.Ordinal), + helper => { Assert.Equal(expectedNames[0], helper.Name, StringComparer.Ordinal); }, + helper => { Assert.Equal(expectedNames[1], helper.Name, StringComparer.Ordinal); }, + helper => { Assert.Equal(expectedNames[2], helper.Name, StringComparer.Ordinal); }, + helper => { Assert.Equal(expectedNames[3], helper.Name, StringComparer.Ordinal); }); + } + + [Fact] + public void GetProperties_ExcludesIndexersAndPropertiesWithoutPublicGetters() + { + // Arrange + var type = typeof(DerivedClassWithNonReadableProperties); + + // Act + var result = PropertyHelper.GetProperties(type).ToArray(); + + // Assert + Assert.Equal(3, result.Length); + Assert.Equal("Visible", result[0].Name); + Assert.Equal("PropA", result[1].Name); + Assert.Equal("PropB", result[2].Name); + } + + [Fact] + public void GetVisibleProperties_NoHiddenProperty() + { + // Arrange + var type = typeof(string); + + // Act + var result = PropertyHelper.GetVisibleProperties(type).ToArray(); + + // Assert + var property = Assert.Single(result); + Assert.Equal("Length", property.Name); + Assert.Equal(typeof(int), property.Property.PropertyType); + } + + [Fact] + public void GetVisibleProperties_HiddenProperty() + { + // Arrange + var type = typeof(DerivedHiddenProperty); + + // Act + var result = PropertyHelper.GetVisibleProperties(type).ToArray(); + + // Assert + Assert.Equal(2, result.Length); + Assert.Equal("Id", result[0].Name); + Assert.Equal(typeof(string), result[0].Property.PropertyType); + Assert.Equal("Name", result[1].Name); + Assert.Equal(typeof(string), result[1].Property.PropertyType); + } + + [Fact] + public void GetVisibleProperties_HiddenProperty_TwoLevels() + { + // Arrange + var type = typeof(DerivedHiddenProperty2); + + // Act + var result = PropertyHelper.GetVisibleProperties(type).ToArray(); + + // Assert + Assert.Equal(2, result.Length); + Assert.Equal("Id", result[0].Name); + Assert.Equal(typeof(Guid), result[0].Property.PropertyType); + Assert.Equal("Name", result[1].Name); + Assert.Equal(typeof(string), result[1].Property.PropertyType); + } + + [Fact] + public void GetVisibleProperties_NoHiddenPropertyWithTypeInfoInput() + { + // Arrange + var type = typeof(string); + + // Act + var result = PropertyHelper.GetVisibleProperties(type.GetTypeInfo()).ToArray(); + + // Assert + var property = Assert.Single(result); + Assert.Equal("Length", property.Name); + Assert.Equal(typeof(int), property.Property.PropertyType); + } + + [Fact] + public void GetVisibleProperties_HiddenPropertyWithTypeInfoInput() + { + // Arrange + var type = typeof(DerivedHiddenProperty); + + // Act + var result = PropertyHelper.GetVisibleProperties(type.GetTypeInfo()).ToArray(); + + // Assert + Assert.Equal(2, result.Length); + Assert.Equal("Id", result[0].Name); + Assert.Equal(typeof(string), result[0].Property.PropertyType); + Assert.Equal("Name", result[1].Name); + Assert.Equal(typeof(string), result[1].Property.PropertyType); + } + + [Fact] + public void GetVisibleProperties_HiddenProperty_TwoLevelsWithTypeInfoInput() + { + // Arrange + var type = typeof(DerivedHiddenProperty2); + + // Act + var result = PropertyHelper.GetVisibleProperties(type.GetTypeInfo()).ToArray(); + + // Assert + Assert.Equal(2, result.Length); + Assert.Equal("Id", result[0].Name); + Assert.Equal(typeof(Guid), result[0].Property.PropertyType); + Assert.Equal("Name", result[1].Name); + Assert.Equal(typeof(string), result[1].Property.PropertyType); + } + + [Fact] + public void MakeFastPropertySetter_SetsPropertyValues_ForPublicAndNobPublicProperties() + { + // Arrange + var instance = new BaseClass(); + var typeInfo = instance.GetType().GetTypeInfo(); + var publicProperty = typeInfo.GetDeclaredProperty("PropA"); + var protectedProperty = typeInfo.GetDeclaredProperty("PropProtected"); + var publicPropertySetter = PropertyHelper.MakeFastPropertySetter(publicProperty); + var protectedPropertySetter = PropertyHelper.MakeFastPropertySetter(protectedProperty); + + // Act + publicPropertySetter(instance, "TestPublic"); + protectedPropertySetter(instance, "TestProtected"); + + // Assert + Assert.Equal("TestPublic", instance.PropA); + Assert.Equal("TestProtected", instance.GetPropProtected()); + } + + [Fact] + public void MakeFastPropertySetter_SetsPropertyValues_ForOverridenProperties() + { + // Arrange + var instance = new DerivedClassWithOverride(); + var typeInfo = instance.GetType().GetTypeInfo(); + var property = typeInfo.GetDeclaredProperty("PropA"); + var propertySetter = PropertyHelper.MakeFastPropertySetter(property); + + // Act + propertySetter(instance, "Test value"); + + // Assert + Assert.Equal("OverridenTest value", instance.PropA); + } + + [Fact] + public void MakeFastPropertySetter_SetsPropertyValues_ForNewedProperties() + { + // Arrange + var instance = new DerivedClassWithNew(); + var typeInfo = instance.GetType().GetTypeInfo(); + var property = typeInfo.GetDeclaredProperty("PropB"); + var propertySetter = PropertyHelper.MakeFastPropertySetter(property); + + // Act + propertySetter(instance, "Test value"); + + // Assert + Assert.Equal("NewedTest value", instance.PropB); + } + + [Fact] + public void MakeFastPropertyGetter_ReferenceType_ForNullObject_Throws() + { + // Arrange + var property = PropertyHelper + .GetProperties(typeof(BaseClass)) + .Single(p => p.Name == nameof(BaseClass.PropA)); + + var accessor = PropertyHelper.MakeFastPropertyGetter(property.Property); + + // Act & Assert + Assert.Throws(() => accessor(null)); + } + + [Fact] + public void MakeFastPropertyGetter_ValueType_ForNullObject_Throws() + { + // Arrange + var property = PropertyHelper + .GetProperties(typeof(MyProperties)) + .Single(p => p.Name == nameof(MyProperties.StringProp)); + + var accessor = PropertyHelper.MakeFastPropertyGetter(property.Property); + + // Act & Assert + Assert.Throws(() => accessor(null)); + } + + [Fact] + public void MakeNullSafeFastPropertyGetter_ReferenceType_Success() + { + // Arrange + var property = PropertyHelper + .GetProperties(typeof(BaseClass)) + .Single(p => p.Name == nameof(BaseClass.PropA)); + + var accessor = PropertyHelper.MakeNullSafeFastPropertyGetter(property.Property); + + // Act + var value = accessor(new BaseClass() { PropA = "Hi" }); + + // Assert + Assert.Equal("Hi", value); + } + + [Fact] + public void MakeNullSafeFastPropertyGetter_ValueType_Success() + { + // Arrange + var property = PropertyHelper + .GetProperties(typeof(MyProperties)) + .Single(p => p.Name == nameof(MyProperties.StringProp)); + + var accessor = PropertyHelper.MakeNullSafeFastPropertyGetter(property.Property); + + // Act + var value = accessor(new MyProperties() { StringProp = "Hi" }); + + // Assert + Assert.Equal("Hi", value); + } + + [Fact] + public void MakeNullSafeFastPropertyGetter_ReferenceType_ForNullObject_ReturnsNull() + { + // Arrange + var property = PropertyHelper + .GetProperties(typeof(BaseClass)) + .Single(p => p.Name == nameof(BaseClass.PropA)); + + var accessor = PropertyHelper.MakeNullSafeFastPropertyGetter(property.Property); + + // Act + var value = accessor(null); + + // Assert + Assert.Null(value); + } + + [Fact] + public void MakeNullSafeFastPropertyGetter_ValueType_ForNullObject_ReturnsNull() + { + // Arrange + var property = PropertyHelper + .GetProperties(typeof(MyProperties)) + .Single(p => p.Name == nameof(MyProperties.StringProp)); + + var accessor = PropertyHelper.MakeNullSafeFastPropertyGetter(property.Property); + + // Act + var value = accessor(null); + + // Assert + Assert.Null(value); + } + + public static TheoryData> IgnoreCaseTestData + { + get + { + return new TheoryData> + { + { + new + { + selected = true, + SeLeCtEd = false + }, + new KeyValuePair("selected", false) + }, + { + new + { + SeLeCtEd = false, + selected = true + }, + new KeyValuePair("SeLeCtEd", true) + }, + { + new + { + SelECTeD = false, + SeLECTED = true + }, + new KeyValuePair("SelECTeD", true) + } + }; + } + } + + [Theory] + [MemberData(nameof(IgnoreCaseTestData))] + public void ObjectToDictionary_IgnoresPropertyCase(object testObject, + KeyValuePair expectedEntry) + { + // Act + var result = PropertyHelper.ObjectToDictionary(testObject); + + // Assert + var entry = Assert.Single(result); + Assert.Equal(expectedEntry, entry); + } + + [Fact] + public void ObjectToDictionary_WithNullObject_ReturnsEmptyDictionary() + { + // Arrange + object value = null; + + // Act + var dictValues = PropertyHelper.ObjectToDictionary(value); + + // Assert + Assert.NotNull(dictValues); + Assert.Equal(0, dictValues.Count); + } + + [Fact] + public void ObjectToDictionary_WithPlainObjectType_ReturnsEmptyDictionary() + { + // Arrange + var value = new object(); + + // Act + var dictValues = PropertyHelper.ObjectToDictionary(value); + + // Assert + Assert.NotNull(dictValues); + Assert.Equal(0, dictValues.Count); + } + + [Fact] + public void ObjectToDictionary_WithPrimitiveType_LooksUpPublicProperties() + { + // Arrange + var value = "test"; + + // Act + var dictValues = PropertyHelper.ObjectToDictionary(value); + + // Assert + Assert.NotNull(dictValues); + Assert.Equal(1, dictValues.Count); + Assert.Equal(4, dictValues["Length"]); + } + + [Fact] + public void ObjectToDictionary_WithAnonymousType_LooksUpProperties() + { + // Arrange + var value = new { test = "value", other = 1 }; + + // Act + var dictValues = PropertyHelper.ObjectToDictionary(value); + + // Assert + Assert.NotNull(dictValues); + Assert.Equal(2, dictValues.Count); + Assert.Equal("value", dictValues["test"]); + Assert.Equal(1, dictValues["other"]); + } + + [Fact] + public void ObjectToDictionary_ReturnsCaseInsensitiveDictionary() + { + // Arrange + var value = new { TEST = "value", oThEr = 1 }; + + // Act + var dictValues = PropertyHelper.ObjectToDictionary(value); + + // Assert + Assert.NotNull(dictValues); + Assert.Equal(2, dictValues.Count); + Assert.Equal("value", dictValues["test"]); + Assert.Equal(1, dictValues["other"]); + } + + [Fact] + public void ObjectToDictionary_ReturnsInheritedProperties() + { + // Arrange + var value = new ThreeDPoint() { X = 5, Y = 10, Z = 17 }; + + // Act + var dictValues = PropertyHelper.ObjectToDictionary(value); + + // Assert + Assert.NotNull(dictValues); + Assert.Equal(3, dictValues.Count); + Assert.Equal(5, dictValues["X"]); + Assert.Equal(10, dictValues["Y"]); + Assert.Equal(17, dictValues["Z"]); + } + + private class Point + { + public int X { get; set; } + public int Y { get; set; } + } + + private class ThreeDPoint : Point + { + public int Z { get; set; } + } + + private class Static + { + public static int Prop2 { get; set; } + public int Prop5 { get; set; } + } + + private struct MyProperties + { + public int IntProp { get; set; } + public string StringProp { get; set; } + } + + private class SetOnly + { + public int Prop2 { set { } } + public int Prop6 { get; set; } + } + + private class PrivateProperties + { + public int Prop1 { get; set; } + protected int Prop2 { get; set; } + private int Prop3 { get; set; } + } + + public class BaseClass + { + public string PropA { get; set; } + + protected string PropProtected { get; set; } + + public string GetPropProtected() + { + return PropProtected; + } + } + + public class DerivedClass : BaseClass + { + public string PropB { get; set; } + } + + public class BaseClassWithVirtual + { + public virtual string PropA { get; set; } + public string PropB { get; set; } + } + + public class DerivedClassWithNew : BaseClassWithVirtual + { + private string _value = "Newed"; + + public new string PropB + { + get { return _value; } + set { _value = "Newed" + value; } + } + } + + public class DerivedClassWithOverride : BaseClassWithVirtual + { + private string _value = "Overriden"; + + public override string PropA + { + get { return _value; } + set { _value = "Overriden" + value; } + } + } + + private class DerivedClassWithNonReadableProperties : BaseClassWithVirtual + { + public string this[int index] + { + get { return string.Empty; } + set { } + } + + public int Visible { get; set; } + + private string NotVisible { get; set; } + + public string NotVisible2 { private get; set; } + + public string NotVisible3 + { + set { } + } + + public static string NotVisible4 { get; set; } + } + + private struct MyStruct + { + public string Foo { get; set; } + } + + private class BaseHiddenProperty + { + public int Id { get; set; } + } + + private class DerivedHiddenProperty : BaseHiddenProperty + { + public new string Id { get; set; } + + public string Name { get; set; } + } + + private class DerivedHiddenProperty2 : DerivedHiddenProperty + { + public new Guid Id { get; set; } + + public new string Name { get; private set; } + } + } +} diff --git a/src/Shared/test/Shared.Tests/Readme.txt b/src/Shared/test/Shared.Tests/Readme.txt new file mode 100644 index 000000000000..b818bd814800 --- /dev/null +++ b/src/Shared/test/Shared.Tests/Readme.txt @@ -0,0 +1,4 @@ +NOTE: +1. The tests for 'ExceptionDetailProvider' and 'StackTraceHelper' in project 'Microsoft.Extensions.StackTrace.Sources' are located in Diagnostics + repo. This is because they refer to some packages from FileSystem repo which causes a circular reference and breaks the + build. \ No newline at end of file diff --git a/src/Shared/test/Shared.Tests/SecurityHelperTests.cs b/src/Shared/test/Shared.Tests/SecurityHelperTests.cs new file mode 100644 index 000000000000..8e7515ad36e7 --- /dev/null +++ b/src/Shared/test/Shared.Tests/SecurityHelperTests.cs @@ -0,0 +1,93 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Linq; +using System.Security.Claims; +using System.Security.Principal; +using Xunit; + +namespace Microsoft.Extensions.Internal +{ + public class SecurityHelperTests + { + [Fact] + public void AddingToAnonymousIdentityDoesNotKeepAnonymousIdentity() + { + var user = SecurityHelper.MergeUserPrincipal(new ClaimsPrincipal(), new GenericPrincipal(new GenericIdentity("Test1", "Alpha"), new string[0])); + + Assert.NotNull(user); + Assert.Equal("Alpha", user.Identity.AuthenticationType); + Assert.Equal("Test1", user.Identity.Name); + Assert.IsAssignableFrom(user); + Assert.IsAssignableFrom(user.Identity); + Assert.Single(user.Identities); + } + + [Fact] + public void AddingExistingIdentityChangesDefaultButPreservesPrior() + { + ClaimsPrincipal user = new GenericPrincipal(new GenericIdentity("Test1", "Alpha"), null); + + Assert.Equal("Alpha", user.Identity.AuthenticationType); + Assert.Equal("Test1", user.Identity.Name); + + user = SecurityHelper.MergeUserPrincipal(user, new GenericPrincipal(new GenericIdentity("Test2", "Beta"), new string[0])); + + Assert.Equal("Beta", user.Identity.AuthenticationType); + Assert.Equal("Test2", user.Identity.Name); + + user = SecurityHelper.MergeUserPrincipal(user, new GenericPrincipal(new GenericIdentity("Test3", "Gamma"), new string[0])); + + Assert.Equal("Gamma", user.Identity.AuthenticationType); + Assert.Equal("Test3", user.Identity.Name); + + Assert.Equal(3, user.Identities.Count()); + Assert.Equal("Test3", user.Identities.Skip(0).First().Name); + Assert.Equal("Test2", user.Identities.Skip(1).First().Name); + Assert.Equal("Test1", user.Identities.Skip(2).First().Name); + } + + [Fact] + public void AddingPreservesNewIdentitiesAndDropsEmpty() + { + var existingPrincipal = new ClaimsPrincipal(new ClaimsIdentity()); + var identityNoAuthTypeWithClaim = new ClaimsIdentity(); + identityNoAuthTypeWithClaim.AddClaim(new Claim("identityNoAuthTypeWithClaim", "yes")); + existingPrincipal.AddIdentity(identityNoAuthTypeWithClaim); + var identityEmptyWithAuthType = new ClaimsIdentity("empty"); + existingPrincipal.AddIdentity(identityEmptyWithAuthType); + + Assert.False(existingPrincipal.Identity.IsAuthenticated); + + var newPrincipal = new ClaimsPrincipal(); + var newEmptyIdentity = new ClaimsIdentity(); + var identityTwo = new ClaimsIdentity("yep"); + newPrincipal.AddIdentity(newEmptyIdentity); + newPrincipal.AddIdentity(identityTwo); + + var user = SecurityHelper.MergeUserPrincipal(existingPrincipal, newPrincipal); + + // Preserve newPrincipal order + Assert.False(user.Identity.IsAuthenticated); + Assert.Null(user.Identity.Name); + + Assert.Equal(4, user.Identities.Count()); + Assert.Equal(newEmptyIdentity, user.Identities.Skip(0).First()); + Assert.Equal(identityTwo, user.Identities.Skip(1).First()); + Assert.Equal(identityNoAuthTypeWithClaim, user.Identities.Skip(2).First()); + Assert.Equal(identityEmptyWithAuthType, user.Identities.Skip(3).First()); + + // This merge should drop newEmptyIdentity since its empty + user = SecurityHelper.MergeUserPrincipal(user, new GenericPrincipal(new GenericIdentity("Test3", "Gamma"), new string[0])); + + Assert.Equal("Gamma", user.Identity.AuthenticationType); + Assert.Equal("Test3", user.Identity.Name); + + Assert.Equal(4, user.Identities.Count()); + Assert.Equal("Test3", user.Identities.Skip(0).First().Name); + Assert.Equal(identityTwo, user.Identities.Skip(1).First()); + Assert.Equal(identityNoAuthTypeWithClaim, user.Identities.Skip(2).First()); + Assert.Equal(identityEmptyWithAuthType, user.Identities.Skip(3).First()); + } + } +} diff --git a/src/Shared/test/Shared.Tests/StackTraceHelperTest.cs b/src/Shared/test/Shared.Tests/StackTraceHelperTest.cs new file mode 100644 index 000000000000..657a310b6e24 --- /dev/null +++ b/src/Shared/test/Shared.Tests/StackTraceHelperTest.cs @@ -0,0 +1,345 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using Microsoft.Extensions.StackTrace.Sources; +using ThrowingLibrary; +using Xunit; + +namespace Microsoft.Extensions.Internal +{ + public class StackTraceHelperTest + { + [Fact] + public void StackTraceHelper_IncludesLineNumbersForFiles() + { + // Arrange + Exception exception = null; + try + { + // Throwing an exception in the current assembly always seems to populate the full stack + // trace regardless of symbol type. Crossing assembly boundaries ensures PortablePdbReader gets used + // on desktop. + Thrower.Throw(); + } + catch (Exception ex) + { + exception = ex; + } + + // Act + var stackFrames = StackTraceHelper.GetFrames(exception); + + // Assert + Assert.Collection(stackFrames, + frame => + { + Assert.Contains("Thrower.cs", frame.FilePath); + Assert.Equal(17, frame.LineNumber); + }, + frame => + { + Assert.Contains("StackTraceHelperTest.cs", frame.FilePath); + }); + } + + [Fact] + public void StackTraceHelper_PrettyPrintsStackTraceForGenericMethods() + { + // Arrange + var exception = Record.Exception(() => GenericMethod(null)); + + // Act + var stackFrames = StackTraceHelper.GetFrames(exception); + + // Assert + var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray(); + Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.GenericMethod(T val)", methods[0]); + } + + [Fact] + public void StackTraceHelper_PrettyPrintsStackTraceForMethodsWithOutParameters() + { + // Arrange + var exception = Record.Exception(() => MethodWithOutParameter(out var value)); + + // Act + var stackFrames = StackTraceHelper.GetFrames(exception); + + // Assert + var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray(); + Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.MethodWithOutParameter(out int value)", methods[0]); + } + + [Fact] + public void StackTraceHelper_PrettyPrintsStackTraceForMethodsWithGenericOutParameters() + { + // Arrange + var exception = Record.Exception(() => MethodWithGenericOutParameter("Test", out int value)); + + // Act + var stackFrames = StackTraceHelper.GetFrames(exception); + + // Assert + var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray(); + Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.MethodWithGenericOutParameter(string a, out TVal value)", methods[0]); + } + + [Fact] + public void StackTraceHelper_PrettyPrintsStackTraceForMethodsWithRefParameters() + { + // Arrange + var value = 0; + var exception = Record.Exception(() => MethodWithRefParameter(ref value)); + + // Act + var stackFrames = StackTraceHelper.GetFrames(exception); + + // Assert + var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray(); + Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.MethodWithRefParameter(ref int value)", methods[0]); + } + + [Fact] + public void StackTraceHelper_PrettyPrintsStackTraceForMethodsWithGenericRefParameters() + { + // Arrange + var value = 0; + var exception = Record.Exception(() => MethodWithGenericRefParameter(ref value)); + + // Act + var stackFrames = StackTraceHelper.GetFrames(exception); + + // Assert + var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray(); + Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.MethodWithGenericRefParameter(ref TVal value)", methods[0]); + } + + [Fact] + public void StackTraceHelper_PrettyPrintsStackTraceForMethodsWithNullableParameters() + { + // Arrange + var value = 0; + var exception = Record.Exception(() => MethodWithNullableParameter(value)); + + // Act + var stackFrames = StackTraceHelper.GetFrames(exception); + + // Assert + var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray(); + Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.MethodWithNullableParameter(Nullable value)", methods[0]); + } + + [Fact] + public void StackTraceHelper_PrettyPrintsStackTraceForMethodsOnGenericTypes() + { + // Arrange + var exception = Record.Exception(() => new GenericClass().Throw(0)); + + // Act + var stackFrames = StackTraceHelper.GetFrames(exception); + + // Assert + var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray(); + Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest+GenericClass.Throw(T parameter)", methods[0]); + } + + [Fact] + public void StackTraceHelper_ProducesReadableOutput() + { + // Arrange + var expectedCallStack = new List() + { + "Microsoft.Extensions.Internal.StackTraceHelperTest.Iterator()+MoveNext()", + "string.Join(string separator, IEnumerable values)", + "Microsoft.Extensions.Internal.StackTraceHelperTest+GenericClass.GenericMethod(ref V value)", + "Microsoft.Extensions.Internal.StackTraceHelperTest.MethodAsync(int value)", + "Microsoft.Extensions.Internal.StackTraceHelperTest.MethodAsync(TValue value)", + "Microsoft.Extensions.Internal.StackTraceHelperTest.Method(string value)", + "Microsoft.Extensions.Internal.StackTraceHelperTest.StackTraceHelper_ProducesReadableOutput()", + }; + + Exception exception = null; + try + { + Method("test"); + } + catch (Exception ex) + { + exception = ex; + } + + // Act + var stackFrames = StackTraceHelper.GetFrames(exception); + var methodNames = stackFrames.Select(stackFrame => stackFrame.MethodDisplayInfo.ToString()).ToArray(); + + // Assert + Assert.Equal(expectedCallStack, methodNames); + } + + [Fact] + public void StackTraceHelper_DoesNotIncludeInstanceMethodsOnTypesWithStackTraceHiddenAttribute() + { + // Arrange + var exception = Record.Exception(() => InvokeMethodOnTypeWithStackTraceHiddenAttribute()); + + // Act + var stackFrames = StackTraceHelper.GetFrames(exception); + + // Assert + var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray(); + Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.ThrowCore()", methods[0]); + Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.InvokeMethodOnTypeWithStackTraceHiddenAttribute()", methods[1]); + } + + [Fact] + public void StackTraceHelper_DoesNotIncludeStaticMethodsOnTypesWithStackTraceHiddenAttribute() + { + // Arrange + var exception = Record.Exception(() => InvokeStaticMethodOnTypeWithStackTraceHiddenAttribute()); + + // Act + var stackFrames = StackTraceHelper.GetFrames(exception); + + // Assert + var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray(); + Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.ThrowCore()", methods[0]); + Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.InvokeStaticMethodOnTypeWithStackTraceHiddenAttribute()", methods[1]); + } + + [Fact] + public void StackTraceHelper_DoesNotIncludeMethodsWithStackTraceHiddenAttribute() + { + // Arrange + var exception = Record.Exception(() => new TypeWithMethodWithStackTraceHiddenAttribute().Throw()); + + // Act + var stackFrames = StackTraceHelper.GetFrames(exception); + + // Assert + var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray(); + Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.ThrowCore()", methods[0]); + Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest+TypeWithMethodWithStackTraceHiddenAttribute.Throw()", methods[1]); + } + + [Fact] + public void GetFrames_DoesNotFailForDynamicallyGeneratedAssemblies() + { + // Arrange + var action = (Action)Expression.Lambda( + Expression.Throw( + Expression.New(typeof(Exception)))).Compile(); + var exception = Record.Exception(action); + + // Act + var frames = StackTraceHelper.GetFrames(exception).ToArray(); + + // Assert + var frame = frames[0]; + Assert.Null(frame.FilePath); + Assert.Equal($"lambda_method(Closure )", frame.MethodDisplayInfo.ToString()); + } + + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] + async Task MethodAsync(int value) + { + await Task.Delay(0); + return GenericClass.GenericMethod(ref value); + } + + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] + async Task MethodAsync(TValue value) + { + return await MethodAsync(1); + } + + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] + string Method(string value) + { + return MethodAsync(value).GetAwaiter().GetResult(); + } + + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] + static IEnumerable Iterator() + { + yield return "Success"; + throw new Exception(); + } + + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] + void MethodWithOutParameter(out int value) => throw new Exception(); + + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] + void MethodWithGenericOutParameter(string a, out TVal value) => throw new Exception(); + + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] + void MethodWithRefParameter(ref int value) => throw new Exception(); + + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] + void MethodWithGenericRefParameter(ref TVal value) => throw new Exception(); + + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] + void MethodWithNullableParameter(int? value) => throw new Exception(); + + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] + void InvokeMethodOnTypeWithStackTraceHiddenAttribute() => new TypeWithStackTraceHiddenAttribute().Throw(); + + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] + void InvokeStaticMethodOnTypeWithStackTraceHiddenAttribute() => TypeWithStackTraceHiddenAttribute.ThrowStatic(); + + class GenericClass + { + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] + public static string GenericMethod(ref V value) + { + var returnVal = ""; + for (var i = 0; i < 10; i++) + { + returnVal += string.Join(", ", Iterator()); + } + return returnVal; + } + + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] + public void Throw(T parameter) => throw new Exception(); + } + + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] + private void GenericMethod(T val) where T : class => throw new Exception(); + + private class StackTraceHiddenAttribute : Attribute + { + } + + [StackTraceHidden] + private class TypeWithStackTraceHiddenAttribute + { + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] + public void Throw() => ThrowCore(); + + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] + public static void ThrowStatic() => ThrowCore(); + } + + private class TypeWithMethodWithStackTraceHiddenAttribute + { + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] + [StackTraceHidden] + public void MethodWithStackTraceHiddenAttribute() + { + ThrowCore(); + } + + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] + public void Throw() => MethodWithStackTraceHiddenAttribute(); + } + + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] + private static void ThrowCore() => throw new Exception(); + } +} diff --git a/src/Shared/test/Shared.Tests/TypeNameHelperTest.cs b/src/Shared/test/Shared.Tests/TypeNameHelperTest.cs new file mode 100644 index 000000000000..b7f4285bdc4f --- /dev/null +++ b/src/Shared/test/Shared.Tests/TypeNameHelperTest.cs @@ -0,0 +1,263 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using Xunit; + +namespace Microsoft.Extensions.Internal +{ + public class TypeNameHelperTest + { + [Theory] + // Predefined Types + [InlineData(typeof(int), "int")] + [InlineData(typeof(List), "System.Collections.Generic.List")] + [InlineData(typeof(Dictionary), "System.Collections.Generic.Dictionary")] + [InlineData(typeof(Dictionary>), "System.Collections.Generic.Dictionary>")] + [InlineData(typeof(List>), "System.Collections.Generic.List>")] + // Classes inside NonGeneric class + [InlineData(typeof(A), + "Microsoft.Extensions.Internal.TypeNameHelperTest+A")] + [InlineData(typeof(B), + "Microsoft.Extensions.Internal.TypeNameHelperTest+B")] + [InlineData(typeof(C), + "Microsoft.Extensions.Internal.TypeNameHelperTest+C")] + [InlineData(typeof(B>), + "Microsoft.Extensions.Internal.TypeNameHelperTest+B>")] + [InlineData(typeof(C>), + "Microsoft.Extensions.Internal.TypeNameHelperTest+C>")] + // Classes inside Generic class + [InlineData(typeof(Outer.D), + "Microsoft.Extensions.Internal.TypeNameHelperTest+Outer+D")] + [InlineData(typeof(Outer.E), + "Microsoft.Extensions.Internal.TypeNameHelperTest+Outer+E")] + [InlineData(typeof(Outer.F), + "Microsoft.Extensions.Internal.TypeNameHelperTest+Outer+F")] + [InlineData(typeof(Level1.Level2.Level3), + "Microsoft.Extensions.Internal.TypeNameHelperTest+Level1+Level2+Level3")] + [InlineData(typeof(Outer.E.E>), + "Microsoft.Extensions.Internal.TypeNameHelperTest+Outer+E+E>")] + [InlineData(typeof(Outer.F.E>), + "Microsoft.Extensions.Internal.TypeNameHelperTest+Outer+F+E>")] + [InlineData(typeof(OuterGeneric.InnerNonGeneric.InnerGeneric.InnerGenericLeafNode), + "Microsoft.Extensions.Internal.TypeNameHelperTest+OuterGeneric+InnerNonGeneric+InnerGeneric+InnerGenericLeafNode")] + public void Can_pretty_print_CLR_full_name(Type type, string expected) + { + Assert.Equal(expected, TypeNameHelper.GetTypeDisplayName(type)); + } + + [Fact] + public void DoesNotPrintNamespace_ForGenericTypes_IfNullOrEmpty() + { + // Arrange + var type = typeof(ClassInGlobalNamespace); + + // Act & Assert + Assert.Equal("ClassInGlobalNamespace", TypeNameHelper.GetTypeDisplayName(type)); + } + + [Theory] + // Predefined Types + [InlineData(typeof(int), "int")] + [InlineData(typeof(List), "List")] + [InlineData(typeof(Dictionary), "Dictionary")] + [InlineData(typeof(Dictionary>), "Dictionary>")] + [InlineData(typeof(List>), "List>")] + // Classes inside NonGeneric class + [InlineData(typeof(A), "A")] + [InlineData(typeof(B), "B")] + [InlineData(typeof(C), "C")] + [InlineData(typeof(C>), "C>")] + [InlineData(typeof(B>), "B>")] + // Classes inside Generic class + [InlineData(typeof(Outer.D), "D")] + [InlineData(typeof(Outer.E), "E")] + [InlineData(typeof(Outer.F), "F")] + [InlineData(typeof(Outer.F.E>), "F>")] + [InlineData(typeof(Outer.E.E>), "E>")] + [InlineData(typeof(OuterGeneric.InnerNonGeneric.InnerGeneric.InnerGenericLeafNode), "InnerGenericLeafNode")] + public void Can_pretty_print_CLR_name(Type type, string expected) + { + Assert.Equal(expected, TypeNameHelper.GetTypeDisplayName(type, false)); + } + + [Theory] + [InlineData(typeof(void), "void")] + [InlineData(typeof(bool), "bool")] + [InlineData(typeof(byte), "byte")] + [InlineData(typeof(char), "char")] + [InlineData(typeof(decimal), "decimal")] + [InlineData(typeof(double), "double")] + [InlineData(typeof(float), "float")] + [InlineData(typeof(int), "int")] + [InlineData(typeof(long), "long")] + [InlineData(typeof(object), "object")] + [InlineData(typeof(sbyte), "sbyte")] + [InlineData(typeof(short), "short")] + [InlineData(typeof(string), "string")] + [InlineData(typeof(uint), "uint")] + [InlineData(typeof(ulong), "ulong")] + [InlineData(typeof(ushort), "ushort")] + public void Returns_common_name_for_built_in_types(Type type, string expected) + { + Assert.Equal(expected, TypeNameHelper.GetTypeDisplayName(type)); + } + + [Theory] + [InlineData(typeof(int[]), true, "int[]")] + [InlineData(typeof(string[][]), true, "string[][]")] + [InlineData(typeof(int[,]), true, "int[,]")] + [InlineData(typeof(bool[,,,]), true, "bool[,,,]")] + [InlineData(typeof(A[,][,,]), true, "Microsoft.Extensions.Internal.TypeNameHelperTest+A[,][,,]")] + [InlineData(typeof(List), true, "System.Collections.Generic.List")] + [InlineData(typeof(List[,][,,]), false, "List[,][,,]")] + public void Can_pretty_print_array_name(Type type, bool fullName, string expected) + { + Assert.Equal(expected, TypeNameHelper.GetTypeDisplayName(type, fullName)); + } + + public static TheoryData GetOpenGenericsTestData() + { + var openDictionaryType = typeof(Dictionary<,>); + var genArgsDictionary = openDictionaryType.GetGenericArguments(); + genArgsDictionary[0] = typeof(B<>); + var closedDictionaryType = openDictionaryType.MakeGenericType(genArgsDictionary); + + var openLevelType = typeof(Level1<>.Level2<>.Level3<>); + var genArgsLevel = openLevelType.GetGenericArguments(); + genArgsLevel[1] = typeof(string); + var closedLevelType = openLevelType.MakeGenericType(genArgsLevel); + + var openInnerType = typeof(OuterGeneric<>.InnerNonGeneric.InnerGeneric<,>.InnerGenericLeafNode<>); + var genArgsInnerType = openInnerType.GetGenericArguments(); + genArgsInnerType[3] = typeof(bool); + var closedInnerType = openInnerType.MakeGenericType(genArgsInnerType); + + return new TheoryData + { + { typeof(List<>), false, "List<>" }, + { typeof(Dictionary<,>), false , "Dictionary<,>" }, + { typeof(List<>), true , "System.Collections.Generic.List<>" }, + { typeof(Dictionary<,>), true , "System.Collections.Generic.Dictionary<,>" }, + { typeof(Level1<>.Level2<>.Level3<>), true, "Microsoft.Extensions.Internal.TypeNameHelperTest+Level1<>+Level2<>+Level3<>" }, + { + typeof(PartiallyClosedGeneric<>).BaseType, + true, + "Microsoft.Extensions.Internal.TypeNameHelperTest+C<, int>" + }, + { + typeof(OuterGeneric<>.InnerNonGeneric.InnerGeneric<,>.InnerGenericLeafNode<>), + true, + "Microsoft.Extensions.Internal.TypeNameHelperTest+OuterGeneric<>+InnerNonGeneric+InnerGeneric<,>+InnerGenericLeafNode<>" + }, + { + closedDictionaryType, + true, + "System.Collections.Generic.Dictionary,>" + }, + { + closedLevelType, + true, + "Microsoft.Extensions.Internal.TypeNameHelperTest+Level1<>+Level2+Level3<>" + }, + { + closedInnerType, + true, + "Microsoft.Extensions.Internal.TypeNameHelperTest+OuterGeneric<>+InnerNonGeneric+InnerGeneric<,>+InnerGenericLeafNode" + } + }; + } + + [Theory] + [MemberData(nameof(GetOpenGenericsTestData))] + public void Can_pretty_print_open_generics(Type type, bool fullName, string expected) + { + Assert.Equal(expected, TypeNameHelper.GetTypeDisplayName(type, fullName)); + } + + public static TheoryData GetTypeDisplayName_IncludesGenericParameterNamesWhenOptionIsSetData => + new TheoryData + { + { typeof(B<>),"Microsoft.Extensions.Internal.TypeNameHelperTest+B" }, + { typeof(C<,>),"Microsoft.Extensions.Internal.TypeNameHelperTest+C" }, + { typeof(PartiallyClosedGeneric<>).BaseType,"Microsoft.Extensions.Internal.TypeNameHelperTest+C" }, + { typeof(Level1<>.Level2<>),"Microsoft.Extensions.Internal.TypeNameHelperTest+Level1+Level2" }, + }; + + [Theory] + [MemberData(nameof(GetTypeDisplayName_IncludesGenericParameterNamesWhenOptionIsSetData))] + public void GetTypeDisplayName_IncludesGenericParameterNamesWhenOptionIsSet(Type type, string expected) + { + // Arrange & Act + var actual = TypeNameHelper.GetTypeDisplayName(type, fullName: true, includeGenericParameterNames: true); + + // Assert + Assert.Equal(expected, actual); + } + + public static TheoryData GetTypeDisplayName_WithoutFullName_IncludesGenericParameterNamesWhenOptionIsSetData => + new TheoryData + { + { typeof(B<>),"B" }, + { typeof(C<,>),"C" }, + { typeof(PartiallyClosedGeneric<>).BaseType,"C" }, + { typeof(Level1<>.Level2<>),"Level2" }, + }; + + [Theory] + [MemberData(nameof(GetTypeDisplayName_WithoutFullName_IncludesGenericParameterNamesWhenOptionIsSetData))] + public void GetTypeDisplayName_WithoutFullName_IncludesGenericParameterNamesWhenOptionIsSet(Type type, string expected) + { + // Arrange & Act + var actual = TypeNameHelper.GetTypeDisplayName(type, fullName: false, includeGenericParameterNames: true); + + // Assert + Assert.Equal(expected, actual); + } + + private class A { } + + private class B { } + + private class C { } + + private class PartiallyClosedGeneric : C { } + + private class Outer + { + public class D { } + + public class E { } + + public class F { } + } + + private class OuterGeneric + { + public class InnerNonGeneric + { + public class InnerGeneric + { + public class InnerGenericLeafNode { } + + public class InnerLeafNode { } + } + } + } + + private class Level1 + { + public class Level2 + { + public class Level3 + { + } + } + } + } +} + +internal class ClassInGlobalNamespace +{ +} diff --git a/src/Shared/test/Shared.Tests/ValueStopwatchTest.cs b/src/Shared/test/Shared.Tests/ValueStopwatchTest.cs new file mode 100644 index 000000000000..fffc2c6656ef --- /dev/null +++ b/src/Shared/test/Shared.Tests/ValueStopwatchTest.cs @@ -0,0 +1,39 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Threading.Tasks; +using Xunit; + +namespace Microsoft.Extensions.Internal.Test +{ + public class ValueStopwatchTest + { + [Fact] + public void IsActiveIsFalseForDefaultValueStopwatch() + { + Assert.False(default(ValueStopwatch).IsActive); + } + + [Fact] + public void IsActiveIsTrueWhenValueStopwatchStartedWithStartNew() + { + Assert.True(ValueStopwatch.StartNew().IsActive); + } + + [Fact] + public void GetElapsedTimeThrowsIfValueStopwatchIsDefaultValue() + { + var stopwatch = default(ValueStopwatch); + Assert.Throws(() => stopwatch.GetElapsedTime()); + } + + [Fact] + public async Task GetElapsedTimeReturnsTimeElapsedSinceStart() + { + var stopwatch = ValueStopwatch.StartNew(); + await Task.Delay(200); + Assert.True(stopwatch.GetElapsedTime().TotalMilliseconds > 0); + } + } +} diff --git a/src/Shared/test/Shared.Tests/WebEncodersTests.cs b/src/Shared/test/Shared.Tests/WebEncodersTests.cs new file mode 100644 index 000000000000..5c71403fd653 --- /dev/null +++ b/src/Shared/test/Shared.Tests/WebEncodersTests.cs @@ -0,0 +1,113 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Linq; +using Xunit; + +namespace Microsoft.Extensions.Internal +{ + public class WebEncodersTests + { + [Theory] + [InlineData("", 1, 0)] + [InlineData("", 0, 1)] + [InlineData("0123456789", 9, 2)] + [InlineData("0123456789", Int32.MaxValue, 2)] + [InlineData("0123456789", 9, -1)] + public void Base64UrlDecode_BadOffsets(string input, int offset, int count) + { + // Act & assert + Assert.ThrowsAny(() => + { + var retVal = WebEncoders.Base64UrlDecode(input, offset, count); + }); + } + + [Theory] + [InlineData("x")] + [InlineData("(x)")] + public void Base64UrlDecode_MalformedInput(string input) + { + // Act & assert + Assert.Throws(() => + { + var retVal = WebEncoders.Base64UrlDecode(input); + }); + } + + [Theory] + [InlineData("", "")] + [InlineData("123456qwerty++//X+/x", "123456qwerty--__X-_x")] + [InlineData("123456qwerty++//X+/xxw==", "123456qwerty--__X-_xxw")] + [InlineData("123456qwerty++//X+/xxw0=", "123456qwerty--__X-_xxw0")] + public void Base64UrlEncode_And_Decode(string base64Input, string expectedBase64Url) + { + // Arrange + byte[] input = new byte[3].Concat(Convert.FromBase64String(base64Input)).Concat(new byte[2]).ToArray(); + + // Act & assert - 1 + string actualBase64Url = WebEncoders.Base64UrlEncode(input, 3, input.Length - 5); // also helps test offsets + Assert.Equal(expectedBase64Url, actualBase64Url); + + // Act & assert - 2 + // Verify that values round-trip + byte[] roundTripped = WebEncoders.Base64UrlDecode("xx" + actualBase64Url + "yyy", 2, actualBase64Url.Length); // also helps test offsets + string roundTrippedAsBase64 = Convert.ToBase64String(roundTripped); + Assert.Equal(roundTrippedAsBase64, base64Input); + } + + [Theory] + [InlineData("", "")] + [InlineData("123456qwerty++//X+/x", "123456qwerty--__X-_x")] + [InlineData("123456qwerty++//X+/xxw==", "123456qwerty--__X-_xxw")] + [InlineData("123456qwerty++//X+/xxw0=", "123456qwerty--__X-_xxw0")] + public void Base64UrlEncode_And_Decode_WithBufferOffsets(string base64Input, string expectedBase64Url) + { + // Arrange + var input = new byte[3].Concat(Convert.FromBase64String(base64Input)).Concat(new byte[2]).ToArray(); + var buffer = new char[30]; + var output = new char[30]; + for (var i = 0; i < buffer.Length; i++) + { + buffer[i] = '^'; + output[i] = '^'; + } + + // Act 1 + var numEncodedChars = + WebEncoders.Base64UrlEncode(input, offset: 3, output: output, outputOffset: 4, count: input.Length - 5); + + // Assert 1 + var encodedString = new string(output, startIndex: 4, length: numEncodedChars); + Assert.Equal(expectedBase64Url, encodedString); + + // Act 2 + var roundTripInput = new string(output); + var roundTripped = + WebEncoders.Base64UrlDecode(roundTripInput, offset: 4, buffer: buffer, bufferOffset: 5, count: numEncodedChars); + + // Assert 2, verify that values round-trip + var roundTrippedAsBase64 = Convert.ToBase64String(roundTripped); + Assert.Equal(roundTrippedAsBase64, base64Input); + } + + [Theory] + [InlineData(0, 1, 0)] + [InlineData(0, 0, 1)] + [InlineData(10, 9, 2)] + [InlineData(10, Int32.MaxValue, 2)] + [InlineData(10, 9, -1)] + public void Base64UrlEncode_BadOffsets(int inputLength, int offset, int count) + { + // Arrange + byte[] input = new byte[inputLength]; + + // Act & assert + Assert.ThrowsAny(() => + { + var retVal = WebEncoders.Base64UrlEncode(input, offset, count); + }); + } + } +} diff --git a/src/Shared/test/testassets/ThrowingLibrary/Thrower.cs b/src/Shared/test/testassets/ThrowingLibrary/Thrower.cs new file mode 100644 index 000000000000..babe2387c6ab --- /dev/null +++ b/src/Shared/test/testassets/ThrowingLibrary/Thrower.cs @@ -0,0 +1,20 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Runtime.CompilerServices; + +namespace ThrowingLibrary +{ + // Throwing an exception in the current assembly always seems to populate the full stack + // trace regardless of symbol type. This type exists to simulate an exception thrown + // across assemblies which is the typical use case for StackTraceHelper. + public static class Thrower + { + [MethodImpl(MethodImplOptions.NoInlining)] + public static void Throw() + { + throw new DivideByZeroException(); + } + } +} diff --git a/src/Shared/test/testassets/ThrowingLibrary/ThrowingLibrary.csproj b/src/Shared/test/testassets/ThrowingLibrary/ThrowingLibrary.csproj new file mode 100644 index 000000000000..d77d392873d7 --- /dev/null +++ b/src/Shared/test/testassets/ThrowingLibrary/ThrowingLibrary.csproj @@ -0,0 +1,8 @@ + + + + netstandard2.0 + portable + + + From 5460fd093ebfc95db12656c5f389de09e0572b2e Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Tue, 30 Oct 2018 16:32:28 -0700 Subject: [PATCH 002/183] Fix the package id for Microsoft.AspNetCore.BenchmarkRunner.Sources \n\nCommit migrated from https://github.com/dotnet/extensions/commit/d0aa5c17017254eeba987f283f401a724e88a56f --- src/Shared/BenchmarkRunner/Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Shared/BenchmarkRunner/Directory.Build.props b/src/Shared/BenchmarkRunner/Directory.Build.props index 34cf593291de..d2f65e8d3dc4 100644 --- a/src/Shared/BenchmarkRunner/Directory.Build.props +++ b/src/Shared/BenchmarkRunner/Directory.Build.props @@ -3,6 +3,6 @@ - Microsoft.Extensions.$(ProjectDirName).Sources + Microsoft.AspNetCore.BenchmarkRunner.Sources From 45b0b83997cf5369d1e6541952af4e35de31c694 Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Tue, 30 Oct 2018 16:39:06 -0700 Subject: [PATCH 003/183] Merge branch 'release/2.1' into release/2.2 \n\nCommit migrated from https://github.com/dotnet/extensions/commit/18fcffbd251abf944e4cc82a3c775882f687a1b2 --- src/ObjectPool/src/DefaultObjectPool.cs | 17 +- src/ObjectPool/test/DefaultObjectPoolTest.cs | 4 +- src/ObjectPool/test/ThreadingTest.cs | 80 ++++ .../AspNetCoreBenchmarkAttribute.cs | 66 ++- .../BenchmarkRunner/DefaultCoreConfig.cs | 4 + .../BenchmarkRunner/DefaultCoreDebugConfig.cs | 23 + .../DefaultCorePerfLabConfig.cs | 48 ++ .../DefaultCoreProfileConfig.cs | 32 ++ .../ParamsDisplayInfoColumn.cs | 26 ++ src/Shared/BenchmarkRunner/Program.cs | 32 +- .../CertificateManager.cs | 425 ++++++++++++++---- .../CertificatePurpose.cs | 3 +- .../EnsureCertificateResult.cs | 2 +- .../NonCapturingTimer/NonCapturingTimer.cs | 43 ++ src/Shared/Process/ProcessHelper.cs | 35 +- src/Shared/PropertyHelper/PropertyHelper.cs | 29 +- .../Shared.Tests/CertificateManagerTests.cs | 196 ++++---- .../test/Shared.Tests/DotNetMuxerTests.cs | 2 +- .../Shared.Tests/NonCapturingTimerTest.cs | 40 ++ .../test/Shared.Tests/PropertyHelperTest.cs | 32 ++ .../ThrowingLibrary/ThrowingLibrary.csproj | 1 + src/Testing/src/CultureReplacer.cs | 79 ++++ src/Testing/src/ExceptionAssertions.cs | 271 +++++++++++ src/Testing/src/HttpClientSlim.cs | 158 +++++++ .../src/Microsoft.AspNetCore.Testing.csproj | 33 ++ src/Testing/src/Properties/AssemblyInfo.cs | 6 + src/Testing/src/ReplaceCulture.cs | 70 +++ src/Testing/src/TaskExtensions.cs | 64 +++ src/Testing/src/TestPathUtilities.cs | 31 ++ src/Testing/src/TestPlatformHelper.cs | 23 + .../src/Tracing/CollectingEventListener.cs | 60 +++ src/Testing/src/Tracing/EventAssert.cs | 60 +++ .../src/Tracing/EventSourceTestBase.cs | 39 ++ .../EventSourceTestCollection.cs | 10 + .../src/xunit/ConditionalFactAttribute.cs | 15 + .../src/xunit/ConditionalFactDiscoverer.cs | 27 ++ .../src/xunit/ConditionalTheoryAttribute.cs | 15 + .../src/xunit/ConditionalTheoryDiscoverer.cs | 47 ++ src/Testing/src/xunit/DockerOnlyAttribute.cs | 38 ++ ...vironmentVariableSkipConditionAttribute.cs | 95 ++++ .../xunit/FrameworkSkipConditionAttribute.cs | 57 +++ src/Testing/src/xunit/IEnvironmentVariable.cs | 10 + src/Testing/src/xunit/ITestCondition.cs | 12 + .../src/xunit/MinimumOsVersionAttribute.cs | 111 +++++ .../src/xunit/OSSkipConditionAttribute.cs | 99 ++++ src/Testing/src/xunit/OperatingSystems.cs | 15 + src/Testing/src/xunit/RuntimeFrameworks.cs | 16 + src/Testing/src/xunit/SkippedTestCase.cs | 40 ++ src/Testing/src/xunit/TestMethodExtensions.cs | 34 ++ src/Testing/src/xunit/WindowsVersions.cs | 18 + .../test/CollectingEventListenerTest.cs | 87 ++++ src/Testing/test/ConditionalFactTest.cs | 60 +++ src/Testing/test/ConditionalTheoryTest.cs | 156 +++++++ src/Testing/test/DockerTests.cs | 21 + .../EnvironmentVariableSkipConditionTest.cs | 166 +++++++ src/Testing/test/ExceptionAssertTest.cs | 39 ++ src/Testing/test/HttpClientSlimTest.cs | 117 +++++ .../Microsoft.AspNetCore.Testing.Tests.csproj | 28 ++ .../test/OSSkipConditionAttributeTest.cs | 132 ++++++ src/Testing/test/OSSkipConditionTest.cs | 116 +++++ .../test/ReplaceCultureAttributeTest.cs | 66 +++ src/Testing/test/TaskExtensionsTest.cs | 18 + src/Testing/test/TestPathUtilitiesTest.cs | 31 ++ src/Testing/test/TestPlatformHelperTest.cs | 55 +++ 64 files changed, 3517 insertions(+), 268 deletions(-) create mode 100644 src/ObjectPool/test/ThreadingTest.cs create mode 100644 src/Shared/BenchmarkRunner/DefaultCoreDebugConfig.cs create mode 100644 src/Shared/BenchmarkRunner/DefaultCorePerfLabConfig.cs create mode 100644 src/Shared/BenchmarkRunner/DefaultCoreProfileConfig.cs create mode 100644 src/Shared/BenchmarkRunner/ParamsDisplayInfoColumn.cs create mode 100644 src/Shared/NonCapturingTimer/NonCapturingTimer.cs create mode 100644 src/Shared/test/Shared.Tests/NonCapturingTimerTest.cs create mode 100644 src/Testing/src/CultureReplacer.cs create mode 100644 src/Testing/src/ExceptionAssertions.cs create mode 100644 src/Testing/src/HttpClientSlim.cs create mode 100644 src/Testing/src/Microsoft.AspNetCore.Testing.csproj create mode 100644 src/Testing/src/Properties/AssemblyInfo.cs create mode 100644 src/Testing/src/ReplaceCulture.cs create mode 100644 src/Testing/src/TaskExtensions.cs create mode 100644 src/Testing/src/TestPathUtilities.cs create mode 100644 src/Testing/src/TestPlatformHelper.cs create mode 100644 src/Testing/src/Tracing/CollectingEventListener.cs create mode 100644 src/Testing/src/Tracing/EventAssert.cs create mode 100644 src/Testing/src/Tracing/EventSourceTestBase.cs create mode 100644 src/Testing/src/contentFiles/cs/netstandard2.0/EventSourceTestCollection.cs create mode 100644 src/Testing/src/xunit/ConditionalFactAttribute.cs create mode 100644 src/Testing/src/xunit/ConditionalFactDiscoverer.cs create mode 100644 src/Testing/src/xunit/ConditionalTheoryAttribute.cs create mode 100644 src/Testing/src/xunit/ConditionalTheoryDiscoverer.cs create mode 100644 src/Testing/src/xunit/DockerOnlyAttribute.cs create mode 100644 src/Testing/src/xunit/EnvironmentVariableSkipConditionAttribute.cs create mode 100644 src/Testing/src/xunit/FrameworkSkipConditionAttribute.cs create mode 100644 src/Testing/src/xunit/IEnvironmentVariable.cs create mode 100644 src/Testing/src/xunit/ITestCondition.cs create mode 100644 src/Testing/src/xunit/MinimumOsVersionAttribute.cs create mode 100644 src/Testing/src/xunit/OSSkipConditionAttribute.cs create mode 100644 src/Testing/src/xunit/OperatingSystems.cs create mode 100644 src/Testing/src/xunit/RuntimeFrameworks.cs create mode 100644 src/Testing/src/xunit/SkippedTestCase.cs create mode 100644 src/Testing/src/xunit/TestMethodExtensions.cs create mode 100644 src/Testing/src/xunit/WindowsVersions.cs create mode 100644 src/Testing/test/CollectingEventListenerTest.cs create mode 100644 src/Testing/test/ConditionalFactTest.cs create mode 100644 src/Testing/test/ConditionalTheoryTest.cs create mode 100644 src/Testing/test/DockerTests.cs create mode 100644 src/Testing/test/EnvironmentVariableSkipConditionTest.cs create mode 100644 src/Testing/test/ExceptionAssertTest.cs create mode 100644 src/Testing/test/HttpClientSlimTest.cs create mode 100644 src/Testing/test/Microsoft.AspNetCore.Testing.Tests.csproj create mode 100644 src/Testing/test/OSSkipConditionAttributeTest.cs create mode 100644 src/Testing/test/OSSkipConditionTest.cs create mode 100644 src/Testing/test/ReplaceCultureAttributeTest.cs create mode 100644 src/Testing/test/TaskExtensionsTest.cs create mode 100644 src/Testing/test/TestPathUtilitiesTest.cs create mode 100644 src/Testing/test/TestPlatformHelperTest.cs diff --git a/src/ObjectPool/src/DefaultObjectPool.cs b/src/ObjectPool/src/DefaultObjectPool.cs index 87825967ac7a..dcd7f1c7152c 100644 --- a/src/ObjectPool/src/DefaultObjectPool.cs +++ b/src/ObjectPool/src/DefaultObjectPool.cs @@ -42,8 +42,7 @@ bool IsDefaultPolicy() public override T Get() { - T item = _firstItem; - + var item = _firstItem; if (item == null || Interlocked.CompareExchange(ref _firstItem, null, item) != item) { item = GetViaScan(); @@ -55,12 +54,10 @@ public override T Get() [MethodImpl(MethodImplOptions.AggressiveInlining)] private T GetViaScan() { - ObjectWrapper[] items = _items; - + var items = _items; for (var i = 0; i < items.Length; i++) { - T item = items[i]; - + var item = items[i].Element; if (item != null && Interlocked.CompareExchange(ref items[i].Element, null, item) == item) { return item; @@ -88,21 +85,17 @@ public override void Return(T obj) [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ReturnViaScan(T obj) { - ObjectWrapper[] items = _items; - + var items = _items; for (var i = 0; i < items.Length && Interlocked.CompareExchange(ref items[i].Element, obj, null) != null; ++i) { } } + // PERF: the struct wrapper avoids array-covariance-checks from the runtime when assigning to elements of the array. [DebuggerDisplay("{Element}")] private struct ObjectWrapper { public T Element; - - public ObjectWrapper(T item) => Element = item; - - public static implicit operator T(ObjectWrapper wrapper) => wrapper.Element; } } } diff --git a/src/ObjectPool/test/DefaultObjectPoolTest.cs b/src/ObjectPool/test/DefaultObjectPoolTest.cs index ca14a3f53b6a..b44aa7e1c743 100644 --- a/src/ObjectPool/test/DefaultObjectPoolTest.cs +++ b/src/ObjectPool/test/DefaultObjectPoolTest.cs @@ -1,12 +1,10 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; using System.Collections.Generic; -using System.Threading.Tasks; using Xunit; -namespace Microsoft.Extensions.ObjectPool.Test +namespace Microsoft.Extensions.ObjectPool { public class DefaultObjectPoolTest { diff --git a/src/ObjectPool/test/ThreadingTest.cs b/src/ObjectPool/test/ThreadingTest.cs new file mode 100644 index 000000000000..541bc5ffd41d --- /dev/null +++ b/src/ObjectPool/test/ThreadingTest.cs @@ -0,0 +1,80 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Threading; +using Xunit; + +namespace Microsoft.Extensions.ObjectPool +{ + public class ThreadingTest + { + private CancellationTokenSource _cts; + private DefaultObjectPool _pool; + private bool _foundError; + + [Fact] + public void RunThreadingTest() + { + _cts = new CancellationTokenSource(); + _pool = new DefaultObjectPool(new DefaultPooledObjectPolicy(), 10); + + var threads = new Thread[8]; + for (var i = 0; i < threads.Length; i++) + { + threads[i] = new Thread(Run); + } + + for (var i = 0; i < threads.Length; i++) + { + threads[i].Start(); + } + + // Run for 1000ms + _cts.CancelAfter(1000); + + // Wait for all threads to complete + for (var i = 0; i < threads.Length; i++) + { + threads[i].Join(); + } + + Assert.False(_foundError, "Race condition found. An item was shared across threads."); + } + + private void Run() + { + while (!_cts.IsCancellationRequested) + { + var obj = _pool.Get(); + if (obj.i != 0) + { + _foundError = true; + } + obj.i = 123; + + var obj2 = _pool.Get(); + if (obj2.i != 0) + { + _foundError = true; + } + obj2.i = 321; + + obj.Reset(); + _pool.Return(obj); + + obj2.Reset(); + _pool.Return(obj2); + } + } + + private class Item + { + public int i = 0; + + public void Reset() + { + i = 0; + } + } + } +} diff --git a/src/Shared/BenchmarkRunner/AspNetCoreBenchmarkAttribute.cs b/src/Shared/BenchmarkRunner/AspNetCoreBenchmarkAttribute.cs index a4044d1b5e84..d16493a738e6 100644 --- a/src/Shared/BenchmarkRunner/AspNetCoreBenchmarkAttribute.cs +++ b/src/Shared/BenchmarkRunner/AspNetCoreBenchmarkAttribute.cs @@ -2,38 +2,72 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Linq; -using System.Reflection; -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Running; +using System.Collections.Generic; using BenchmarkDotNet.Configs; -using BenchmarkDotNet.Jobs; -using BenchmarkDotNet.Toolchains.InProcess; namespace BenchmarkDotNet.Attributes { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Assembly)] internal class AspNetCoreBenchmarkAttribute : Attribute, IConfigSource { - public static bool UseValidationConfig { get; set; } - - public Type ConfigType { get; } - public Type ValidationConfigType { get; } - - public AspNetCoreBenchmarkAttribute() : this(typeof(DefaultCoreConfig)) + public AspNetCoreBenchmarkAttribute() + : this(typeof(DefaultCoreConfig)) { } - public AspNetCoreBenchmarkAttribute(Type configType) : this(configType, typeof(DefaultCoreValidationConfig)) + public AspNetCoreBenchmarkAttribute(Type configType) + : this(configType, typeof(DefaultCoreValidationConfig)) { } public AspNetCoreBenchmarkAttribute(Type configType, Type validationConfigType) { - ConfigType = configType; - ValidationConfigType = validationConfigType; + ConfigTypes = new Dictionary() + { + { NamedConfiguration.Default, typeof(DefaultCoreConfig) }, + { NamedConfiguration.Validation, typeof(DefaultCoreValidationConfig) }, + { NamedConfiguration.Profile, typeof(DefaultCoreProfileConfig) }, + { NamedConfiguration.Debug, typeof(DefaultCoreDebugConfig) }, + { NamedConfiguration.PerfLab, typeof(DefaultCorePerfLabConfig) }, + }; + + if (configType != null) + { + ConfigTypes[NamedConfiguration.Default] = configType; + } + + if (validationConfigType != null) + { + ConfigTypes[NamedConfiguration.Validation] = validationConfigType; + } } - public IConfig Config => (IConfig) Activator.CreateInstance(UseValidationConfig ? ValidationConfigType : ConfigType, Array.Empty()); + public IConfig Config + { + get + { + if (!ConfigTypes.TryGetValue(ConfigName ?? NamedConfiguration.Default, out var configType)) + { + var message = $"Could not find a configuration matching {ConfigName}. " + + $"Known configurations: {string.Join(", ", ConfigTypes.Keys)}"; + throw new InvalidOperationException(message); + } + + return (IConfig)Activator.CreateInstance(configType, Array.Empty()); + } + } + + public Dictionary ConfigTypes { get; } + + public static string ConfigName { get; set; } = NamedConfiguration.Default; + + public static class NamedConfiguration + { + public static readonly string Default = "default"; + public static readonly string Validation = "validation"; + public static readonly string Profile = "profile"; + public static readonly string Debug = "debug"; + public static readonly string PerfLab = "perflab"; + } } } diff --git a/src/Shared/BenchmarkRunner/DefaultCoreConfig.cs b/src/Shared/BenchmarkRunner/DefaultCoreConfig.cs index a8d9d6053646..5e2bafd50636 100644 --- a/src/Shared/BenchmarkRunner/DefaultCoreConfig.cs +++ b/src/Shared/BenchmarkRunner/DefaultCoreConfig.cs @@ -28,7 +28,11 @@ public DefaultCoreConfig() Add(JitOptimizationsValidator.FailOnError); Add(Job.Core +#if NETCOREAPP2_1 .With(CsProjCoreToolchain.From(NetCoreAppSettings.NetCoreApp21)) +#else + .With(CsProjCoreToolchain.From(new NetCoreAppSettings("netcoreapp2.2", null, ".NET Core 2.2"))) +#endif .With(new GcMode { Server = true }) .With(RunStrategy.Throughput)); } diff --git a/src/Shared/BenchmarkRunner/DefaultCoreDebugConfig.cs b/src/Shared/BenchmarkRunner/DefaultCoreDebugConfig.cs new file mode 100644 index 000000000000..f51bed2db997 --- /dev/null +++ b/src/Shared/BenchmarkRunner/DefaultCoreDebugConfig.cs @@ -0,0 +1,23 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Engines; +using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Loggers; +using BenchmarkDotNet.Validators; + +namespace BenchmarkDotNet.Attributes +{ + internal class DefaultCoreDebugConfig : ManualConfig + { + public DefaultCoreDebugConfig() + { + Add(ConsoleLogger.Default); + Add(JitOptimizationsValidator.DontFailOnError); + + Add(Job.InProcess + .With(RunStrategy.Throughput)); + } + } +} diff --git a/src/Shared/BenchmarkRunner/DefaultCorePerfLabConfig.cs b/src/Shared/BenchmarkRunner/DefaultCorePerfLabConfig.cs new file mode 100644 index 000000000000..5cc809e16613 --- /dev/null +++ b/src/Shared/BenchmarkRunner/DefaultCorePerfLabConfig.cs @@ -0,0 +1,48 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using BenchmarkDotNet.Columns; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Diagnosers; +using BenchmarkDotNet.Engines; +using BenchmarkDotNet.Exporters; +using BenchmarkDotNet.Exporters.Csv; +using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Loggers; +using BenchmarkDotNet.Validators; + +namespace BenchmarkDotNet.Attributes +{ + internal class DefaultCorePerfLabConfig : ManualConfig + { + public DefaultCorePerfLabConfig() + { + Add(ConsoleLogger.Default); + + Add(MemoryDiagnoser.Default); + Add(StatisticColumn.OperationsPerSecond); + Add(new ParamsSummaryColumn()); + Add(DefaultColumnProviders.Statistics, DefaultColumnProviders.Diagnosers, DefaultColumnProviders.Target); + + // TODO: When upgrading to BDN 0.11.1, use Add(DefaultColumnProviders.Descriptor); + // DefaultColumnProviders.Target is deprecated + + Add(JitOptimizationsValidator.FailOnError); + + Add(Job.InProcess + .With(RunStrategy.Throughput)); + + Add(MarkdownExporter.GitHub); + + Add(new CsvExporter( + CsvSeparator.Comma, + new Reports.SummaryStyle + { + PrintUnitsInHeader = true, + PrintUnitsInContent = false, + TimeUnit = Horology.TimeUnit.Microsecond, + SizeUnit = SizeUnit.KB + })); + } + } +} diff --git a/src/Shared/BenchmarkRunner/DefaultCoreProfileConfig.cs b/src/Shared/BenchmarkRunner/DefaultCoreProfileConfig.cs new file mode 100644 index 000000000000..1b59cb89c54c --- /dev/null +++ b/src/Shared/BenchmarkRunner/DefaultCoreProfileConfig.cs @@ -0,0 +1,32 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using BenchmarkDotNet.Columns; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Diagnosers; +using BenchmarkDotNet.Engines; +using BenchmarkDotNet.Exporters; +using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Loggers; +using BenchmarkDotNet.Validators; + +namespace BenchmarkDotNet.Attributes +{ + internal class DefaultCoreProfileConfig : ManualConfig + { + public DefaultCoreProfileConfig() + { + Add(ConsoleLogger.Default); + Add(MarkdownExporter.GitHub); + + Add(MemoryDiagnoser.Default); + Add(StatisticColumn.OperationsPerSecond); + Add(DefaultColumnProviders.Instance); + + Add(JitOptimizationsValidator.FailOnError); + + Add(Job.InProcess + .With(RunStrategy.Throughput)); + } + } +} diff --git a/src/Shared/BenchmarkRunner/ParamsDisplayInfoColumn.cs b/src/Shared/BenchmarkRunner/ParamsDisplayInfoColumn.cs new file mode 100644 index 000000000000..b246e21c2e29 --- /dev/null +++ b/src/Shared/BenchmarkRunner/ParamsDisplayInfoColumn.cs @@ -0,0 +1,26 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using BenchmarkDotNet.Columns; +using BenchmarkDotNet.Reports; +using BenchmarkDotNet.Running; + +namespace BenchmarkDotNet.Attributes +{ + public class ParamsSummaryColumn : IColumn + { + public string Id => nameof(ParamsSummaryColumn); + public string ColumnName { get; } = "Params"; + public bool IsDefault(Summary summary, Benchmark benchmark) => false; + public string GetValue(Summary summary, Benchmark benchmark) => benchmark.Parameters.DisplayInfo; + public bool IsAvailable(Summary summary) => true; + public bool AlwaysShow => true; + public ColumnCategory Category => ColumnCategory.Params; + public int PriorityInCategory => 0; + public override string ToString() => ColumnName; + public bool IsNumeric => false; + public UnitType UnitType => UnitType.Dimensionless; + public string GetValue(Summary summary, Benchmark benchmark, ISummaryStyle style) => GetValue(summary, benchmark); + public string Legend => $"Summary of all parameter values"; + } +} \ No newline at end of file diff --git a/src/Shared/BenchmarkRunner/Program.cs b/src/Shared/BenchmarkRunner/Program.cs index 3297d5dae98e..a1db1a50e83a 100644 --- a/src/Shared/BenchmarkRunner/Program.cs +++ b/src/Shared/BenchmarkRunner/Program.cs @@ -2,15 +2,14 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Text; using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Running; using BenchmarkDotNet.Configs; -using BenchmarkDotNet.Jobs; -using BenchmarkDotNet.Toolchains.InProcess; +using BenchmarkDotNet.Running; namespace Microsoft.AspNetCore.BenchmarkDotNet.Runner { @@ -25,7 +24,7 @@ private static int Main(string[] args) { BeforeMain(args); - CheckValidate(ref args); + AssignConfiguration(ref args); var summaries = BenchmarkSwitcher.FromAssembly(typeof(Program).GetTypeInfo().Assembly) .Run(args, ManualConfig.CreateEmpty()); @@ -66,16 +65,35 @@ private static int Fail(object o, string message) return 1; } - private static void CheckValidate(ref string[] args) + private static void AssignConfiguration(ref string[] args) { var argsList = args.ToList(); if (argsList.Remove("--validate") || argsList.Remove("--validate-fast")) { + // Compat: support the old style of passing a config that is used by our build system. SuppressConsole(); - AspNetCoreBenchmarkAttribute.UseValidationConfig = true; + AspNetCoreBenchmarkAttribute.ConfigName = AspNetCoreBenchmarkAttribute.NamedConfiguration.Validation; + args = argsList.ToArray(); + return; + } + + var index = argsList.IndexOf("--config"); + if (index >= 0 && index < argsList.Count -1) + { + AspNetCoreBenchmarkAttribute.ConfigName = argsList[index + 1]; + argsList.RemoveAt(index + 1); + argsList.RemoveAt(index); + args = argsList.ToArray(); + return; } - args = argsList.ToArray(); + if (Debugger.IsAttached) + { + Console.WriteLine("Using the debug config since you are debugging. I hope that's OK!"); + Console.WriteLine("Specify a configuration with --config to override"); + AspNetCoreBenchmarkAttribute.ConfigName = AspNetCoreBenchmarkAttribute.NamedConfiguration.Debug; + return; + } } private static void SuppressConsole() diff --git a/src/Shared/CertificateGeneration/CertificateManager.cs b/src/Shared/CertificateGeneration/CertificateManager.cs index 4e2a0a99643f..952cf7c36d87 100644 --- a/src/Shared/CertificateGeneration/CertificateManager.cs +++ b/src/Shared/CertificateGeneration/CertificateManager.cs @@ -19,17 +19,12 @@ internal class CertificateManager public const string AspNetHttpsOid = "1.3.6.1.4.1.311.84.1.1"; public const string AspNetHttpsOidFriendlyName = "ASP.NET Core HTTPS development certificate"; - public const string AspNetIdentityOid = "1.3.6.1.4.1.311.84.1.2"; - public const string AspNetIdentityOidFriendlyName = "ASP.NET Core Identity Json Web Token signing development certificate"; - private const string ServerAuthenticationEnhancedKeyUsageOid = "1.3.6.1.5.5.7.3.1"; private const string ServerAuthenticationEnhancedKeyUsageOidFriendlyName = "Server Authentication"; private const string LocalhostHttpsDnsName = "localhost"; private const string LocalhostHttpsDistinguishedName = "CN=" + LocalhostHttpsDnsName; - private const string IdentityDistinguishedName = "CN=Microsoft.AspNetCore.Identity.Signing"; - public const int RSAMinimumKeySizeInBits = 2048; private static readonly TimeSpan MaxRegexTimeout = TimeSpan.FromMinutes(1); @@ -37,7 +32,7 @@ internal class CertificateManager private const string MacOSSystemKeyChain = "/Library/Keychains/System.keychain"; private static readonly string MacOSUserKeyChain = Environment.GetEnvironmentVariable("HOME") + "/Library/Keychains/login.keychain-db"; private const string MacOSFindCertificateCommandLine = "security"; -#if NETCOREAPP2_0 || NETCOREAPP2_1 +#if NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 private static readonly string MacOSFindCertificateCommandLineArgumentsFormat = "find-certificate -c {0} -a -Z -p " + MacOSSystemKeyChain; #endif private const string MacOSFindCertificateOutputRegex = "SHA-1 hash: ([0-9A-Z]+)"; @@ -46,7 +41,7 @@ internal class CertificateManager private const string MacOSDeleteCertificateCommandLine = "sudo"; private const string MacOSDeleteCertificateCommandLineArgumentsFormat = "security delete-certificate -Z {0} {1}"; private const string MacOSTrustCertificateCommandLine = "sudo"; -#if NETCOREAPP2_0 || NETCOREAPP2_1 +#if NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 private static readonly string MacOSTrustCertificateCommandLineArguments = "security add-trusted-cert -d -r trustRoot -k " + MacOSSystemKeyChain + " "; #endif private const int UserCancelledErrorCode = 1223; @@ -56,8 +51,10 @@ public IList ListCertificates( StoreName storeName, StoreLocation location, bool isValid, - bool requireExportable = true) + bool requireExportable = true, + DiagnosticInformation diagnostics = null) { + diagnostics?.Debug($"Listing '{purpose.ToString()}' certificates on '{location}\\{storeName}'."); var certificates = new List(); try { @@ -70,28 +67,37 @@ public IList ListCertificates( { case CertificatePurpose.All: matchingCertificates = matchingCertificates - .Where(c => HasOid(c, AspNetHttpsOid) || HasOid(c, AspNetIdentityOid)); + .Where(c => HasOid(c, AspNetHttpsOid)); break; case CertificatePurpose.HTTPS: matchingCertificates = matchingCertificates .Where(c => HasOid(c, AspNetHttpsOid)); break; - case CertificatePurpose.Signing: - matchingCertificates = matchingCertificates - .Where(c => HasOid(c, AspNetIdentityOid)); - break; default: break; } + + diagnostics?.Debug(diagnostics.DescribeCertificates(matchingCertificates)); if (isValid) { // Ensure the certificate hasn't expired, has a private key and its exportable // (for container/unix scenarios). + diagnostics?.Debug("Checking certificates for validity."); var now = DateTimeOffset.Now; - matchingCertificates = matchingCertificates + var validCertificates = matchingCertificates .Where(c => c.NotBefore <= now && now <= c.NotAfter && - (!requireExportable || !RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || IsExportable(c))); + (!requireExportable || !RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || IsExportable(c))) + .ToArray(); + + var invalidCertificates = matchingCertificates.Except(validCertificates); + + diagnostics?.Debug("Listing valid certificates"); + diagnostics?.Debug(diagnostics.DescribeCertificates(validCertificates)); + diagnostics?.Debug("Listing invalid certificates"); + diagnostics?.Debug(diagnostics.DescribeCertificates(invalidCertificates)); + + matchingCertificates = validCertificates; } // We need to enumerate the certificates early to prevent dispoisng issues. @@ -123,7 +129,7 @@ bool IsExportable(X509Certificate2 c) => cngPrivateKey.Key.ExportPolicy == CngExportPolicies.AllowExport)); #else // Only check for RSA CryptoServiceProvider and do not fail in XPlat tooling as - // System.Security.Cryptography.Cng is not pat of the shared framework and we don't + // System.Security.Cryptography.Cng is not part of the shared framework and we don't // want to bring the dependency in on CLI scenarios. This functionality will be used // on CLI scenarios as part of the first run experience, so checking the exportability // of the certificate is not important. @@ -133,7 +139,7 @@ bool IsExportable(X509Certificate2 c) => #endif } - private void DisposeCertificates(IEnumerable disposables) + private static void DisposeCertificates(IEnumerable disposables) { foreach (var disposable in disposables) { @@ -147,9 +153,9 @@ private void DisposeCertificates(IEnumerable disposables) } } -#if NETCOREAPP2_0 || NETCOREAPP2_1 +#if NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 - public X509Certificate2 CreateAspNetCoreHttpsDevelopmentCertificate(DateTimeOffset notBefore, DateTimeOffset notAfter, string subjectOverride) + public X509Certificate2 CreateAspNetCoreHttpsDevelopmentCertificate(DateTimeOffset notBefore, DateTimeOffset notAfter, string subjectOverride, DiagnosticInformation diagnostics = null) { var subject = new X500DistinguishedName(subjectOverride ?? LocalhostHttpsDistinguishedName); var extensions = new List(); @@ -192,46 +198,6 @@ public X509Certificate2 CreateAspNetCoreHttpsDevelopmentCertificate(DateTimeOffs return certificate; } - public X509Certificate2 CreateApplicationTokenSigningDevelopmentCertificate(DateTimeOffset notBefore, DateTimeOffset notAfter, string subjectOverride) - { - var subject = new X500DistinguishedName(subjectOverride ?? IdentityDistinguishedName); - var extensions = new List(); - - var keyUsage = new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature, critical: true); - var enhancedKeyUsage = new X509EnhancedKeyUsageExtension( - new OidCollection() { - new Oid( - ServerAuthenticationEnhancedKeyUsageOid, - ServerAuthenticationEnhancedKeyUsageOidFriendlyName) - }, - critical: true); - - var basicConstraints = new X509BasicConstraintsExtension( - certificateAuthority: false, - hasPathLengthConstraint: false, - pathLengthConstraint: 0, - critical: true); - - var aspNetIdentityExtension = new X509Extension( - new AsnEncodedData( - new Oid(AspNetIdentityOid, AspNetIdentityOidFriendlyName), - Encoding.ASCII.GetBytes(AspNetIdentityOidFriendlyName)), - critical: false); - - extensions.Add(basicConstraints); - extensions.Add(keyUsage); - extensions.Add(enhancedKeyUsage); - extensions.Add(aspNetIdentityExtension); - - var certificate = CreateSelfSignedCertificate(subject, extensions, notBefore, notAfter); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - certificate.FriendlyName = AspNetIdentityOidFriendlyName; - } - - return certificate; - } - public X509Certificate2 CreateSelfSignedCertificate( X500DistinguishedName subject, IEnumerable extensions, @@ -260,8 +226,9 @@ RSA CreateKeyMaterial(int minimumKeySize) } } - public X509Certificate2 SaveCertificateInStore(X509Certificate2 certificate, StoreName name, StoreLocation location) + public X509Certificate2 SaveCertificateInStore(X509Certificate2 certificate, StoreName name, StoreLocation location, DiagnosticInformation diagnostics = null) { + diagnostics?.Debug("Saving the certificate into the certificate store."); var imported = certificate; if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { @@ -287,33 +254,67 @@ public X509Certificate2 SaveCertificateInStore(X509Certificate2 certificate, Sto return imported; } - public void ExportCertificate(X509Certificate2 certificate, string path, bool includePrivateKey, string password) + public void ExportCertificate(X509Certificate2 certificate, string path, bool includePrivateKey, string password, DiagnosticInformation diagnostics = null) { - if (Path.GetDirectoryName(path) != "") + diagnostics?.Debug( + $"Exporting certificate to '{path}'", + includePrivateKey ? "The certificate will contain the private key" : "The certificate will not contain the private key"); + if (includePrivateKey && password == null) + { + diagnostics?.Debug("No password was provided for the certificate."); + } + + var targetDirectoryPath = Path.GetDirectoryName(path); + if (targetDirectoryPath != "") { - Directory.CreateDirectory(Path.GetDirectoryName(path)); + diagnostics?.Debug($"Ensuring that the directory for the target exported certificate path exists '{targetDirectoryPath}'"); + Directory.CreateDirectory(targetDirectoryPath); } + byte[] bytes; if (includePrivateKey) { - var bytes = certificate.Export(X509ContentType.Pkcs12, password); try { - File.WriteAllBytes(path, bytes); + diagnostics?.Debug($"Exporting the certificate including the private key."); + bytes = certificate.Export(X509ContentType.Pkcs12, password); } - finally + catch (Exception e) { - Array.Clear(bytes, 0, bytes.Length); + diagnostics?.Error($"Failed to export the certificate with the private key", e); + throw; } } else { - var bytes = certificate.Export(X509ContentType.Cert); + try + { + diagnostics?.Debug($"Exporting the certificate without the private key."); + bytes = certificate.Export(X509ContentType.Cert); + } + catch (Exception ex) + { + diagnostics?.Error($"Failed to export the certificate without the private key", ex); + throw; + } + } + try + { + diagnostics?.Debug($"Writing exported certificate to path '{path}'."); File.WriteAllBytes(path, bytes); } + catch (Exception ex) + { + diagnostics?.Error("Failed writing the certificate to the target path", ex); + throw; + } + finally + { + Array.Clear(bytes, 0, bytes.Length); + } } - public void TrustCertificate(X509Certificate2 certificate) + public void TrustCertificate(X509Certificate2 certificate, DiagnosticInformation diagnostics = null) { // Strip certificate of the private key if any. var publicCertificate = new X509Certificate2(certificate.Export(X509ContentType.Cert)); @@ -322,21 +323,24 @@ public void TrustCertificate(X509Certificate2 certificate) { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - TrustCertificateOnWindows(certificate, publicCertificate); + diagnostics?.Debug("Trusting the certificate on Windows."); + TrustCertificateOnWindows(certificate, publicCertificate, diagnostics); } else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { - TrustCertificateOnMac(publicCertificate); + diagnostics?.Debug("Trusting the certificate on MAC."); + TrustCertificateOnMac(publicCertificate, diagnostics); } } } - private void TrustCertificateOnMac(X509Certificate2 publicCertificate) + private void TrustCertificateOnMac(X509Certificate2 publicCertificate, DiagnosticInformation diagnostics) { var tmpFile = Path.GetTempFileName(); try { ExportCertificate(publicCertificate, tmpFile, includePrivateKey: false, password: null); + diagnostics?.Debug("Running the trust command on Mac OS"); using (var process = Process.Start(MacOSTrustCertificateCommandLine, MacOSTrustCertificateCommandLineArguments + tmpFile)) { process.WaitForExit(); @@ -362,19 +366,29 @@ private void TrustCertificateOnMac(X509Certificate2 publicCertificate) } } - private static void TrustCertificateOnWindows(X509Certificate2 certificate, X509Certificate2 publicCertificate) + private static void TrustCertificateOnWindows(X509Certificate2 certificate, X509Certificate2 publicCertificate, DiagnosticInformation diagnostics = null) { publicCertificate.FriendlyName = certificate.FriendlyName; using (var store = new X509Store(StoreName.Root, StoreLocation.CurrentUser)) { store.Open(OpenFlags.ReadWrite); + var existing = store.Certificates.Find(X509FindType.FindByThumbprint, publicCertificate.Thumbprint, validOnly: false); + if (existing.Count > 0) + { + diagnostics?.Debug("Certificate already trusted. Skipping trust step."); + DisposeCertificates(existing.OfType()); + return; + } + try { + diagnostics?.Debug("Adding certificate to the store."); store.Add(publicCertificate); } catch (CryptographicException exception) when (exception.HResult == UserCancelledErrorCode) { + diagnostics?.Debug("User cancelled the trust prompt."); throw new UserCancelledTrustException(); } store.Close(); @@ -437,6 +451,30 @@ public void CleanupCertificates(CertificatePurpose purpose, string subject) } } + public DiagnosticInformation CleanupHttpsCertificates2(string subject = LocalhostHttpsDistinguishedName) + { + return CleanupCertificates2(CertificatePurpose.HTTPS, subject); + } + + public DiagnosticInformation CleanupCertificates2(CertificatePurpose purpose, string subject) + { + var diagnostics = new DiagnosticInformation(); + // On OS X we don't have a good way to manage trusted certificates in the system keychain + // so we do everything by invoking the native toolchain. + // This has some limitations, like for example not being able to identify our custom OID extension. For that + // matter, when we are cleaning up certificates on the machine, we start by removing the trusted certificates. + // To do this, we list the certificates that we can identify on the current user personal store and we invoke + // the native toolchain to remove them from the sytem keychain. Once we have removed the trusted certificates, + // we remove the certificates from the local user store to finish up the cleanup. + var certificates = ListCertificates(purpose, StoreName.My, StoreLocation.CurrentUser, isValid: false, requireExportable: true, diagnostics); + foreach (var certificate in certificates) + { + RemoveCertificate(certificate, RemoveLocations.All, diagnostics); + } + + return diagnostics; + } + public void RemoveAllCertificates(CertificatePurpose purpose, StoreName storeName, StoreLocation storeLocation, string subject = null) { var certificates = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? @@ -454,32 +492,33 @@ public void RemoveAllCertificates(CertificatePurpose purpose, StoreName storeNam DisposeCertificates(certificates); } - private void RemoveCertificate(X509Certificate2 certificate, RemoveLocations locations) + private void RemoveCertificate(X509Certificate2 certificate, RemoveLocations locations, DiagnosticInformation diagnostics = null) { switch (locations) { case RemoveLocations.Undefined: throw new InvalidOperationException($"'{nameof(RemoveLocations.Undefined)}' is not a valid location."); case RemoveLocations.Local: - RemoveCertificateFromUserStore(certificate); + RemoveCertificateFromUserStore(certificate, diagnostics); break; case RemoveLocations.Trusted when !RuntimeInformation.IsOSPlatform(OSPlatform.Linux): - RemoveCertificateFromTrustedRoots(certificate); + RemoveCertificateFromTrustedRoots(certificate, diagnostics); break; case RemoveLocations.All: if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { - RemoveCertificateFromTrustedRoots(certificate); + RemoveCertificateFromTrustedRoots(certificate, diagnostics); } - RemoveCertificateFromUserStore(certificate); + RemoveCertificateFromUserStore(certificate, diagnostics); break; default: throw new InvalidOperationException("Invalid location."); } } - private static void RemoveCertificateFromUserStore(X509Certificate2 certificate) + private static void RemoveCertificateFromUserStore(X509Certificate2 certificate, DiagnosticInformation diagnostics) { + diagnostics?.Debug($"Trying to remove certificate with thumbprint '{certificate.Thumbprint}' from certificate store '{StoreLocation.CurrentUser}\\{StoreName.My}'."); using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser)) { store.Open(OpenFlags.ReadWrite); @@ -492,8 +531,9 @@ private static void RemoveCertificateFromUserStore(X509Certificate2 certificate) } } - private void RemoveCertificateFromTrustedRoots(X509Certificate2 certificate) + private void RemoveCertificateFromTrustedRoots(X509Certificate2 certificate, DiagnosticInformation diagnostics) { + diagnostics?.Debug($"Trying to remove certificate with thumbprint '{certificate.Thumbprint}' from certificate store '{StoreLocation.CurrentUser}\\{StoreName.Root}'."); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { using (var store = new X509Store(StoreName.Root, StoreLocation.CurrentUser)) @@ -501,9 +541,13 @@ private void RemoveCertificateFromTrustedRoots(X509Certificate2 certificate) store.Open(OpenFlags.ReadWrite); var matching = store.Certificates .OfType() - .Single(c => c.SerialNumber == certificate.SerialNumber); + .SingleOrDefault(c => c.SerialNumber == certificate.SerialNumber); + + if (matching != null) + { + store.Remove(matching); + } - store.Remove(matching); store.Close(); } } @@ -513,10 +557,12 @@ private void RemoveCertificateFromTrustedRoots(X509Certificate2 certificate) { try { + diagnostics?.Debug("Trying to remove the certificate trust rule."); RemoveCertificateTrustRule(certificate); } catch { + diagnostics?.Debug("Failed to remove the certificate trust rule."); // We don't care if we fail to remove the trust rule if // for some reason the certificate became untrusted. // The delete command will fail if the certificate is @@ -524,6 +570,10 @@ private void RemoveCertificateFromTrustedRoots(X509Certificate2 certificate) } RemoveCertificateFromKeyChain(MacOSSystemKeyChain, certificate); } + else + { + diagnostics?.Debug("The certificate was not trusted."); + } } } @@ -601,18 +651,6 @@ public EnsureCertificateResult EnsureAspNetCoreHttpsDevelopmentCertificate( return EnsureValidCertificateExists(notBefore, notAfter, CertificatePurpose.HTTPS, path, trust, includePrivateKey, password, subject); } - public EnsureCertificateResult EnsureAspNetCoreApplicationTokensDevelopmentCertificate( - DateTimeOffset notBefore, - DateTimeOffset notAfter, - string path = null, - bool trust = false, - bool includePrivateKey = false, - string password = null, - string subject = IdentityDistinguishedName) - { - return EnsureValidCertificateExists(notBefore, notAfter, CertificatePurpose.Signing, path, trust, includePrivateKey, password, subject); - } - public EnsureCertificateResult EnsureValidCertificateExists( DateTimeOffset notBefore, DateTimeOffset notAfter, @@ -652,9 +690,6 @@ public EnsureCertificateResult EnsureValidCertificateExists( case CertificatePurpose.HTTPS: certificate = CreateAspNetCoreHttpsDevelopmentCertificate(notBefore, notAfter, subjectOverride); break; - case CertificatePurpose.Signing: - certificate = CreateApplicationTokenSigningDevelopmentCertificate(notBefore, notAfter, subjectOverride); - break; default: throw new InvalidOperationException("The certificate must have a purpose."); } @@ -704,6 +739,143 @@ public EnsureCertificateResult EnsureValidCertificateExists( return result; } + // This is just to avoid breaking changes across repos. + // Will be renamed back to EnsureAspNetCoreHttpsDevelopmentCertificate once updates are made elsewhere. + public DetailedEnsureCertificateResult EnsureAspNetCoreHttpsDevelopmentCertificate2( + DateTimeOffset notBefore, + DateTimeOffset notAfter, + string path = null, + bool trust = false, + bool includePrivateKey = false, + string password = null, + string subject = LocalhostHttpsDistinguishedName) + { + return EnsureValidCertificateExists2(notBefore, notAfter, CertificatePurpose.HTTPS, path, trust, includePrivateKey, password, subject); + } + + public DetailedEnsureCertificateResult EnsureValidCertificateExists2( + DateTimeOffset notBefore, + DateTimeOffset notAfter, + CertificatePurpose purpose, + string path, + bool trust, + bool includePrivateKey, + string password, + string subject) + { + if (purpose == CertificatePurpose.All) + { + throw new ArgumentException("The certificate must have a specific purpose."); + } + + var result = new DetailedEnsureCertificateResult(); + + var certificates = ListCertificates(purpose, StoreName.My, StoreLocation.CurrentUser, isValid: true, requireExportable: true, result.Diagnostics).Concat( + ListCertificates(purpose, StoreName.My, StoreLocation.LocalMachine, isValid: true, requireExportable: true, result.Diagnostics)); + + var filteredCertificates = subject == null ? certificates : certificates.Where(c => c.Subject == subject); + if (subject != null) + { + var excludedCertificates = certificates.Except(filteredCertificates); + + result.Diagnostics.Debug($"Filtering found certificates to those with a subject equal to '{subject}'"); + result.Diagnostics.Debug(result.Diagnostics.DescribeCertificates(filteredCertificates)); + result.Diagnostics.Debug($"Listing certificates excluded from consideration."); + result.Diagnostics.Debug(result.Diagnostics.DescribeCertificates(excludedCertificates)); + } + else + { + result.Diagnostics.Debug("Skipped filtering certificates by subject."); + } + + certificates = filteredCertificates; + + result.ResultCode = EnsureCertificateResult.Succeeded; + + X509Certificate2 certificate = null; + if (certificates.Count() > 0) + { + result.Diagnostics.Debug("Found valid certificates present on the machine."); + result.Diagnostics.Debug(result.Diagnostics.DescribeCertificates(certificates)); + certificate = certificates.First(); + result.Diagnostics.Debug("Selected certificate"); + result.Diagnostics.Debug(result.Diagnostics.DescribeCertificates(certificate)); + result.ResultCode = EnsureCertificateResult.ValidCertificatePresent; + } + else + { + result.Diagnostics.Debug("No valid certificates present on this machine. Trying to create one."); + try + { + switch (purpose) + { + case CertificatePurpose.All: + throw new InvalidOperationException("The certificate must have a specific purpose."); + case CertificatePurpose.HTTPS: + certificate = CreateAspNetCoreHttpsDevelopmentCertificate(notBefore, notAfter, subject, result.Diagnostics); + break; + default: + throw new InvalidOperationException("The certificate must have a purpose."); + } + } + catch (Exception e) + { + result.Diagnostics.Error("Error creating the certificate.", e); + result.ResultCode = EnsureCertificateResult.ErrorCreatingTheCertificate; + return result; + } + + try + { + certificate = SaveCertificateInStore(certificate, StoreName.My, StoreLocation.CurrentUser, result.Diagnostics); + } + catch (Exception e) + { + result.Diagnostics.Error($"Error saving the certificate in the certificate store '{StoreLocation.CurrentUser}\\{StoreName.My}'.", e); + result.ResultCode = EnsureCertificateResult.ErrorSavingTheCertificateIntoTheCurrentUserPersonalStore; + return result; + } + } + if (path != null) + { + result.Diagnostics.Debug("Trying to export the certificate."); + result.Diagnostics.Debug(result.Diagnostics.DescribeCertificates(certificate)); + try + { + ExportCertificate(certificate, path, includePrivateKey, password, result.Diagnostics); + } + catch (Exception e) + { + result.Diagnostics.Error("An error ocurred exporting the certificate.", e); + result.ResultCode = EnsureCertificateResult.ErrorExportingTheCertificate; + return result; + } + } + + if ((RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) && trust) + { + try + { + result.Diagnostics.Debug("Trying to export the certificate."); + TrustCertificate(certificate, result.Diagnostics); + } + catch (UserCancelledTrustException) + { + result.Diagnostics.Error("The user cancelled trusting the certificate.", null); + result.ResultCode = EnsureCertificateResult.UserCancelledTrustStep; + return result; + } + catch (Exception e) + { + result.Diagnostics.Error("There was an error trusting the certificate.", e); + result.ResultCode = EnsureCertificateResult.FailedToTrustTheCertificate; + return result; + } + } + + return result; + } + private class UserCancelledTrustException : Exception { } @@ -715,6 +887,65 @@ private enum RemoveLocations Trusted, All } + + internal class DetailedEnsureCertificateResult + { + public EnsureCertificateResult ResultCode { get; set; } + public DiagnosticInformation Diagnostics { get; set; } = new DiagnosticInformation(); + } #endif + + internal class DiagnosticInformation + { + public IList Messages { get; } = new List(); + + public IList Exceptions { get; } = new List(); + + internal void Debug(params string[] messages) + { + foreach (var message in messages) + { + Messages.Add(message); + } + } + + internal string[] DescribeCertificates(params X509Certificate2[] certificates) + { + return DescribeCertificates(certificates.AsEnumerable()); + } + + internal string[] DescribeCertificates(IEnumerable certificates) + { + var result = new List(); + result.Add($"'{certificates.Count()}' found matching the criteria."); + result.Add($"SUBJECT - THUMBPRINT - NOT BEFORE - EXPIRES - HAS PRIVATE KEY"); + foreach (var certificate in certificates) + { + result.Add(DescribeCertificate(certificate)); + } + + return result.ToArray(); + } + + private static string DescribeCertificate(X509Certificate2 certificate) => + $"{certificate.Subject} - {certificate.Thumbprint} - {certificate.NotBefore} - {certificate.NotAfter} - {certificate.HasPrivateKey}"; + + internal void Error(string preamble, Exception e) + { + Messages.Add(preamble); + if (Exceptions.Count > 0 && Exceptions[Exceptions.Count - 1] == e) + { + return; + } + + var ex = e; + while (ex != null) + { + Messages.Add("Exception message: " + ex.Message); + ex = ex.InnerException; + } + + } + } } } \ No newline at end of file diff --git a/src/Shared/CertificateGeneration/CertificatePurpose.cs b/src/Shared/CertificateGeneration/CertificatePurpose.cs index 1ad1a6d79b65..7b3231f80d53 100644 --- a/src/Shared/CertificateGeneration/CertificatePurpose.cs +++ b/src/Shared/CertificateGeneration/CertificatePurpose.cs @@ -6,7 +6,6 @@ namespace Microsoft.AspNetCore.Certificates.Generation internal enum CertificatePurpose { All, - HTTPS, - Signing + HTTPS } } \ No newline at end of file diff --git a/src/Shared/CertificateGeneration/EnsureCertificateResult.cs b/src/Shared/CertificateGeneration/EnsureCertificateResult.cs index d3c86ce05d08..84c495249dbb 100644 --- a/src/Shared/CertificateGeneration/EnsureCertificateResult.cs +++ b/src/Shared/CertificateGeneration/EnsureCertificateResult.cs @@ -1,7 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -#if NETCOREAPP2_0 || NETCOREAPP2_1 +#if NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 namespace Microsoft.AspNetCore.Certificates.Generation { diff --git a/src/Shared/NonCapturingTimer/NonCapturingTimer.cs b/src/Shared/NonCapturingTimer/NonCapturingTimer.cs new file mode 100644 index 000000000000..6f54b2db47c0 --- /dev/null +++ b/src/Shared/NonCapturingTimer/NonCapturingTimer.cs @@ -0,0 +1,43 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Threading; + +namespace Microsoft.Extensions.Internal +{ + // A convenience API for interacting with System.Threading.Timer in a way + // that doesn't capture the ExecutionContext. We should be using this (or equivalent) + // everywhere we use timers to avoid rooting any values stored in asynclocals. + internal static class NonCapturingTimer + { + public static Timer Create(TimerCallback callback, object state, TimeSpan dueTime, TimeSpan period) + { + if (callback == null) + { + throw new ArgumentNullException(nameof(callback)); + } + + // Don't capture the current ExecutionContext and its AsyncLocals onto the timer + bool restoreFlow = false; + try + { + if (!ExecutionContext.IsFlowSuppressed()) + { + ExecutionContext.SuppressFlow(); + restoreFlow = true; + } + + return new Timer(callback, state, dueTime, period); + } + finally + { + // Restore the current ExecutionContext + if (restoreFlow) + { + ExecutionContext.RestoreFlow(); + } + } + } + } +} diff --git a/src/Shared/Process/ProcessHelper.cs b/src/Shared/Process/ProcessHelper.cs index cf42a7e3a77e..c6cbd1f9700b 100644 --- a/src/Shared/Process/ProcessHelper.cs +++ b/src/Shared/Process/ProcessHelper.cs @@ -14,44 +14,40 @@ internal static class ProcessExtensions private static readonly bool _isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); private static readonly TimeSpan _defaultTimeout = TimeSpan.FromSeconds(30); - public static void KillTree(this Process process) - { - process.KillTree(_defaultTimeout); - } + public static void KillTree(this Process process) => process.KillTree(_defaultTimeout); public static void KillTree(this Process process, TimeSpan timeout) { - string stdout; + var pid = process.Id; if (_isWindows) { RunProcessAndWaitForExit( "taskkill", - $"/T /F /PID {process.Id}", + $"/T /F /PID {pid}", timeout, - out stdout); + out var _); } else { var children = new HashSet(); - GetAllChildIdsUnix(process.Id, children, timeout); + GetAllChildIdsUnix(pid, children, timeout); foreach (var childId in children) { KillProcessUnix(childId, timeout); } - KillProcessUnix(process.Id, timeout); + KillProcessUnix(pid, timeout); } } private static void GetAllChildIdsUnix(int parentId, ISet children, TimeSpan timeout) { - string stdout; - var exitCode = RunProcessAndWaitForExit( + RunProcessAndWaitForExit( "pgrep", $"-P {parentId}", timeout, - out stdout); + out var stdout); - if (exitCode == 0 && !string.IsNullOrEmpty(stdout)) + if (!string.IsNullOrEmpty(stdout)) { using (var reader = new StringReader(stdout)) { @@ -63,8 +59,7 @@ private static void GetAllChildIdsUnix(int parentId, ISet children, TimeSpa return; } - int id; - if (int.TryParse(text, out id)) + if (int.TryParse(text, out var id)) { children.Add(id); // Recursively get the children @@ -77,22 +72,22 @@ private static void GetAllChildIdsUnix(int parentId, ISet children, TimeSpa private static void KillProcessUnix(int processId, TimeSpan timeout) { - string stdout; RunProcessAndWaitForExit( "kill", $"-TERM {processId}", timeout, - out stdout); + out var stdout); } - private static int RunProcessAndWaitForExit(string fileName, string arguments, TimeSpan timeout, out string stdout) + private static void RunProcessAndWaitForExit(string fileName, string arguments, TimeSpan timeout, out string stdout) { var startInfo = new ProcessStartInfo { FileName = fileName, Arguments = arguments, RedirectStandardOutput = true, - UseShellExecute = false + RedirectStandardError = true, + UseShellExecute = false, }; var process = Process.Start(startInfo); @@ -106,8 +101,6 @@ private static int RunProcessAndWaitForExit(string fileName, string arguments, T { process.Kill(); } - - return process.ExitCode; } } } diff --git a/src/Shared/PropertyHelper/PropertyHelper.cs b/src/Shared/PropertyHelper/PropertyHelper.cs index 27ba5661a423..f6aad151e529 100644 --- a/src/Shared/PropertyHelper/PropertyHelper.cs +++ b/src/Shared/PropertyHelper/PropertyHelper.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.Linq; using System.Reflection; +using System.Runtime.CompilerServices; namespace Microsoft.Extensions.Internal { @@ -37,6 +38,12 @@ internal class PropertyHelper private static readonly ConcurrentDictionary VisiblePropertiesCache = new ConcurrentDictionary(); + // We need to be able to check if a type is a 'ref struct' - but we need to be able to compile + // for platforms where the attribute is not defined, like net46. So we can fetch the attribute + // by late binding. If the attribute isn't defined, then we assume we won't encounter any + // 'ref struct' types. + private static readonly Type IsByRefLikeAttribute = Type.GetType("System.Runtime.CompilerServices.IsByRefLikeAttribute", throwOnError: false); + private Action _valueSetter; private Func _valueGetter; @@ -511,16 +518,34 @@ protected static PropertyHelper[] GetProperties( return helpers; } - // Indexed properties are not useful (or valid) for grabbing properties off an object. + private static bool IsInterestingProperty(PropertyInfo property) { // For improving application startup time, do not use GetIndexParameters() api early in this check as it // creates a copy of parameter array and also we would like to check for the presence of a get method // and short circuit asap. - return property.GetMethod != null && + return + property.GetMethod != null && property.GetMethod.IsPublic && !property.GetMethod.IsStatic && + + // PropertyHelper can't work with ref structs. + !IsRefStructProperty(property) && + + // Indexed properties are not useful (or valid) for grabbing properties off an object. property.GetMethod.GetParameters().Length == 0; } + + // PropertyHelper can't really interact with ref-struct properties since they can't be + // boxed and can't be used as generic types. We just ignore them. + // + // see: https://github.com/aspnet/Mvc/issues/8545 + private static bool IsRefStructProperty(PropertyInfo property) + { + return + IsByRefLikeAttribute != null && + property.PropertyType.IsValueType && + property.PropertyType.IsDefined(IsByRefLikeAttribute); + } } } diff --git a/src/Shared/test/Shared.Tests/CertificateManagerTests.cs b/src/Shared/test/Shared.Tests/CertificateManagerTests.cs index 613f5c966f15..cd314383c90d 100644 --- a/src/Shared/test/Shared.Tests/CertificateManagerTests.cs +++ b/src/Shared/test/Shared.Tests/CertificateManagerTests.cs @@ -1,7 +1,7 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -#if NETCOREAPP2_0 || NETCOREAPP2_1 +#if NETCOREAPP2_2 using System; using System.IO; @@ -26,7 +26,7 @@ public CertificateManagerTests(ITestOutputHelper output) public ITestOutputHelper Output { get; } - [Fact] + [Fact(Skip = "True")] public void EnsureCreateHttpsCertificate_CreatesACertificate_WhenThereAreNoHttpsCertificates() { try @@ -109,6 +109,92 @@ public void EnsureCreateHttpsCertificate_CreatesACertificate_WhenThereAreNoHttps } } + [Fact] + public void EnsureCreateHttpsCertificate2_CreatesACertificate_WhenThereAreNoHttpsCertificates() + { + try + { + // Arrange + const string CertificateName = nameof(EnsureCreateHttpsCertificate_CreatesACertificate_WhenThereAreNoHttpsCertificates) + ".cer"; + var manager = new CertificateManager(); + manager.RemoveAllCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, TestCertificateSubject); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + manager.RemoveAllCertificates(CertificatePurpose.HTTPS, StoreName.Root, StoreLocation.CurrentUser, TestCertificateSubject); + } + + // Act + DateTimeOffset now = DateTimeOffset.UtcNow; + now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset); + var result = manager.EnsureAspNetCoreHttpsDevelopmentCertificate2(now, now.AddYears(1), CertificateName, trust: false, subject: TestCertificateSubject); + + // Assert + Assert.Equal(EnsureCertificateResult.Succeeded, result.ResultCode); + Assert.NotNull(result.Diagnostics); + Assert.NotEmpty(result.Diagnostics.Messages); + Assert.Empty(result.Diagnostics.Exceptions); + + Assert.True(File.Exists(CertificateName)); + + var exportedCertificate = new X509Certificate2(File.ReadAllBytes(CertificateName)); + Assert.NotNull(exportedCertificate); + Assert.False(exportedCertificate.HasPrivateKey); + + var httpsCertificates = manager.ListCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, isValid: false); + var httpsCertificate = Assert.Single(httpsCertificates, c => c.Subject == TestCertificateSubject); + Assert.True(httpsCertificate.HasPrivateKey); + Assert.Equal(TestCertificateSubject, httpsCertificate.Subject); + Assert.Equal(TestCertificateSubject, httpsCertificate.Issuer); + Assert.Equal("sha256RSA", httpsCertificate.SignatureAlgorithm.FriendlyName); + Assert.Equal("1.2.840.113549.1.1.11", httpsCertificate.SignatureAlgorithm.Value); + + Assert.Equal(now.LocalDateTime, httpsCertificate.NotBefore); + Assert.Equal(now.AddYears(1).LocalDateTime, httpsCertificate.NotAfter); + Assert.Contains( + httpsCertificate.Extensions.OfType(), + e => e is X509BasicConstraintsExtension basicConstraints && + basicConstraints.Critical == true && + basicConstraints.CertificateAuthority == false && + basicConstraints.HasPathLengthConstraint == false && + basicConstraints.PathLengthConstraint == 0); + + Assert.Contains( + httpsCertificate.Extensions.OfType(), + e => e is X509KeyUsageExtension keyUsage && + keyUsage.Critical == true && + keyUsage.KeyUsages == X509KeyUsageFlags.KeyEncipherment); + + Assert.Contains( + httpsCertificate.Extensions.OfType(), + e => e is X509EnhancedKeyUsageExtension enhancedKeyUsage && + enhancedKeyUsage.Critical == true && + enhancedKeyUsage.EnhancedKeyUsages.OfType().Single() is Oid keyUsage && + keyUsage.Value == "1.3.6.1.5.5.7.3.1"); + + // Subject alternative name + Assert.Contains( + httpsCertificate.Extensions.OfType(), + e => e.Critical == true && + e.Oid.Value == "2.5.29.17"); + + // ASP.NET HTTPS Development certificate extension + Assert.Contains( + httpsCertificate.Extensions.OfType(), + e => e.Critical == false && + e.Oid.Value == "1.3.6.1.4.1.311.84.1.1" && + Encoding.ASCII.GetString(e.RawData) == "ASP.NET Core HTTPS development certificate"); + + Assert.Equal(httpsCertificate.GetCertHashString(), exportedCertificate.GetCertHashString()); + + } + catch (Exception e) + { + Output.WriteLine(e.Message); + ListCertificates(Output); + throw; + } + } + private void ListCertificates(ITestOutputHelper output) { using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser)) @@ -125,7 +211,7 @@ private void ListCertificates(ITestOutputHelper output) } } - [Fact] + [Fact(Skip = "true")] public void EnsureCreateHttpsCertificate_DoesNotCreateACertificate_WhenThereIsAnExistingHttpsCertificates() { // Arrange @@ -196,108 +282,6 @@ public void EnsureAspNetCoreHttpsDevelopmentCertificate_CanRemoveCertificates() Assert.Empty(manager.ListCertificates(CertificatePurpose.HTTPS, StoreName.Root, StoreLocation.CurrentUser, isValid: false).Where(c => c.Subject == TestCertificateSubject)); } } - - [Fact] - public void EnsureCreateIdentityTokenSigningCertificate_CreatesACertificate_WhenThereAreNoHttpsCertificates() - { - // Arrange - const string CertificateName = nameof(EnsureCreateIdentityTokenSigningCertificate_CreatesACertificate_WhenThereAreNoHttpsCertificates) + ".cer"; - var manager = new CertificateManager(); - - manager.RemoveAllCertificates(CertificatePurpose.Signing, StoreName.My, StoreLocation.CurrentUser, TestCertificateSubject); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - manager.RemoveAllCertificates(CertificatePurpose.Signing, StoreName.Root, StoreLocation.CurrentUser, TestCertificateSubject); - } - - // Act - DateTimeOffset now = DateTimeOffset.UtcNow; - now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset); - var result = manager.EnsureAspNetCoreApplicationTokensDevelopmentCertificate(now, now.AddYears(1), CertificateName, trust: false, subject: TestCertificateSubject); - - // Assert - Assert.Equal(EnsureCertificateResult.Succeeded, result); - Assert.True(File.Exists(CertificateName)); - - var exportedCertificate = new X509Certificate2(File.ReadAllBytes(CertificateName)); - Assert.NotNull(exportedCertificate); - Assert.False(exportedCertificate.HasPrivateKey); - - var identityCertificates = manager.ListCertificates(CertificatePurpose.Signing, StoreName.My, StoreLocation.CurrentUser, isValid: false); - var identityCertificate = Assert.Single(identityCertificates, i => i.Subject == TestCertificateSubject); - Assert.True(identityCertificate.HasPrivateKey); - Assert.Equal(TestCertificateSubject, identityCertificate.Subject); - Assert.Equal(TestCertificateSubject, identityCertificate.Issuer); - Assert.Equal("sha256RSA", identityCertificate.SignatureAlgorithm.FriendlyName); - Assert.Equal("1.2.840.113549.1.1.11", identityCertificate.SignatureAlgorithm.Value); - - Assert.Equal(now.LocalDateTime, identityCertificate.NotBefore); - Assert.Equal(now.AddYears(1).LocalDateTime, identityCertificate.NotAfter); - Assert.Contains( - identityCertificate.Extensions.OfType(), - e => e is X509BasicConstraintsExtension basicConstraints && - basicConstraints.Critical == true && - basicConstraints.CertificateAuthority == false && - basicConstraints.HasPathLengthConstraint == false && - basicConstraints.PathLengthConstraint == 0); - - Assert.Contains( - identityCertificate.Extensions.OfType(), - e => e is X509KeyUsageExtension keyUsage && - keyUsage.Critical == true && - keyUsage.KeyUsages == X509KeyUsageFlags.DigitalSignature); - - Assert.Contains( - identityCertificate.Extensions.OfType(), - e => e is X509EnhancedKeyUsageExtension enhancedKeyUsage && - enhancedKeyUsage.Critical == true && - enhancedKeyUsage.EnhancedKeyUsages.OfType().Single() is Oid keyUsage && - keyUsage.Value == "1.3.6.1.5.5.7.3.1"); - - // ASP.NET Core Identity Json Web Token signing development certificate - Assert.Contains( - identityCertificate.Extensions.OfType(), - e => e.Critical == false && - e.Oid.Value == "1.3.6.1.4.1.311.84.1.2" && - Encoding.ASCII.GetString(e.RawData) == "ASP.NET Core Identity Json Web Token signing development certificate"); - - Assert.Equal(identityCertificate.GetCertHashString(), exportedCertificate.GetCertHashString()); - } - - [Fact] - public void EnsureCreateIdentityTokenSigningCertificate_DoesNotCreateACertificate_WhenThereIsAnExistingHttpsCertificates() - { - // Arrange - const string CertificateName = nameof(EnsureCreateIdentityTokenSigningCertificate_DoesNotCreateACertificate_WhenThereIsAnExistingHttpsCertificates) + ".pfx"; - var certificatePassword = Guid.NewGuid().ToString(); - - var manager = new CertificateManager(); - - manager.RemoveAllCertificates(CertificatePurpose.Signing, StoreName.My, StoreLocation.CurrentUser, TestCertificateSubject); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - manager.RemoveAllCertificates(CertificatePurpose.Signing, StoreName.Root, StoreLocation.CurrentUser, TestCertificateSubject); - } - - DateTimeOffset now = DateTimeOffset.UtcNow; - now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset); - manager.EnsureAspNetCoreApplicationTokensDevelopmentCertificate(now, now.AddYears(1), path: null, trust: false, subject: TestCertificateSubject); - - var identityTokenSigningCertificates = manager.ListCertificates(CertificatePurpose.Signing, StoreName.My, StoreLocation.CurrentUser, isValid: false).Single(c => c.Subject == TestCertificateSubject); - - // Act - var result = manager.EnsureAspNetCoreApplicationTokensDevelopmentCertificate(now, now.AddYears(1), CertificateName, trust: false, includePrivateKey: true, password: certificatePassword, subject: TestCertificateSubject); - - // Assert - Assert.Equal(EnsureCertificateResult.ValidCertificatePresent, result); - Assert.True(File.Exists(CertificateName)); - - var exportedCertificate = new X509Certificate2(File.ReadAllBytes(CertificateName), certificatePassword); - Assert.NotNull(exportedCertificate); - Assert.True(exportedCertificate.HasPrivateKey); - - Assert.Equal(identityTokenSigningCertificates.GetCertHashString(), exportedCertificate.GetCertHashString()); - } } } diff --git a/src/Shared/test/Shared.Tests/DotNetMuxerTests.cs b/src/Shared/test/Shared.Tests/DotNetMuxerTests.cs index ba1dd0651104..92e06a8f7032 100644 --- a/src/Shared/test/Shared.Tests/DotNetMuxerTests.cs +++ b/src/Shared/test/Shared.Tests/DotNetMuxerTests.cs @@ -1,7 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -#if NETCOREAPP2_0 || NETCOREAPP2_1 +#if NETCOREAPP2_2 using System.IO; using System.Runtime.InteropServices; using Xunit; diff --git a/src/Shared/test/Shared.Tests/NonCapturingTimerTest.cs b/src/Shared/test/Shared.Tests/NonCapturingTimerTest.cs new file mode 100644 index 000000000000..ef21ce5f3b01 --- /dev/null +++ b/src/Shared/test/Shared.Tests/NonCapturingTimerTest.cs @@ -0,0 +1,40 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace Microsoft.Extensions.Internal +{ + public class NonCapturingTimerTest + { + [Fact] + public async Task NonCapturingTimer_DoesntCaptureExecutionContext() + { + // Arrange + var message = new AsyncLocal(); + message.Value = "Hey, this is a value stored in the execuion context"; + + var tcs = new TaskCompletionSource(); + + // Act + var timer = NonCapturingTimer.Create((_) => + { + // Observe the value based on the current execution context + tcs.SetResult(message.Value); + }, state: null, dueTime: TimeSpan.FromMilliseconds(1), Timeout.InfiniteTimeSpan); + + // Assert + var messageFromTimer = await tcs.Task; + timer.Dispose(); + + // ExecutionContext didn't flow to timer callback + Assert.Null(messageFromTimer); + + // ExecutionContext was restored + Assert.NotNull(await Task.Run(() => message.Value)); + } + } +} diff --git a/src/Shared/test/Shared.Tests/PropertyHelperTest.cs b/src/Shared/test/Shared.Tests/PropertyHelperTest.cs index 19cf08b3705a..1c43dc880b3f 100644 --- a/src/Shared/test/Shared.Tests/PropertyHelperTest.cs +++ b/src/Shared/test/Shared.Tests/PropertyHelperTest.cs @@ -153,6 +153,22 @@ public void PropertyHelper_DoesNotFindStaticProperties() Assert.Equal("Prop5", helper.Name); } +#if NETSTANDARD || NETCOREAPP + [Fact] + public void PropertyHelper_RefStructProperties() + { + // Arrange + var obj = new RefStructProperties(); + + // Act + Assert + var helper = Assert.Single(PropertyHelper.GetProperties(obj.GetType().GetTypeInfo())); + Assert.Equal("Prop5", helper.Name); + } +#elif NET46 || NET461 +#else +#error Unknown TFM - update the set of TFMs where we test for ref structs +#endif + [Fact] public void PropertyHelper_DoesNotFindSetOnlyProperties() { @@ -718,6 +734,22 @@ private class Static public int Prop5 { get; set; } } +#if NETSTANDARD || NETCOREAPP + private class RefStructProperties + { + public Span Span => throw new NotImplementedException(); + public MyRefStruct UserDefined => throw new NotImplementedException(); + + public int Prop5 { get; set; } + } + + private readonly ref struct MyRefStruct + { + } +#elif NET46 || NET461 +#else +#error Unknown TFM - update the set of TFMs where we test for ref structs +#endif private struct MyProperties { public int IntProp { get; set; } diff --git a/src/Shared/test/testassets/ThrowingLibrary/ThrowingLibrary.csproj b/src/Shared/test/testassets/ThrowingLibrary/ThrowingLibrary.csproj index d77d392873d7..2b2900911a8a 100644 --- a/src/Shared/test/testassets/ThrowingLibrary/ThrowingLibrary.csproj +++ b/src/Shared/test/testassets/ThrowingLibrary/ThrowingLibrary.csproj @@ -3,6 +3,7 @@ netstandard2.0 portable + false diff --git a/src/Testing/src/CultureReplacer.cs b/src/Testing/src/CultureReplacer.cs new file mode 100644 index 000000000000..51e35e83544a --- /dev/null +++ b/src/Testing/src/CultureReplacer.cs @@ -0,0 +1,79 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Globalization; +using System.Threading; +using Xunit; + +namespace Microsoft.AspNetCore.Testing +{ + public class CultureReplacer : IDisposable + { + private const string _defaultCultureName = "en-GB"; + private const string _defaultUICultureName = "en-US"; + private static readonly CultureInfo _defaultCulture = new CultureInfo(_defaultCultureName); + private readonly CultureInfo _originalCulture; + private readonly CultureInfo _originalUICulture; + private readonly long _threadId; + + // Culture => Formatting of dates/times/money/etc, defaults to en-GB because en-US is the same as InvariantCulture + // We want to be able to find issues where the InvariantCulture is used, but a specific culture should be. + // + // UICulture => Language + public CultureReplacer(string culture = _defaultCultureName, string uiCulture = _defaultUICultureName) + : this(new CultureInfo(culture), new CultureInfo(uiCulture)) + { + } + + public CultureReplacer(CultureInfo culture, CultureInfo uiCulture) + { + _originalCulture = CultureInfo.CurrentCulture; + _originalUICulture = CultureInfo.CurrentUICulture; + _threadId = Thread.CurrentThread.ManagedThreadId; + CultureInfo.CurrentCulture = culture; + CultureInfo.CurrentUICulture = uiCulture; + } + + /// + /// The name of the culture that is used as the default value for CultureInfo.DefaultThreadCurrentCulture when CultureReplacer is used. + /// + public static string DefaultCultureName + { + get { return _defaultCultureName; } + } + + /// + /// The name of the culture that is used as the default value for [Thread.CurrentThread(NET45)/CultureInfo(K10)].CurrentUICulture when CultureReplacer is used. + /// + public static string DefaultUICultureName + { + get { return _defaultUICultureName; } + } + + /// + /// The culture that is used as the default value for [Thread.CurrentThread(NET45)/CultureInfo(K10)].CurrentCulture when CultureReplacer is used. + /// + public static CultureInfo DefaultCulture + { + get { return _defaultCulture; } + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (disposing) + { + Assert.True(Thread.CurrentThread.ManagedThreadId == _threadId, + "The current thread is not the same as the thread invoking the constructor. This should never happen."); + CultureInfo.CurrentCulture = _originalCulture; + CultureInfo.CurrentUICulture = _originalUICulture; + } + } + } +} diff --git a/src/Testing/src/ExceptionAssertions.cs b/src/Testing/src/ExceptionAssertions.cs new file mode 100644 index 000000000000..244cad5a37db --- /dev/null +++ b/src/Testing/src/ExceptionAssertions.cs @@ -0,0 +1,271 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Reflection; +using System.Threading.Tasks; +using Xunit; + +namespace Microsoft.AspNetCore.Testing +{ + // TODO: eventually want: public partial class Assert : Xunit.Assert + public static class ExceptionAssert + { + /// + /// Verifies that an exception of the given type (or optionally a derived type) is thrown. + /// + /// The type of the exception expected to be thrown + /// A delegate to the code to be tested + /// The exception that was thrown, when successful + public static TException Throws(Action testCode) + where TException : Exception + { + return VerifyException(RecordException(testCode)); + } + + /// + /// Verifies that an exception of the given type is thrown. + /// Also verifies that the exception message matches. + /// + /// The type of the exception expected to be thrown + /// A delegate to the code to be tested + /// The exception message to verify + /// The exception that was thrown, when successful + public static TException Throws(Action testCode, string exceptionMessage) + where TException : Exception + { + var ex = Throws(testCode); + VerifyExceptionMessage(ex, exceptionMessage); + return ex; + } + + /// + /// Verifies that an exception of the given type is thrown. + /// Also verifies that the exception message matches. + /// + /// The type of the exception expected to be thrown + /// A delegate to the code to be tested + /// The exception message to verify + /// The exception that was thrown, when successful + public static async Task ThrowsAsync(Func testCode, string exceptionMessage) + where TException : Exception + { + // The 'testCode' Task might execute asynchronously in a different thread making it hard to enforce the thread culture. + // The correct way to verify exception messages in such a scenario would be to run the task synchronously inside of a + // culture enforced block. + var ex = await Assert.ThrowsAsync(testCode); + VerifyExceptionMessage(ex, exceptionMessage); + return ex; + } + + /// + /// Verifies that an exception of the given type is thrown. + /// Also verified that the exception message matches. + /// + /// The type of the exception expected to be thrown + /// A delegate to the code to be tested + /// The exception message to verify + /// The exception that was thrown, when successful + public static TException Throws(Func testCode, string exceptionMessage) + where TException : Exception + { + return Throws(() => { testCode(); }, exceptionMessage); + } + + /// + /// Verifies that the code throws an . + /// + /// A delegate to the code to be tested + /// The name of the parameter that should throw the exception + /// The exception message to verify + /// The exception that was thrown, when successful + public static ArgumentException ThrowsArgument(Action testCode, string paramName, string exceptionMessage) + { + return ThrowsArgumentInternal(testCode, paramName, exceptionMessage); + } + + private static TException ThrowsArgumentInternal( + Action testCode, + string paramName, + string exceptionMessage) + where TException : ArgumentException + { + var ex = Throws(testCode); + if (paramName != null) + { + Assert.Equal(paramName, ex.ParamName); + } + VerifyExceptionMessage(ex, exceptionMessage, partialMatch: true); + return ex; + } + + /// + /// Verifies that the code throws an . + /// + /// A delegate to the code to be tested + /// The name of the parameter that should throw the exception + /// The exception message to verify + /// The exception that was thrown, when successful + public static Task ThrowsArgumentAsync(Func testCode, string paramName, string exceptionMessage) + { + return ThrowsArgumentAsyncInternal(testCode, paramName, exceptionMessage); + } + + private static async Task ThrowsArgumentAsyncInternal( + Func testCode, + string paramName, + string exceptionMessage) + where TException : ArgumentException + { + var ex = await Assert.ThrowsAsync(testCode); + if (paramName != null) + { + Assert.Equal(paramName, ex.ParamName); + } + VerifyExceptionMessage(ex, exceptionMessage, partialMatch: true); + return ex; + } + + /// + /// Verifies that the code throws an . + /// + /// A delegate to the code to be tested + /// The name of the parameter that should throw the exception + /// The exception that was thrown, when successful + public static ArgumentNullException ThrowsArgumentNull(Action testCode, string paramName) + { + var ex = Throws(testCode); + if (paramName != null) + { + Assert.Equal(paramName, ex.ParamName); + } + return ex; + } + + /// + /// Verifies that the code throws an ArgumentException with the expected message that indicates that the value cannot + /// be null or empty. + /// + /// A delegate to the code to be tested + /// The name of the parameter that should throw the exception + /// The exception that was thrown, when successful + public static ArgumentException ThrowsArgumentNullOrEmpty(Action testCode, string paramName) + { + return ThrowsArgumentInternal(testCode, paramName, "Value cannot be null or empty."); + } + + /// + /// Verifies that the code throws an ArgumentException with the expected message that indicates that the value cannot + /// be null or empty. + /// + /// A delegate to the code to be tested + /// The name of the parameter that should throw the exception + /// The exception that was thrown, when successful + public static Task ThrowsArgumentNullOrEmptyAsync(Func testCode, string paramName) + { + return ThrowsArgumentAsyncInternal(testCode, paramName, "Value cannot be null or empty."); + } + + /// + /// Verifies that the code throws an ArgumentNullException with the expected message that indicates that the value cannot + /// be null or empty string. + /// + /// A delegate to the code to be tested + /// The name of the parameter that should throw the exception + /// The exception that was thrown, when successful + public static ArgumentException ThrowsArgumentNullOrEmptyString(Action testCode, string paramName) + { + return ThrowsArgumentInternal(testCode, paramName, "Value cannot be null or an empty string."); + } + + /// + /// Verifies that the code throws an ArgumentNullException with the expected message that indicates that the value cannot + /// be null or empty string. + /// + /// A delegate to the code to be tested + /// The name of the parameter that should throw the exception + /// The exception that was thrown, when successful + public static Task ThrowsArgumentNullOrEmptyStringAsync(Func testCode, string paramName) + { + return ThrowsArgumentAsyncInternal(testCode, paramName, "Value cannot be null or an empty string."); + } + + /// + /// Verifies that the code throws an ArgumentOutOfRangeException (or optionally any exception which derives from it). + /// + /// A delegate to the code to be tested + /// The name of the parameter that should throw the exception + /// The exception message to verify + /// The actual value provided + /// The exception that was thrown, when successful + public static ArgumentOutOfRangeException ThrowsArgumentOutOfRange(Action testCode, string paramName, string exceptionMessage, object actualValue = null) + { + var ex = ThrowsArgumentInternal(testCode, paramName, exceptionMessage); + + if (paramName != null) + { + Assert.Equal(paramName, ex.ParamName); + } + + if (actualValue != null) + { + Assert.Equal(actualValue, ex.ActualValue); + } + + return ex; + } + + // We've re-implemented all the xUnit.net Throws code so that we can get this + // updated implementation of RecordException which silently unwraps any instances + // of AggregateException. In addition to unwrapping exceptions, this method ensures + // that tests are executed in with a known set of Culture and UICulture. This prevents + // tests from failing when executed on a non-English machine. + private static Exception RecordException(Action testCode) + { + try + { + using (new CultureReplacer()) + { + testCode(); + } + return null; + } + catch (Exception exception) + { + return UnwrapException(exception); + } + } + + private static Exception UnwrapException(Exception exception) + { + var aggEx = exception as AggregateException; + return aggEx != null ? aggEx.GetBaseException() : exception; + } + + private static TException VerifyException(Exception exception) + { + var tie = exception as TargetInvocationException; + if (tie != null) + { + exception = tie.InnerException; + } + Assert.NotNull(exception); + return Assert.IsAssignableFrom(exception); + } + + private static void VerifyExceptionMessage(Exception exception, string expectedMessage, bool partialMatch = false) + { + if (expectedMessage != null) + { + if (!partialMatch) + { + Assert.Equal(expectedMessage, exception.Message); + } + else + { + Assert.Contains(expectedMessage, exception.Message); + } + } + } + } +} \ No newline at end of file diff --git a/src/Testing/src/HttpClientSlim.cs b/src/Testing/src/HttpClientSlim.cs new file mode 100644 index 000000000000..6214ffefc18f --- /dev/null +++ b/src/Testing/src/HttpClientSlim.cs @@ -0,0 +1,158 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Globalization; +using System.IO; +using System.Net; +using System.Net.Http; +using System.Net.Security; +using System.Net.Sockets; +using System.Security.Authentication; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Testing +{ + /// + /// Lightweight version of HttpClient implemented using Socket and SslStream. + /// + public static class HttpClientSlim + { + public static async Task GetStringAsync(string requestUri, bool validateCertificate = true) + => await GetStringAsync(new Uri(requestUri), validateCertificate).ConfigureAwait(false); + + public static async Task GetStringAsync(Uri requestUri, bool validateCertificate = true) + { + using (var stream = await GetStream(requestUri, validateCertificate).ConfigureAwait(false)) + { + using (var writer = new StreamWriter(stream, Encoding.ASCII, bufferSize: 1024, leaveOpen: true)) + { + await writer.WriteAsync($"GET {requestUri.PathAndQuery} HTTP/1.0\r\n").ConfigureAwait(false); + await writer.WriteAsync($"Host: {GetHost(requestUri)}\r\n").ConfigureAwait(false); + await writer.WriteAsync("\r\n").ConfigureAwait(false); + } + + return await ReadResponse(stream).ConfigureAwait(false); + } + } + + internal static string GetHost(Uri requestUri) + { + var authority = requestUri.Authority; + if (requestUri.HostNameType == UriHostNameType.IPv6) + { + // Make sure there's no % scope id. https://github.com/aspnet/KestrelHttpServer/issues/2637 + var address = IPAddress.Parse(requestUri.Host); + address = new IPAddress(address.GetAddressBytes()); // Drop scope Id. + if (requestUri.IsDefaultPort) + { + authority = $"[{address}]"; + } + else + { + authority = $"[{address}]:{requestUri.Port.ToString(CultureInfo.InvariantCulture)}"; + } + } + return authority; + } + + public static async Task PostAsync(string requestUri, HttpContent content, bool validateCertificate = true) + => await PostAsync(new Uri(requestUri), content, validateCertificate).ConfigureAwait(false); + + public static async Task PostAsync(Uri requestUri, HttpContent content, bool validateCertificate = true) + { + using (var stream = await GetStream(requestUri, validateCertificate)) + { + using (var writer = new StreamWriter(stream, Encoding.ASCII, bufferSize: 1024, leaveOpen: true)) + { + await writer.WriteAsync($"POST {requestUri.PathAndQuery} HTTP/1.0\r\n").ConfigureAwait(false); + await writer.WriteAsync($"Host: {requestUri.Authority}\r\n").ConfigureAwait(false); + await writer.WriteAsync($"Content-Type: {content.Headers.ContentType}\r\n").ConfigureAwait(false); + await writer.WriteAsync($"Content-Length: {content.Headers.ContentLength}\r\n").ConfigureAwait(false); + await writer.WriteAsync("\r\n").ConfigureAwait(false); + } + + await content.CopyToAsync(stream).ConfigureAwait(false); + + return await ReadResponse(stream).ConfigureAwait(false); + } + } + + private static async Task ReadResponse(Stream stream) + { + using (var reader = new StreamReader(stream, Encoding.ASCII, detectEncodingFromByteOrderMarks: true, + bufferSize: 1024, leaveOpen: true)) + { + var response = await reader.ReadToEndAsync().ConfigureAwait(false); + + var status = GetStatus(response); + new HttpResponseMessage(status).EnsureSuccessStatusCode(); + + var body = response.Substring(response.IndexOf("\r\n\r\n") + 4); + return body; + } + } + + private static HttpStatusCode GetStatus(string response) + { + var statusStart = response.IndexOf(' ') + 1; + var statusEnd = response.IndexOf(' ', statusStart) - 1; + var statusLength = statusEnd - statusStart + 1; + + if (statusLength < 1) + { + throw new InvalidDataException($"No StatusCode found in '{response}'"); + } + + return (HttpStatusCode)int.Parse(response.Substring(statusStart, statusLength)); + } + + private static async Task GetStream(Uri requestUri, bool validateCertificate) + { + var socket = await GetSocket(requestUri); + var stream = new NetworkStream(socket, ownsSocket: true); + + if (requestUri.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase)) + { + var sslStream = new SslStream(stream, leaveInnerStreamOpen: false, userCertificateValidationCallback: + validateCertificate ? null : (RemoteCertificateValidationCallback)((a, b, c, d) => true)); + + await sslStream.AuthenticateAsClientAsync(requestUri.Host, clientCertificates: null, + enabledSslProtocols: SslProtocols.Tls11 | SslProtocols.Tls12, + checkCertificateRevocation: validateCertificate).ConfigureAwait(false); + return sslStream; + } + else + { + return stream; + } + } + + public static async Task GetSocket(Uri requestUri) + { + var tcs = new TaskCompletionSource(); + + var socketArgs = new SocketAsyncEventArgs(); + socketArgs.RemoteEndPoint = new DnsEndPoint(requestUri.DnsSafeHost, requestUri.Port); + socketArgs.Completed += (s, e) => tcs.TrySetResult(e.ConnectSocket); + + // Must use static ConnectAsync(), since instance Connect() does not support DNS names on OSX/Linux. + if (Socket.ConnectAsync(SocketType.Stream, ProtocolType.Tcp, socketArgs)) + { + await tcs.Task.ConfigureAwait(false); + } + + var socket = socketArgs.ConnectSocket; + + if (socket == null) + { + throw new SocketException((int)socketArgs.SocketError); + } + else + { + return socket; + } + } + } +} diff --git a/src/Testing/src/Microsoft.AspNetCore.Testing.csproj b/src/Testing/src/Microsoft.AspNetCore.Testing.csproj new file mode 100644 index 000000000000..d9d9008dd2bd --- /dev/null +++ b/src/Testing/src/Microsoft.AspNetCore.Testing.csproj @@ -0,0 +1,33 @@ + + + + Various helpers for writing tests that use ASP.NET Core. + netstandard2.0;net46 + $(NoWarn);CS1591 + true + aspnetcore + false + true + + + + + + + + + + + + + + + + + + True + contentFiles\cs\netstandard2.0\ + + + + diff --git a/src/Testing/src/Properties/AssemblyInfo.cs b/src/Testing/src/Properties/AssemblyInfo.cs new file mode 100644 index 000000000000..0212e111ee08 --- /dev/null +++ b/src/Testing/src/Properties/AssemblyInfo.cs @@ -0,0 +1,6 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Testing.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] diff --git a/src/Testing/src/ReplaceCulture.cs b/src/Testing/src/ReplaceCulture.cs new file mode 100644 index 000000000000..9580bfd0da7e --- /dev/null +++ b/src/Testing/src/ReplaceCulture.cs @@ -0,0 +1,70 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Globalization; +using System.Reflection; +using Xunit.Sdk; + +namespace Microsoft.AspNetCore.Testing +{ + /// + /// Replaces the current culture and UI culture for the test. + /// + [AttributeUsage(AttributeTargets.Method)] + public class ReplaceCultureAttribute : BeforeAfterTestAttribute + { + private const string _defaultCultureName = "en-GB"; + private const string _defaultUICultureName = "en-US"; + private CultureInfo _originalCulture; + private CultureInfo _originalUICulture; + + /// + /// Replaces the current culture and UI culture to en-GB and en-US respectively. + /// + public ReplaceCultureAttribute() : + this(_defaultCultureName, _defaultUICultureName) + { + } + + /// + /// Replaces the current culture and UI culture based on specified values. + /// + public ReplaceCultureAttribute(string currentCulture, string currentUICulture) + { + Culture = new CultureInfo(currentCulture); + UICulture = new CultureInfo(currentUICulture); + } + + /// + /// The for the test. Defaults to en-GB. + /// + /// + /// en-GB is used here as the default because en-US is equivalent to the InvariantCulture. We + /// want to be able to find bugs where we're accidentally relying on the Invariant instead of the + /// user's culture. + /// + public CultureInfo Culture { get; } + + /// + /// The for the test. Defaults to en-US. + /// + public CultureInfo UICulture { get; } + + public override void Before(MethodInfo methodUnderTest) + { + _originalCulture = CultureInfo.CurrentCulture; + _originalUICulture = CultureInfo.CurrentUICulture; + + CultureInfo.CurrentCulture = Culture; + CultureInfo.CurrentUICulture = UICulture; + } + + public override void After(MethodInfo methodUnderTest) + { + CultureInfo.CurrentCulture = _originalCulture; + CultureInfo.CurrentUICulture = _originalUICulture; + } + } +} + diff --git a/src/Testing/src/TaskExtensions.cs b/src/Testing/src/TaskExtensions.cs new file mode 100644 index 000000000000..83130aeae405 --- /dev/null +++ b/src/Testing/src/TaskExtensions.cs @@ -0,0 +1,64 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Testing +{ + public static class TaskExtensions + { + public static async Task TimeoutAfter(this Task task, TimeSpan timeout, + [CallerFilePath] string filePath = null, + [CallerLineNumber] int lineNumber = default(int)) + { + // Don't create a timer if the task is already completed + if (task.IsCompleted) + { + return await task; + } + + var cts = new CancellationTokenSource(); + if (task == await Task.WhenAny(task, Task.Delay(timeout, cts.Token))) + { + cts.Cancel(); + return await task; + } + else + { + throw new TimeoutException( + CreateMessage(timeout, filePath, lineNumber)); + } + } + + public static async Task TimeoutAfter(this Task task, TimeSpan timeout, + [CallerFilePath] string filePath = null, + [CallerLineNumber] int lineNumber = default(int)) + { + // Don't create a timer if the task is already completed + if (task.IsCompleted) + { + await task; + return; + } + + var cts = new CancellationTokenSource(); + if (task == await Task.WhenAny(task, Task.Delay(timeout, cts.Token))) + { + cts.Cancel(); + await task; + } + else + { + throw new TimeoutException(CreateMessage(timeout, filePath, lineNumber)); + } + } + + private static string CreateMessage(TimeSpan timeout, string filePath, int lineNumber) + => string.IsNullOrEmpty(filePath) + ? $"The operation timed out after reaching the limit of {timeout.TotalMilliseconds}ms." + : $"The operation at {filePath}:{lineNumber} timed out after reaching the limit of {timeout.TotalMilliseconds}ms."; + } +} diff --git a/src/Testing/src/TestPathUtilities.cs b/src/Testing/src/TestPathUtilities.cs new file mode 100644 index 000000000000..ebd10897c382 --- /dev/null +++ b/src/Testing/src/TestPathUtilities.cs @@ -0,0 +1,31 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; + +namespace Microsoft.AspNetCore.Testing +{ + public class TestPathUtilities + { + public static string GetSolutionRootDirectory(string solution) + { + var applicationBasePath = AppContext.BaseDirectory; + var directoryInfo = new DirectoryInfo(applicationBasePath); + + do + { + var projectFileInfo = new FileInfo(Path.Combine(directoryInfo.FullName, $"{solution}.sln")); + if (projectFileInfo.Exists) + { + return projectFileInfo.DirectoryName; + } + + directoryInfo = directoryInfo.Parent; + } + while (directoryInfo.Parent != null); + + throw new Exception($"Solution file {solution}.sln could not be found in {applicationBasePath} or its parent directories."); + } + } +} diff --git a/src/Testing/src/TestPlatformHelper.cs b/src/Testing/src/TestPlatformHelper.cs new file mode 100644 index 000000000000..1a3f275c7e12 --- /dev/null +++ b/src/Testing/src/TestPlatformHelper.cs @@ -0,0 +1,23 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Runtime.InteropServices; + +namespace Microsoft.AspNetCore.Testing +{ + public static class TestPlatformHelper + { + public static bool IsMono => + Type.GetType("Mono.Runtime") != null; + + public static bool IsWindows => + RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + + public static bool IsLinux => + RuntimeInformation.IsOSPlatform(OSPlatform.Linux); + + public static bool IsMac => + RuntimeInformation.IsOSPlatform(OSPlatform.OSX); + } +} \ No newline at end of file diff --git a/src/Testing/src/Tracing/CollectingEventListener.cs b/src/Testing/src/Tracing/CollectingEventListener.cs new file mode 100644 index 000000000000..d22a4996afbd --- /dev/null +++ b/src/Testing/src/Tracing/CollectingEventListener.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics.Tracing; +using System.Linq; + +namespace Microsoft.AspNetCore.Testing.Tracing +{ + public class CollectingEventListener : EventListener + { + private ConcurrentQueue _events = new ConcurrentQueue(); + + private object _lock = new object(); + + private Dictionary _existingSources = new Dictionary(StringComparer.OrdinalIgnoreCase); + private HashSet _requestedEventSources = new HashSet(); + + public void CollectFrom(string eventSourceName) + { + lock(_lock) + { + // Check if it's already been created + if(_existingSources.TryGetValue(eventSourceName, out var existingSource)) + { + // It has, so just enable it now + CollectFrom(existingSource); + } + else + { + // It hasn't, so queue this request for when it is created + _requestedEventSources.Add(eventSourceName); + } + } + } + + public void CollectFrom(EventSource eventSource) => EnableEvents(eventSource, EventLevel.Verbose, EventKeywords.All); + + public IReadOnlyList GetEventsWritten() => _events.ToArray(); + + protected override void OnEventSourceCreated(EventSource eventSource) + { + lock (_lock) + { + // Add this to the list of existing sources for future CollectEventsFrom requests. + _existingSources[eventSource.Name] = eventSource; + + // Check if we have a pending request to enable it + if (_requestedEventSources.Contains(eventSource.Name)) + { + CollectFrom(eventSource); + } + } + } + + protected override void OnEventWritten(EventWrittenEventArgs eventData) + { + _events.Enqueue(eventData); + } + } +} diff --git a/src/Testing/src/Tracing/EventAssert.cs b/src/Testing/src/Tracing/EventAssert.cs new file mode 100644 index 000000000000..b32fb36dad99 --- /dev/null +++ b/src/Testing/src/Tracing/EventAssert.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.Tracing; +using System.Linq; +using Xunit; + +namespace Microsoft.AspNetCore.Testing.Tracing +{ + public class EventAssert + { + private readonly int _expectedId; + private readonly string _expectedName; + private readonly EventLevel _expectedLevel; + private readonly IList<(string name, Action asserter)> _payloadAsserters = new List<(string, Action)>(); + + public EventAssert(int expectedId, string expectedName, EventLevel expectedLevel) + { + _expectedId = expectedId; + _expectedName = expectedName; + _expectedLevel = expectedLevel; + } + + public static void Collection(IEnumerable events, params EventAssert[] asserts) + { + Assert.Collection( + events, + asserts.Select(a => a.CreateAsserter()).ToArray()); + } + + public static EventAssert Event(int id, string name, EventLevel level) + { + return new EventAssert(id, name, level); + } + + public EventAssert Payload(string name, object expectedValue) => Payload(name, actualValue => Assert.Equal(expectedValue, actualValue)); + + public EventAssert Payload(string name, Action asserter) + { + _payloadAsserters.Add((name, asserter)); + return this; + } + + private Action CreateAsserter() => Execute; + + private void Execute(EventWrittenEventArgs evt) + { + Assert.Equal(_expectedId, evt.EventId); + Assert.Equal(_expectedName, evt.EventName); + Assert.Equal(_expectedLevel, evt.Level); + + Action CreateNameAsserter((string name, Action asserter) val) + { + return actualValue => Assert.Equal(val.name, actualValue); + } + + Assert.Collection(evt.PayloadNames, _payloadAsserters.Select(CreateNameAsserter).ToArray()); + Assert.Collection(evt.Payload, _payloadAsserters.Select(t => t.asserter).ToArray()); + } + } +} diff --git a/src/Testing/src/Tracing/EventSourceTestBase.cs b/src/Testing/src/Tracing/EventSourceTestBase.cs new file mode 100644 index 000000000000..721966d6c5c0 --- /dev/null +++ b/src/Testing/src/Tracing/EventSourceTestBase.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.Tracing; +using Xunit; + +namespace Microsoft.AspNetCore.Testing.Tracing +{ + // This collection attribute is what makes the "magic" happen. It forces xunit to run all tests that inherit from this + // base class sequentially, preventing conflicts (since EventSource/EventListener is a process-global concept). + [Collection(CollectionName)] + public abstract class EventSourceTestBase : IDisposable + { + public const string CollectionName = "Microsoft.AspNetCore.Testing.Tracing.EventSourceTestCollection"; + + private readonly CollectingEventListener _listener; + + public EventSourceTestBase() + { + _listener = new CollectingEventListener(); + } + + protected void CollectFrom(string eventSourceName) + { + _listener.CollectFrom(eventSourceName); + } + + protected void CollectFrom(EventSource eventSource) + { + _listener.CollectFrom(eventSource); + } + + protected IReadOnlyList GetEvents() => _listener.GetEventsWritten(); + + public void Dispose() + { + _listener.Dispose(); + } + } +} diff --git a/src/Testing/src/contentFiles/cs/netstandard2.0/EventSourceTestCollection.cs b/src/Testing/src/contentFiles/cs/netstandard2.0/EventSourceTestCollection.cs new file mode 100644 index 000000000000..0ed9e1a9a9b4 --- /dev/null +++ b/src/Testing/src/contentFiles/cs/netstandard2.0/EventSourceTestCollection.cs @@ -0,0 +1,10 @@ +namespace Microsoft.AspNetCore.Testing.Tracing +{ + // This file comes from Microsoft.AspNetCore.Testing and has to be defined in the test assembly. + // It enables EventSourceTestBase's parallel isolation functionality. + + [Xunit.CollectionDefinition(EventSourceTestBase.CollectionName, DisableParallelization = true)] + public class EventSourceTestCollection + { + } +} diff --git a/src/Testing/src/xunit/ConditionalFactAttribute.cs b/src/Testing/src/xunit/ConditionalFactAttribute.cs new file mode 100644 index 000000000000..7448b48d8cfc --- /dev/null +++ b/src/Testing/src/xunit/ConditionalFactAttribute.cs @@ -0,0 +1,15 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Xunit; +using Xunit.Sdk; + +namespace Microsoft.AspNetCore.Testing.xunit +{ + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] + [XunitTestCaseDiscoverer("Microsoft.AspNetCore.Testing.xunit." + nameof(ConditionalFactDiscoverer), "Microsoft.AspNetCore.Testing")] + public class ConditionalFactAttribute : FactAttribute + { + } +} \ No newline at end of file diff --git a/src/Testing/src/xunit/ConditionalFactDiscoverer.cs b/src/Testing/src/xunit/ConditionalFactDiscoverer.cs new file mode 100644 index 000000000000..819373fa3136 --- /dev/null +++ b/src/Testing/src/xunit/ConditionalFactDiscoverer.cs @@ -0,0 +1,27 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Xunit.Abstractions; +using Xunit.Sdk; + +namespace Microsoft.AspNetCore.Testing.xunit +{ + internal class ConditionalFactDiscoverer : FactDiscoverer + { + private readonly IMessageSink _diagnosticMessageSink; + + public ConditionalFactDiscoverer(IMessageSink diagnosticMessageSink) + : base(diagnosticMessageSink) + { + _diagnosticMessageSink = diagnosticMessageSink; + } + + protected override IXunitTestCase CreateTestCase(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo factAttribute) + { + var skipReason = testMethod.EvaluateSkipConditions(); + return skipReason != null + ? new SkippedTestCase(skipReason, _diagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), testMethod) + : base.CreateTestCase(discoveryOptions, testMethod, factAttribute); + } + } +} \ No newline at end of file diff --git a/src/Testing/src/xunit/ConditionalTheoryAttribute.cs b/src/Testing/src/xunit/ConditionalTheoryAttribute.cs new file mode 100644 index 000000000000..9249078cc5c3 --- /dev/null +++ b/src/Testing/src/xunit/ConditionalTheoryAttribute.cs @@ -0,0 +1,15 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Xunit; +using Xunit.Sdk; + +namespace Microsoft.AspNetCore.Testing.xunit +{ + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] + [XunitTestCaseDiscoverer("Microsoft.AspNetCore.Testing.xunit." + nameof(ConditionalTheoryDiscoverer), "Microsoft.AspNetCore.Testing")] + public class ConditionalTheoryAttribute : TheoryAttribute + { + } +} \ No newline at end of file diff --git a/src/Testing/src/xunit/ConditionalTheoryDiscoverer.cs b/src/Testing/src/xunit/ConditionalTheoryDiscoverer.cs new file mode 100644 index 000000000000..d24421f5cd32 --- /dev/null +++ b/src/Testing/src/xunit/ConditionalTheoryDiscoverer.cs @@ -0,0 +1,47 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using Xunit.Abstractions; +using Xunit.Sdk; + +namespace Microsoft.AspNetCore.Testing.xunit +{ + internal class ConditionalTheoryDiscoverer : TheoryDiscoverer + { + public ConditionalTheoryDiscoverer(IMessageSink diagnosticMessageSink) + : base(diagnosticMessageSink) + { + } + + protected override IEnumerable CreateTestCasesForTheory(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo theoryAttribute) + { + var skipReason = testMethod.EvaluateSkipConditions(); + return skipReason != null + ? new[] { new SkippedTestCase(skipReason, DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), testMethod) } + : base.CreateTestCasesForTheory(discoveryOptions, testMethod, theoryAttribute); + } + + protected override IEnumerable CreateTestCasesForDataRow(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo theoryAttribute, object[] dataRow) + { + var skipReason = testMethod.EvaluateSkipConditions(); + if (skipReason == null && dataRow?.Length > 0) + { + var obj = dataRow[0]; + if (obj != null) + { + var type = obj.GetType(); + var property = type.GetProperty("Skip"); + if (property != null && property.PropertyType.Equals(typeof(string))) + { + skipReason = property.GetValue(obj) as string; + } + } + } + + return skipReason != null ? + base.CreateTestCasesForSkippedDataRow(discoveryOptions, testMethod, theoryAttribute, dataRow, skipReason) + : base.CreateTestCasesForDataRow(discoveryOptions, testMethod, theoryAttribute, dataRow); + } + } +} \ No newline at end of file diff --git a/src/Testing/src/xunit/DockerOnlyAttribute.cs b/src/Testing/src/xunit/DockerOnlyAttribute.cs new file mode 100644 index 000000000000..d67a35a672af --- /dev/null +++ b/src/Testing/src/xunit/DockerOnlyAttribute.cs @@ -0,0 +1,38 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; + +namespace Microsoft.AspNetCore.Testing.xunit +{ + [AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = false)] + public sealed class DockerOnlyAttribute : Attribute, ITestCondition + { + public string SkipReason { get; } = "This test can only run in a Docker container."; + + public bool IsMet + { + get + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + // we currently don't have a good way to detect if running in a Windows container + return false; + } + + const string procFile = "/proc/1/cgroup"; + if (!File.Exists(procFile)) + { + return false; + } + + var lines = File.ReadAllLines(procFile); + // typically the last line in the file is "1:name=openrc:/docker" + return lines.Reverse().Any(l => l.EndsWith("name=openrc:/docker", StringComparison.Ordinal)); + } + } + } +} diff --git a/src/Testing/src/xunit/EnvironmentVariableSkipConditionAttribute.cs b/src/Testing/src/xunit/EnvironmentVariableSkipConditionAttribute.cs new file mode 100644 index 000000000000..8bf1bfd15eee --- /dev/null +++ b/src/Testing/src/xunit/EnvironmentVariableSkipConditionAttribute.cs @@ -0,0 +1,95 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Linq; + +namespace Microsoft.AspNetCore.Testing.xunit +{ + /// + /// Skips a test when the value of an environment variable matches any of the supplied values. + /// + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly, AllowMultiple = true)] + public class EnvironmentVariableSkipConditionAttribute : Attribute, ITestCondition + { + private readonly string _variableName; + private readonly string[] _values; + private string _currentValue; + private readonly IEnvironmentVariable _environmentVariable; + + /// + /// Creates a new instance of . + /// + /// Name of the environment variable. + /// Value(s) of the environment variable to match for the test to be skipped + public EnvironmentVariableSkipConditionAttribute(string variableName, params string[] values) + : this(new EnvironmentVariable(), variableName, values) + { + } + + // To enable unit testing + internal EnvironmentVariableSkipConditionAttribute( + IEnvironmentVariable environmentVariable, + string variableName, + params string[] values) + { + if (environmentVariable == null) + { + throw new ArgumentNullException(nameof(environmentVariable)); + } + if (variableName == null) + { + throw new ArgumentNullException(nameof(variableName)); + } + if (values == null) + { + throw new ArgumentNullException(nameof(values)); + } + + _variableName = variableName; + _values = values; + _environmentVariable = environmentVariable; + } + + /// + /// Skips the test only if the value of the variable matches any of the supplied values. Default is True. + /// + public bool SkipOnMatch { get; set; } = true; + + public bool IsMet + { + get + { + _currentValue = _environmentVariable.Get(_variableName); + var hasMatched = _values.Any(value => string.Compare(value, _currentValue, ignoreCase: true) == 0); + + if (SkipOnMatch) + { + return hasMatched; + } + else + { + return !hasMatched; + } + } + } + + public string SkipReason + { + get + { + var value = _currentValue == null ? "(null)" : _currentValue; + return $"Test skipped on environment variable with name '{_variableName}' and value '{value}' " + + $"for the '{nameof(SkipOnMatch)}' value of '{SkipOnMatch}'."; + } + } + + private struct EnvironmentVariable : IEnvironmentVariable + { + public string Get(string name) + { + return Environment.GetEnvironmentVariable(name); + } + } + } +} diff --git a/src/Testing/src/xunit/FrameworkSkipConditionAttribute.cs b/src/Testing/src/xunit/FrameworkSkipConditionAttribute.cs new file mode 100644 index 000000000000..168076a4347d --- /dev/null +++ b/src/Testing/src/xunit/FrameworkSkipConditionAttribute.cs @@ -0,0 +1,57 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.AspNetCore.Testing.xunit +{ + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] + public class FrameworkSkipConditionAttribute : Attribute, ITestCondition + { + private readonly RuntimeFrameworks _excludedFrameworks; + + public FrameworkSkipConditionAttribute(RuntimeFrameworks excludedFrameworks) + { + _excludedFrameworks = excludedFrameworks; + } + + public bool IsMet + { + get + { + return CanRunOnThisFramework(_excludedFrameworks); + } + } + + public string SkipReason { get; set; } = "Test cannot run on this runtime framework."; + + private static bool CanRunOnThisFramework(RuntimeFrameworks excludedFrameworks) + { + if (excludedFrameworks == RuntimeFrameworks.None) + { + return true; + } + +#if NET461 || NET46 + if (excludedFrameworks.HasFlag(RuntimeFrameworks.Mono) && + TestPlatformHelper.IsMono) + { + return false; + } + + if (excludedFrameworks.HasFlag(RuntimeFrameworks.CLR)) + { + return false; + } +#elif NETSTANDARD2_0 + if (excludedFrameworks.HasFlag(RuntimeFrameworks.CoreCLR)) + { + return false; + } +#else +#error Target frameworks need to be updated. +#endif + return true; + } + } +} \ No newline at end of file diff --git a/src/Testing/src/xunit/IEnvironmentVariable.cs b/src/Testing/src/xunit/IEnvironmentVariable.cs new file mode 100644 index 000000000000..068c210611c7 --- /dev/null +++ b/src/Testing/src/xunit/IEnvironmentVariable.cs @@ -0,0 +1,10 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNetCore.Testing.xunit +{ + internal interface IEnvironmentVariable + { + string Get(string name); + } +} diff --git a/src/Testing/src/xunit/ITestCondition.cs b/src/Testing/src/xunit/ITestCondition.cs new file mode 100644 index 000000000000..bb6ff1f03125 --- /dev/null +++ b/src/Testing/src/xunit/ITestCondition.cs @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNetCore.Testing.xunit +{ + public interface ITestCondition + { + bool IsMet { get; } + + string SkipReason { get; } + } +} \ No newline at end of file diff --git a/src/Testing/src/xunit/MinimumOsVersionAttribute.cs b/src/Testing/src/xunit/MinimumOsVersionAttribute.cs new file mode 100644 index 000000000000..89e3b19556bd --- /dev/null +++ b/src/Testing/src/xunit/MinimumOsVersionAttribute.cs @@ -0,0 +1,111 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Runtime.InteropServices; +using Microsoft.Win32; + +namespace Microsoft.AspNetCore.Testing.xunit +{ + /// + /// Skips a test if the OS is the given type (Windows) and the OS version is less than specified. + /// E.g. Specifying Window 10.0 skips on Win 8, but not on Linux. Combine with OSSkipConditionAttribute as needed. + /// + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly, AllowMultiple = true)] + public class MinimumOSVersionAttribute : Attribute, ITestCondition + { + private readonly OperatingSystems _excludedOperatingSystem; + private readonly Version _minVersion; + private readonly OperatingSystems _osPlatform; + private readonly Version _osVersion; + + public MinimumOSVersionAttribute(OperatingSystems operatingSystem, string minVersion) : + this( + operatingSystem, + GetCurrentOS(), + GetCurrentOSVersion(), + Version.Parse(minVersion)) + { + } + + // to enable unit testing + internal MinimumOSVersionAttribute( + OperatingSystems operatingSystem, OperatingSystems osPlatform, Version osVersion, Version minVersion) + { + if (operatingSystem != OperatingSystems.Windows) + { + throw new NotImplementedException("Min version support is only implemented for Windows."); + } + _excludedOperatingSystem = operatingSystem; + _minVersion = minVersion; + _osPlatform = osPlatform; + _osVersion = osVersion; + + SkipReason = $"This test requires {_excludedOperatingSystem} {_minVersion} or later."; + } + + public bool IsMet + { + get + { + // Do not skip other OS's, Use OSSkipConditionAttribute or a separate MinimumOSVersionAttribute for that. + if (_osPlatform != _excludedOperatingSystem) + { + return true; + } + + return _osVersion >= _minVersion; + } + } + + public string SkipReason { get; set; } + + private static OperatingSystems GetCurrentOS() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return OperatingSystems.Windows; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + return OperatingSystems.Linux; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + return OperatingSystems.MacOSX; + } + throw new PlatformNotSupportedException(); + } + + private static Version GetCurrentOSVersion() + { + // currently not used on other OS's + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + // Win10+ + var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion"); + var major = key.GetValue("CurrentMajorVersionNumber") as int?; + var minor = key.GetValue("CurrentMinorVersionNumber") as int?; + + if (major.HasValue && minor.HasValue) + { + return new Version(major.Value, minor.Value); + } + + // CurrentVersion doesn't work past Win8.1 + var current = key.GetValue("CurrentVersion") as string; + if (!string.IsNullOrEmpty(current) && Version.TryParse(current, out var currentVersion)) + { + return currentVersion; + } + + // Environment.OSVersion doesn't work past Win8. + return Environment.OSVersion.Version; + } + else + { + return new Version(); + } + } + } +} diff --git a/src/Testing/src/xunit/OSSkipConditionAttribute.cs b/src/Testing/src/xunit/OSSkipConditionAttribute.cs new file mode 100644 index 000000000000..99965107182b --- /dev/null +++ b/src/Testing/src/xunit/OSSkipConditionAttribute.cs @@ -0,0 +1,99 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; + +namespace Microsoft.AspNetCore.Testing.xunit +{ + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly, AllowMultiple = true)] + public class OSSkipConditionAttribute : Attribute, ITestCondition + { + private readonly OperatingSystems _excludedOperatingSystem; + private readonly IEnumerable _excludedVersions; + private readonly OperatingSystems _osPlatform; + private readonly string _osVersion; + + public OSSkipConditionAttribute(OperatingSystems operatingSystem, params string[] versions) : + this( + operatingSystem, + GetCurrentOS(), + GetCurrentOSVersion(), + versions) + { + } + + // to enable unit testing + internal OSSkipConditionAttribute( + OperatingSystems operatingSystem, OperatingSystems osPlatform, string osVersion, params string[] versions) + { + _excludedOperatingSystem = operatingSystem; + _excludedVersions = versions ?? Enumerable.Empty(); + _osPlatform = osPlatform; + _osVersion = osVersion; + } + + public bool IsMet + { + get + { + var currentOSInfo = new OSInfo() + { + OperatingSystem = _osPlatform, + Version = _osVersion, + }; + + var skip = (_excludedOperatingSystem & currentOSInfo.OperatingSystem) == currentOSInfo.OperatingSystem; + if (_excludedVersions.Any()) + { + skip = skip + && _excludedVersions.Any(ex => _osVersion.StartsWith(ex, StringComparison.OrdinalIgnoreCase)); + } + + // Since a test would be excuted only if 'IsMet' is true, return false if we want to skip + return !skip; + } + } + + public string SkipReason { get; set; } = "Test cannot run on this operating system."; + + static private OperatingSystems GetCurrentOS() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return OperatingSystems.Windows; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + return OperatingSystems.Linux; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + return OperatingSystems.MacOSX; + } + throw new PlatformNotSupportedException(); + } + + static private string GetCurrentOSVersion() + { + // currently not used on other OS's + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return Environment.OSVersion.Version.ToString(); + } + else + { + return string.Empty; + } + } + + private class OSInfo + { + public OperatingSystems OperatingSystem { get; set; } + + public string Version { get; set; } + } + } +} diff --git a/src/Testing/src/xunit/OperatingSystems.cs b/src/Testing/src/xunit/OperatingSystems.cs new file mode 100644 index 000000000000..c575d3e1977f --- /dev/null +++ b/src/Testing/src/xunit/OperatingSystems.cs @@ -0,0 +1,15 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.AspNetCore.Testing.xunit +{ + [Flags] + public enum OperatingSystems + { + Linux = 1, + MacOSX = 2, + Windows = 4, + } +} \ No newline at end of file diff --git a/src/Testing/src/xunit/RuntimeFrameworks.cs b/src/Testing/src/xunit/RuntimeFrameworks.cs new file mode 100644 index 000000000000..2ec5ea7ec1a5 --- /dev/null +++ b/src/Testing/src/xunit/RuntimeFrameworks.cs @@ -0,0 +1,16 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.AspNetCore.Testing.xunit +{ + [Flags] + public enum RuntimeFrameworks + { + None = 0, + Mono = 1 << 0, + CLR = 1 << 1, + CoreCLR = 1 << 2 + } +} \ No newline at end of file diff --git a/src/Testing/src/xunit/SkippedTestCase.cs b/src/Testing/src/xunit/SkippedTestCase.cs new file mode 100644 index 000000000000..c2e15fa640c4 --- /dev/null +++ b/src/Testing/src/xunit/SkippedTestCase.cs @@ -0,0 +1,40 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Xunit.Abstractions; +using Xunit.Sdk; + +namespace Microsoft.AspNetCore.Testing.xunit +{ + public class SkippedTestCase : XunitTestCase + { + private string _skipReason; + + [Obsolete("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")] + public SkippedTestCase() : base() + { + } + + public SkippedTestCase(string skipReason, IMessageSink diagnosticMessageSink, TestMethodDisplay defaultMethodDisplay, ITestMethod testMethod, object[] testMethodArguments = null) + : base(diagnosticMessageSink, defaultMethodDisplay, testMethod, testMethodArguments) + { + _skipReason = skipReason; + } + + protected override string GetSkipReason(IAttributeInfo factAttribute) + => _skipReason ?? base.GetSkipReason(factAttribute); + + public override void Deserialize(IXunitSerializationInfo data) + { + base.Deserialize(data); + _skipReason = data.GetValue(nameof(_skipReason)); + } + + public override void Serialize(IXunitSerializationInfo data) + { + base.Serialize(data); + data.AddValue(nameof(_skipReason), _skipReason); + } + } +} \ No newline at end of file diff --git a/src/Testing/src/xunit/TestMethodExtensions.cs b/src/Testing/src/xunit/TestMethodExtensions.cs new file mode 100644 index 000000000000..5ec3bb4ec377 --- /dev/null +++ b/src/Testing/src/xunit/TestMethodExtensions.cs @@ -0,0 +1,34 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Linq; +using Xunit.Abstractions; +using Xunit.Sdk; + +namespace Microsoft.AspNetCore.Testing.xunit +{ + public static class TestMethodExtensions + { + public static string EvaluateSkipConditions(this ITestMethod testMethod) + { + var testClass = testMethod.TestClass.Class; + var assembly = testMethod.TestClass.TestCollection.TestAssembly.Assembly; + var conditionAttributes = testMethod.Method + .GetCustomAttributes(typeof(ITestCondition)) + .Concat(testClass.GetCustomAttributes(typeof(ITestCondition))) + .Concat(assembly.GetCustomAttributes(typeof(ITestCondition))) + .OfType() + .Select(attributeInfo => attributeInfo.Attribute); + + foreach (ITestCondition condition in conditionAttributes) + { + if (!condition.IsMet) + { + return condition.SkipReason; + } + } + + return null; + } + } +} diff --git a/src/Testing/src/xunit/WindowsVersions.cs b/src/Testing/src/xunit/WindowsVersions.cs new file mode 100644 index 000000000000..ff8312b36385 --- /dev/null +++ b/src/Testing/src/xunit/WindowsVersions.cs @@ -0,0 +1,18 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNetCore.Testing.xunit +{ + public static class WindowsVersions + { + public const string Win7 = "6.1"; + + public const string Win2008R2 = Win7; + + public const string Win8 = "6.2"; + + public const string Win81 = "6.3"; + + public const string Win10 = "10.0"; + } +} diff --git a/src/Testing/test/CollectingEventListenerTest.cs b/src/Testing/test/CollectingEventListenerTest.cs new file mode 100644 index 000000000000..8f131982f0f0 --- /dev/null +++ b/src/Testing/test/CollectingEventListenerTest.cs @@ -0,0 +1,87 @@ +using System.Diagnostics.Tracing; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Testing.Tracing; +using Xunit; + +namespace Microsoft.AspNetCore.Testing.Tests +{ + // We are verifying here that when event listener tests are spread among multiple classes, they still + // work, even when run in parallel. To do that we have a bunch of tests in different classes (since + // that affects parallelism) and do some Task.Yielding in them. + public class CollectingEventListenerTests + { + public abstract class CollectingTestBase : EventSourceTestBase + { + [Fact] + public async Task CollectingEventListenerTest() + { + CollectFrom("Microsoft-AspNetCore-Testing-Test"); + + await Task.Yield(); + TestEventSource.Log.Test(); + await Task.Yield(); + TestEventSource.Log.TestWithPayload(42, 4.2); + await Task.Yield(); + + var events = GetEvents(); + EventAssert.Collection(events, + EventAssert.Event(1, "Test", EventLevel.Informational), + EventAssert.Event(2, "TestWithPayload", EventLevel.Verbose) + .Payload("payload1", 42) + .Payload("payload2", 4.2)); + } + } + + // These tests are designed to interfere with the collecting ones by running in parallel and writing events + public abstract class NonCollectingTestBase + { + [Fact] + public async Task CollectingEventListenerTest() + { + await Task.Yield(); + TestEventSource.Log.Test(); + await Task.Yield(); + TestEventSource.Log.TestWithPayload(42, 4.2); + await Task.Yield(); + } + } + + public class CollectingTests + { + public class A : CollectingTestBase { } + public class B : CollectingTestBase { } + public class C : CollectingTestBase { } + public class D : CollectingTestBase { } + public class E : CollectingTestBase { } + public class F : CollectingTestBase { } + public class G : CollectingTestBase { } + } + + public class NonCollectingTests + { + public class A : NonCollectingTestBase { } + public class B : NonCollectingTestBase { } + public class C : NonCollectingTestBase { } + public class D : NonCollectingTestBase { } + public class E : NonCollectingTestBase { } + public class F : NonCollectingTestBase { } + public class G : NonCollectingTestBase { } + } + } + + [EventSource(Name = "Microsoft-AspNetCore-Testing-Test")] + public class TestEventSource : EventSource + { + public static readonly TestEventSource Log = new TestEventSource(); + + private TestEventSource() + { + } + + [Event(eventId: 1, Level = EventLevel.Informational, Message = "Test")] + public void Test() => WriteEvent(1); + + [Event(eventId: 2, Level = EventLevel.Verbose, Message = "Test")] + public void TestWithPayload(int payload1, double payload2) => WriteEvent(2, payload1, payload2); + } +} diff --git a/src/Testing/test/ConditionalFactTest.cs b/src/Testing/test/ConditionalFactTest.cs new file mode 100644 index 000000000000..a04eb1731db5 --- /dev/null +++ b/src/Testing/test/ConditionalFactTest.cs @@ -0,0 +1,60 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNetCore.Testing.xunit; +using Xunit; + +namespace Microsoft.AspNetCore.Testing +{ + public class ConditionalFactTest : IClassFixture + { + public ConditionalFactTest(ConditionalFactAsserter collector) + { + Asserter = collector; + } + + private ConditionalFactAsserter Asserter { get; } + + [Fact] + public void TestAlwaysRun() + { + // This is required to ensure that the type at least gets initialized. + Assert.True(true); + } + + [ConditionalFact(Skip = "Test is always skipped.")] + public void ConditionalFactSkip() + { + Assert.True(false, "This test should always be skipped."); + } + +#if NETCOREAPP2_2 + [ConditionalFact] + [FrameworkSkipCondition(RuntimeFrameworks.CLR)] + public void ThisTestMustRunOnCoreCLR() + { + Asserter.TestRan = true; + } +#elif NET461 || NET46 + [ConditionalFact] + [FrameworkSkipCondition(RuntimeFrameworks.CoreCLR)] + public void ThisTestMustRunOnCLR() + { + Asserter.TestRan = true; + } +#else +#error Target frameworks need to be updated. +#endif + + public class ConditionalFactAsserter : IDisposable + { + public bool TestRan { get; set; } + + public void Dispose() + { + Assert.True(TestRan, "If this assertion fails, a conditional fact wasn't discovered."); + } + } + } +} \ No newline at end of file diff --git a/src/Testing/test/ConditionalTheoryTest.cs b/src/Testing/test/ConditionalTheoryTest.cs new file mode 100644 index 000000000000..1181f1365af1 --- /dev/null +++ b/src/Testing/test/ConditionalTheoryTest.cs @@ -0,0 +1,156 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNetCore.Testing.xunit; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.AspNetCore.Testing +{ + public class ConditionalTheoryTest : IClassFixture + { + public ConditionalTheoryTest(ConditionalTheoryAsserter asserter) + { + Asserter = asserter; + } + + public ConditionalTheoryAsserter Asserter { get; } + + [ConditionalTheory(Skip = "Test is always skipped.")] + [InlineData(0)] + public void ConditionalTheorySkip(int arg) + { + Assert.True(false, "This test should always be skipped."); + } + + private static int _conditionalTheoryRuns = 0; + + [ConditionalTheory] + [InlineData(0)] + [InlineData(1)] + [InlineData(2, Skip = "Skip these data")] + public void ConditionalTheoryRunOncePerDataLine(int arg) + { + _conditionalTheoryRuns++; + Assert.True(_conditionalTheoryRuns <= 2, $"Theory should run 2 times, but ran {_conditionalTheoryRuns} times."); + } + + [ConditionalTheory, Trait("Color", "Blue")] + [InlineData(1)] + public void ConditionalTheoriesShouldPreserveTraits(int arg) + { + Assert.True(true); + } + + [ConditionalTheory(Skip = "Skip this")] + [MemberData(nameof(GetInts))] + public void ConditionalTheoriesWithSkippedMemberData(int arg) + { + Assert.True(false, "This should never run"); + } + + private static int _conditionalMemberDataRuns = 0; + + [ConditionalTheory] + [InlineData(4)] + [MemberData(nameof(GetInts))] + public void ConditionalTheoriesWithMemberData(int arg) + { + _conditionalMemberDataRuns++; + Assert.True(_conditionalTheoryRuns <= 3, $"Theory should run 2 times, but ran {_conditionalMemberDataRuns} times."); + } + + public static TheoryData GetInts + => new TheoryData { 0, 1 }; + + [ConditionalTheory] + [OSSkipCondition(OperatingSystems.Windows)] + [OSSkipCondition(OperatingSystems.MacOSX)] + [OSSkipCondition(OperatingSystems.Linux)] + [MemberData(nameof(GetActionTestData))] + public void ConditionalTheoryWithFuncs(Func func) + { + Assert.True(false, "This should never run"); + } + + [Fact] + public void TestAlwaysRun() + { + // This is required to ensure that this type at least gets initialized. + Assert.True(true); + } + +#if NETCOREAPP2_2 + [ConditionalTheory] + [FrameworkSkipCondition(RuntimeFrameworks.CLR)] + [MemberData(nameof(GetInts))] + public void ThisTestMustRunOnCoreCLR(int value) + { + Asserter.TestRan = true; + } +#elif NET461 || NET46 + [ConditionalTheory] + [FrameworkSkipCondition(RuntimeFrameworks.CoreCLR)] + [MemberData(nameof(GetInts))] + public void ThisTestMustRunOnCLR(int value) + { + Asserter.TestRan = true; + } +#else +#error Target frameworks need to be updated. +#endif + + public static TheoryData> GetActionTestData + => new TheoryData> + { + (i) => i * 1 + }; + + public class ConditionalTheoryAsserter : IDisposable + { + public bool TestRan { get; set; } + + public void Dispose() + { + Assert.True(TestRan, "If this assertion fails, a conditional theory wasn't discovered."); + } + } + + [ConditionalTheory] + [MemberData(nameof(SkippableData))] + public void WithSkipableData(Skippable skippable) + { + Assert.Null(skippable.Skip); + Assert.Equal(1, skippable.Data); + } + + public static TheoryData SkippableData => new TheoryData + { + new Skippable() { Data = 1 }, + new Skippable() { Data = 2, Skip = "This row should be skipped." } + }; + + public class Skippable : IXunitSerializable + { + public Skippable() { } + public int Data { get; set; } + public string Skip { get; set; } + + public void Serialize(IXunitSerializationInfo info) + { + info.AddValue(nameof(Data), Data, typeof(int)); + } + + public void Deserialize(IXunitSerializationInfo info) + { + Data = info.GetValue(nameof(Data)); + } + + public override string ToString() + { + return Data.ToString(); + } + } + } +} diff --git a/src/Testing/test/DockerTests.cs b/src/Testing/test/DockerTests.cs new file mode 100644 index 000000000000..c66fdd679c26 --- /dev/null +++ b/src/Testing/test/DockerTests.cs @@ -0,0 +1,21 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Runtime.InteropServices; +using Microsoft.AspNetCore.Testing.xunit; +using Xunit; + +namespace Microsoft.AspNetCore.Testing +{ + public class DockerTests + { + [ConditionalFact] + [DockerOnly] + [Trait("Docker", "true")] + public void DoesNotRunOnWindows() + { + Assert.False(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)); + } + } +} diff --git a/src/Testing/test/EnvironmentVariableSkipConditionTest.cs b/src/Testing/test/EnvironmentVariableSkipConditionTest.cs new file mode 100644 index 000000000000..b536ae56f709 --- /dev/null +++ b/src/Testing/test/EnvironmentVariableSkipConditionTest.cs @@ -0,0 +1,166 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Xunit; + +namespace Microsoft.AspNetCore.Testing.xunit +{ + public class EnvironmentVariableSkipConditionTest + { + private readonly string _skipReason = "Test skipped on environment variable with name '{0}' and value '{1}'" + + $" for the '{nameof(EnvironmentVariableSkipConditionAttribute.SkipOnMatch)}' value of '{{2}}'."; + + [Theory] + [InlineData("false")] + [InlineData("")] + [InlineData(null)] + public void IsMet_DoesNotMatch(string environmentVariableValue) + { + // Arrange + var attribute = new EnvironmentVariableSkipConditionAttribute( + new TestEnvironmentVariable(environmentVariableValue), + "Run", + "true"); + + // Act + var isMet = attribute.IsMet; + + // Assert + Assert.False(isMet); + } + + [Theory] + [InlineData("True")] + [InlineData("TRUE")] + [InlineData("true")] + public void IsMet_DoesCaseInsensitiveMatch_OnValue(string environmentVariableValue) + { + // Arrange + var attribute = new EnvironmentVariableSkipConditionAttribute( + new TestEnvironmentVariable(environmentVariableValue), + "Run", + "true"); + + // Act + var isMet = attribute.IsMet; + + // Assert + Assert.True(isMet); + Assert.Equal( + string.Format(_skipReason, "Run", environmentVariableValue, attribute.SkipOnMatch), + attribute.SkipReason); + } + + [Fact] + public void IsMet_DoesSuccessfulMatch_OnNull() + { + // Arrange + var attribute = new EnvironmentVariableSkipConditionAttribute( + new TestEnvironmentVariable(null), + "Run", + "true", null); // skip the test when the variable 'Run' is explicitly set to 'true' or is null (default) + + // Act + var isMet = attribute.IsMet; + + // Assert + Assert.True(isMet); + Assert.Equal( + string.Format(_skipReason, "Run", "(null)", attribute.SkipOnMatch), + attribute.SkipReason); + } + + [Theory] + [InlineData("false")] + [InlineData("")] + [InlineData(null)] + public void IsMet_MatchesOnMultipleSkipValues(string environmentVariableValue) + { + // Arrange + var attribute = new EnvironmentVariableSkipConditionAttribute( + new TestEnvironmentVariable(environmentVariableValue), + "Run", + "false", "", null); + + // Act + var isMet = attribute.IsMet; + + // Assert + Assert.True(isMet); + } + + [Fact] + public void IsMet_DoesNotMatch_OnMultipleSkipValues() + { + // Arrange + var attribute = new EnvironmentVariableSkipConditionAttribute( + new TestEnvironmentVariable("100"), + "Build", + "125", "126"); + + // Act + var isMet = attribute.IsMet; + + // Assert + Assert.False(isMet); + } + + [Theory] + [InlineData("CentOS")] + [InlineData(null)] + [InlineData("")] + public void IsMet_Matches_WhenSkipOnMatchIsFalse(string environmentVariableValue) + { + // Arrange + var attribute = new EnvironmentVariableSkipConditionAttribute( + new TestEnvironmentVariable(environmentVariableValue), + "LinuxFlavor", + "Ubuntu14.04") + { + // Example: Run this test on all OSes except on "Ubuntu14.04" + SkipOnMatch = false + }; + + // Act + var isMet = attribute.IsMet; + + // Assert + Assert.True(isMet); + } + + [Fact] + public void IsMet_DoesNotMatch_WhenSkipOnMatchIsFalse() + { + // Arrange + var attribute = new EnvironmentVariableSkipConditionAttribute( + new TestEnvironmentVariable("Ubuntu14.04"), + "LinuxFlavor", + "Ubuntu14.04") + { + // Example: Run this test on all OSes except on "Ubuntu14.04" + SkipOnMatch = false + }; + + // Act + var isMet = attribute.IsMet; + + // Assert + Assert.False(isMet); + } + + private struct TestEnvironmentVariable : IEnvironmentVariable + { + public TestEnvironmentVariable(string value) + { + Value = value; + } + + public string Value { get; private set; } + + public string Get(string name) + { + return Value; + } + } + } +} diff --git a/src/Testing/test/ExceptionAssertTest.cs b/src/Testing/test/ExceptionAssertTest.cs new file mode 100644 index 000000000000..aa7354dca88d --- /dev/null +++ b/src/Testing/test/ExceptionAssertTest.cs @@ -0,0 +1,39 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Xunit; + +namespace Microsoft.AspNetCore.Testing +{ + public class ExceptionAssertTest + { + [Fact] + [ReplaceCulture("fr-FR", "fr-FR")] + public void AssertArgumentNullOrEmptyString_WorksInNonEnglishCultures() + { + // Arrange + Action action = () => + { + throw new ArgumentException("Value cannot be null or an empty string.", "foo"); + }; + + // Act and Assert + ExceptionAssert.ThrowsArgumentNullOrEmptyString(action, "foo"); + } + + [Fact] + [ReplaceCulture("fr-FR", "fr-FR")] + public void AssertArgumentOutOfRangeException_WorksInNonEnglishCultures() + { + // Arrange + Action action = () => + { + throw new ArgumentOutOfRangeException("foo", 10, "exception message."); + }; + + // Act and Assert + ExceptionAssert.ThrowsArgumentOutOfRange(action, "foo", "exception message.", 10); + } + } +} \ No newline at end of file diff --git a/src/Testing/test/HttpClientSlimTest.cs b/src/Testing/test/HttpClientSlimTest.cs new file mode 100644 index 000000000000..42b19ece08d7 --- /dev/null +++ b/src/Testing/test/HttpClientSlimTest.cs @@ -0,0 +1,117 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Net; +using System.Net.Http; +using System.Net.Sockets; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace Microsoft.AspNetCore.Testing +{ + public class HttpClientSlimTest + { + private static byte[] _defaultResponse = Encoding.ASCII.GetBytes("test"); + + [Fact] + public async Task GetStringAsyncHttp() + { + using (var host = StartHost(out var address)) + { + Assert.Equal("test", await HttpClientSlim.GetStringAsync(address)); + } + } + + [Fact] + public async Task GetStringAsyncThrowsForErrorResponse() + { + using (var host = StartHost(out var address, statusCode: 500)) + { + await Assert.ThrowsAnyAsync(() => HttpClientSlim.GetStringAsync(address)); + } + } + + [Fact] + public async Task PostAsyncHttp() + { + using (var host = StartHost(out var address, handler: context => context.Request.InputStream.CopyToAsync(context.Response.OutputStream))) + { + Assert.Equal("test post", await HttpClientSlim.PostAsync(address, new StringContent("test post"))); + } + } + + [Fact] + public async Task PostAsyncThrowsForErrorResponse() + { + using (var host = StartHost(out var address, statusCode: 500)) + { + await Assert.ThrowsAnyAsync( + () => HttpClientSlim.PostAsync(address, new StringContent(""))); + } + } + + [Fact] + public void Ipv6ScopeIdsFilteredOut() + { + var requestUri = new Uri("http://[fe80::5d2a:d070:6fd6:1bac%7]:5003/"); + Assert.Equal("[fe80::5d2a:d070:6fd6:1bac]:5003", HttpClientSlim.GetHost(requestUri)); + } + + [Fact] + public void GetHostExcludesDefaultPort() + { + var requestUri = new Uri("http://[fe80::5d2a:d070:6fd6:1bac%7]:80/"); + Assert.Equal("[fe80::5d2a:d070:6fd6:1bac]", HttpClientSlim.GetHost(requestUri)); + } + + private HttpListener StartHost(out string address, int statusCode = 200, Func handler = null) + { + var listener = new HttpListener(); + var random = new Random(); + address = null; + + for (var i = 0; i < 10; i++) + { + try + { + // HttpListener doesn't support requesting port 0 (dynamic). + // Requesting port 0 from Sockets and then passing that to HttpListener is racy. + // Just keep trying until we find a free one. + address = $"http://127.0.0.1:{random.Next(1024, ushort.MaxValue)}/"; + listener.Prefixes.Add(address); + listener.Start(); + break; + } + catch (HttpListenerException) + { + // Address in use + listener.Close(); + listener = new HttpListener(); + } + } + + Assert.True(listener.IsListening, "IsListening"); + + _ = listener.GetContextAsync().ContinueWith(async task => + { + var context = task.Result; + context.Response.StatusCode = statusCode; + + if (handler == null) + { + await context.Response.OutputStream.WriteAsync(_defaultResponse, 0, _defaultResponse.Length); + } + else + { + await handler(context); + } + + context.Response.Close(); + }); + + return listener; + } + } +} diff --git a/src/Testing/test/Microsoft.AspNetCore.Testing.Tests.csproj b/src/Testing/test/Microsoft.AspNetCore.Testing.Tests.csproj new file mode 100644 index 000000000000..3fdd9ff379d4 --- /dev/null +++ b/src/Testing/test/Microsoft.AspNetCore.Testing.Tests.csproj @@ -0,0 +1,28 @@ + + + + $(StandardTestTfms) + + + $(NoWarn);xUnit1004 + + $(NoWarn);xUnit1026 + + + + + + + + + + + + + + + + + + + diff --git a/src/Testing/test/OSSkipConditionAttributeTest.cs b/src/Testing/test/OSSkipConditionAttributeTest.cs new file mode 100644 index 000000000000..0120eb7a4c1a --- /dev/null +++ b/src/Testing/test/OSSkipConditionAttributeTest.cs @@ -0,0 +1,132 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Runtime.InteropServices; +using Xunit; + +namespace Microsoft.AspNetCore.Testing.xunit +{ + public class OSSkipConditionAttributeTest + { + [Fact] + public void Skips_WhenOnlyOperatingSystemIsSupplied() + { + // Act + var osSkipAttribute = new OSSkipConditionAttribute( + OperatingSystems.Windows, + OperatingSystems.Windows, + "2.5"); + + // Assert + Assert.False(osSkipAttribute.IsMet); + } + + [Fact] + public void DoesNotSkip_WhenOperatingSystemDoesNotMatch() + { + // Act + var osSkipAttribute = new OSSkipConditionAttribute( + OperatingSystems.Linux, + OperatingSystems.Windows, + "2.5"); + + // Assert + Assert.True(osSkipAttribute.IsMet); + } + + [Fact] + public void DoesNotSkip_WhenVersionsDoNotMatch() + { + // Act + var osSkipAttribute = new OSSkipConditionAttribute( + OperatingSystems.Windows, + OperatingSystems.Windows, + "2.5", + "10.0"); + + // Assert + Assert.True(osSkipAttribute.IsMet); + } + + [Fact] + public void DoesNotSkip_WhenOnlyVersionsMatch() + { + // Act + var osSkipAttribute = new OSSkipConditionAttribute( + OperatingSystems.Linux, + OperatingSystems.Windows, + "2.5", + "2.5"); + + // Assert + Assert.True(osSkipAttribute.IsMet); + } + + [Theory] + [InlineData("2.5", "2.5")] + [InlineData("blue", "Blue")] + public void Skips_WhenVersionsMatches(string currentOSVersion, string skipVersion) + { + // Act + var osSkipAttribute = new OSSkipConditionAttribute( + OperatingSystems.Windows, + OperatingSystems.Windows, + currentOSVersion, + skipVersion); + + // Assert + Assert.False(osSkipAttribute.IsMet); + } + + [Fact] + public void Skips_WhenVersionsMatchesOutOfMultiple() + { + // Act + var osSkipAttribute = new OSSkipConditionAttribute( + OperatingSystems.Windows, + OperatingSystems.Windows, + "2.5", + "10.0", "3.4", "2.5"); + + // Assert + Assert.False(osSkipAttribute.IsMet); + } + + [Fact] + public void Skips_BothMacOSXAndLinux() + { + // Act + var osSkipAttributeLinux = new OSSkipConditionAttribute(OperatingSystems.Linux | OperatingSystems.MacOSX, OperatingSystems.Linux, string.Empty); + var osSkipAttributeMacOSX = new OSSkipConditionAttribute(OperatingSystems.Linux | OperatingSystems.MacOSX, OperatingSystems.MacOSX, string.Empty); + + // Assert + Assert.False(osSkipAttributeLinux.IsMet); + Assert.False(osSkipAttributeMacOSX.IsMet); + } + + [Fact] + public void Skips_BothMacOSXAndWindows() + { + // Act + var osSkipAttribute = new OSSkipConditionAttribute(OperatingSystems.Windows | OperatingSystems.MacOSX, OperatingSystems.Windows, string.Empty); + var osSkipAttributeMacOSX = new OSSkipConditionAttribute(OperatingSystems.Windows | OperatingSystems.MacOSX, OperatingSystems.MacOSX, string.Empty); + + // Assert + Assert.False(osSkipAttribute.IsMet); + Assert.False(osSkipAttributeMacOSX.IsMet); + } + + [Fact] + public void Skips_BothWindowsAndLinux() + { + // Act + var osSkipAttribute = new OSSkipConditionAttribute(OperatingSystems.Linux | OperatingSystems.Windows, OperatingSystems.Windows, string.Empty); + var osSkipAttributeLinux = new OSSkipConditionAttribute(OperatingSystems.Linux | OperatingSystems.Windows, OperatingSystems.Linux, string.Empty); + + // Assert + Assert.False(osSkipAttribute.IsMet); + Assert.False(osSkipAttributeLinux.IsMet); + } + } +} diff --git a/src/Testing/test/OSSkipConditionTest.cs b/src/Testing/test/OSSkipConditionTest.cs new file mode 100644 index 000000000000..2d76f2c2cd81 --- /dev/null +++ b/src/Testing/test/OSSkipConditionTest.cs @@ -0,0 +1,116 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Runtime.InteropServices; +using Xunit; + +namespace Microsoft.AspNetCore.Testing.xunit +{ + public class OSSkipConditionTest + { + [ConditionalFact] + [OSSkipCondition(OperatingSystems.Linux)] + public void TestSkipLinux() + { + Assert.False( + RuntimeInformation.IsOSPlatform(OSPlatform.Linux), + "Test should not be running on Linux"); + } + + [ConditionalFact] + [OSSkipCondition(OperatingSystems.MacOSX)] + public void TestSkipMacOSX() + { + Assert.False( + RuntimeInformation.IsOSPlatform(OSPlatform.OSX), + "Test should not be running on MacOSX."); + } + + [ConditionalFact] + [OSSkipCondition(OperatingSystems.Windows, WindowsVersions.Win7, WindowsVersions.Win2008R2)] + public void RunTest_DoesNotRunOnWin7OrWin2008R2() + { + Assert.False( + RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && + Environment.OSVersion.Version.ToString().StartsWith("6.1"), + "Test should not be running on Win7 or Win2008R2."); + } + + [ConditionalFact] + [OSSkipCondition(OperatingSystems.Windows)] + public void TestSkipWindows() + { + Assert.False( + RuntimeInformation.IsOSPlatform(OSPlatform.Windows), + "Test should not be running on Windows."); + } + + [ConditionalFact] + [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)] + public void TestSkipLinuxAndMacOSX() + { + Assert.False( + RuntimeInformation.IsOSPlatform(OSPlatform.Linux), + "Test should not be running on Linux."); + Assert.False( + RuntimeInformation.IsOSPlatform(OSPlatform.OSX), + "Test should not be running on MacOSX."); + } + + [ConditionalTheory] + [OSSkipCondition(OperatingSystems.Linux)] + [InlineData(1)] + public void TestTheorySkipLinux(int arg) + { + Assert.False( + RuntimeInformation.IsOSPlatform(OSPlatform.Linux), + "Test should not be running on Linux"); + } + + [ConditionalTheory] + [OSSkipCondition(OperatingSystems.MacOSX)] + [InlineData(1)] + public void TestTheorySkipMacOS(int arg) + { + Assert.False( + RuntimeInformation.IsOSPlatform(OSPlatform.OSX), + "Test should not be running on MacOSX."); + } + + [ConditionalTheory] + [OSSkipCondition(OperatingSystems.Windows)] + [InlineData(1)] + public void TestTheorySkipWindows(int arg) + { + Assert.False( + RuntimeInformation.IsOSPlatform(OSPlatform.Windows), + "Test should not be running on Windows."); + } + + [ConditionalTheory] + [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)] + [InlineData(1)] + public void TestTheorySkipLinuxAndMacOSX(int arg) + { + Assert.False( + RuntimeInformation.IsOSPlatform(OSPlatform.Linux), + "Test should not be running on Linux."); + Assert.False( + RuntimeInformation.IsOSPlatform(OSPlatform.OSX), + "Test should not be running on MacOSX."); + } + } + + [OSSkipCondition(OperatingSystems.Windows)] + public class OSSkipConditionClassTest + { + [ConditionalFact] + public void TestSkipClassWindows() + { + Assert.False( + RuntimeInformation.IsOSPlatform(OSPlatform.Windows), + "Test should not be running on Windows."); + } + } +} diff --git a/src/Testing/test/ReplaceCultureAttributeTest.cs b/src/Testing/test/ReplaceCultureAttributeTest.cs new file mode 100644 index 000000000000..6b8df346c93e --- /dev/null +++ b/src/Testing/test/ReplaceCultureAttributeTest.cs @@ -0,0 +1,66 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Globalization; +using Xunit; + +namespace Microsoft.AspNetCore.Testing +{ + public class RepalceCultureAttributeTest + { + [Fact] + public void DefaultsTo_EnGB_EnUS() + { + // Arrange + var culture = new CultureInfo("en-GB"); + var uiCulture = new CultureInfo("en-US"); + + // Act + var replaceCulture = new ReplaceCultureAttribute(); + + // Assert + Assert.Equal(culture, replaceCulture.Culture); + Assert.Equal(uiCulture, replaceCulture.UICulture); + } + + [Fact] + public void UsesSuppliedCultureAndUICulture() + { + // Arrange + var culture = "de-DE"; + var uiCulture = "fr-CA"; + + // Act + var replaceCulture = new ReplaceCultureAttribute(culture, uiCulture); + + // Assert + Assert.Equal(new CultureInfo(culture), replaceCulture.Culture); + Assert.Equal(new CultureInfo(uiCulture), replaceCulture.UICulture); + } + + [Fact] + public void BeforeAndAfterTest_ReplacesCulture() + { + // Arrange + var originalCulture = CultureInfo.CurrentCulture; + var originalUICulture = CultureInfo.CurrentUICulture; + var culture = "de-DE"; + var uiCulture = "fr-CA"; + var replaceCulture = new ReplaceCultureAttribute(culture, uiCulture); + + // Act + replaceCulture.Before(methodUnderTest: null); + + // Assert + Assert.Equal(new CultureInfo(culture), CultureInfo.CurrentCulture); + Assert.Equal(new CultureInfo(uiCulture), CultureInfo.CurrentUICulture); + + // Act + replaceCulture.After(methodUnderTest: null); + + // Assert + Assert.Equal(originalCulture, CultureInfo.CurrentCulture); + Assert.Equal(originalUICulture, CultureInfo.CurrentUICulture); + } + } +} \ No newline at end of file diff --git a/src/Testing/test/TaskExtensionsTest.cs b/src/Testing/test/TaskExtensionsTest.cs new file mode 100644 index 000000000000..f7ad603df551 --- /dev/null +++ b/src/Testing/test/TaskExtensionsTest.cs @@ -0,0 +1,18 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Threading.Tasks; +using Xunit; + +namespace Microsoft.AspNetCore.Testing +{ + public class TaskExtensionsTest + { + [Fact] + public async Task TimeoutAfterTest() + { + await Assert.ThrowsAsync(async () => await Task.Delay(1000).TimeoutAfter(TimeSpan.FromMilliseconds(50))); + } + } +} diff --git a/src/Testing/test/TestPathUtilitiesTest.cs b/src/Testing/test/TestPathUtilitiesTest.cs new file mode 100644 index 000000000000..0c9a7c5ee4a8 --- /dev/null +++ b/src/Testing/test/TestPathUtilitiesTest.cs @@ -0,0 +1,31 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; +using Xunit; + +namespace Microsoft.AspNetCore.Testing +{ + public class TestPathUtilitiesTest + { + [Fact] + public void GetSolutionRootDirectory_ResolvesSolutionRoot() + { + // Directory.GetCurrentDirectory() gives: + // Testing\test\Microsoft.AspNetCore.Testing.Tests\bin\Debug\netcoreapp2.0 + // Testing\test\Microsoft.AspNetCore.Testing.Tests\bin\Debug\net461 + // Testing\test\Microsoft.AspNetCore.Testing.Tests\bin\Debug\net46 + var expectedPath = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), "..", "..", "..", "..", "..", "..", "..")); + + Assert.Equal(expectedPath, TestPathUtilities.GetSolutionRootDirectory("Extensions")); + } + + [Fact] + public void GetSolutionRootDirectory_Throws_IfNotFound() + { + var exception = Assert.Throws(() => TestPathUtilities.GetSolutionRootDirectory("NotTesting")); + Assert.Equal($"Solution file NotTesting.sln could not be found in {AppContext.BaseDirectory} or its parent directories.", exception.Message); + } + } +} diff --git a/src/Testing/test/TestPlatformHelperTest.cs b/src/Testing/test/TestPlatformHelperTest.cs new file mode 100644 index 000000000000..8e35e164d5c5 --- /dev/null +++ b/src/Testing/test/TestPlatformHelperTest.cs @@ -0,0 +1,55 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Testing.xunit; +using Xunit; + +namespace Microsoft.AspNetCore.Testing +{ + public class TestPlatformHelperTest + { + [ConditionalFact] + [OSSkipCondition(OperatingSystems.MacOSX)] + [OSSkipCondition(OperatingSystems.Windows)] + public void IsLinux_TrueOnLinux() + { + Assert.True(TestPlatformHelper.IsLinux); + Assert.False(TestPlatformHelper.IsMac); + Assert.False(TestPlatformHelper.IsWindows); + } + + [ConditionalFact] + [OSSkipCondition(OperatingSystems.Linux)] + [OSSkipCondition(OperatingSystems.Windows)] + public void IsMac_TrueOnMac() + { + Assert.False(TestPlatformHelper.IsLinux); + Assert.True(TestPlatformHelper.IsMac); + Assert.False(TestPlatformHelper.IsWindows); + } + + [ConditionalFact] + [OSSkipCondition(OperatingSystems.Linux)] + [OSSkipCondition(OperatingSystems.MacOSX)] + public void IsWindows_TrueOnWindows() + { + Assert.False(TestPlatformHelper.IsLinux); + Assert.False(TestPlatformHelper.IsMac); + Assert.True(TestPlatformHelper.IsWindows); + } + + [ConditionalFact] + [FrameworkSkipCondition(RuntimeFrameworks.CLR | RuntimeFrameworks.CoreCLR | RuntimeFrameworks.None)] + public void IsMono_TrueOnMono() + { + Assert.True(TestPlatformHelper.IsMono); + } + + [ConditionalFact] + [FrameworkSkipCondition(RuntimeFrameworks.Mono)] + public void IsMono_FalseElsewhere() + { + Assert.False(TestPlatformHelper.IsMono); + } + } +} From 5c9701e0b6640b06635e4c3d570bf77e6de79b2b Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Tue, 30 Oct 2018 17:16:51 -0700 Subject: [PATCH 004/183] Merge branch 'release/2.2' \n\nCommit migrated from https://github.com/dotnet/extensions/commit/34204b6bc41de865f5310f5f237781a57a83976c --- src/Shared/BenchmarkRunner/DefaultCoreConfig.cs | 2 +- src/Shared/CertificateGeneration/CertificateManager.cs | 8 ++++---- .../CertificateGeneration/EnsureCertificateResult.cs | 4 ++-- src/Shared/test/Shared.Tests/CertificateManagerTests.cs | 4 ++-- src/Shared/test/Shared.Tests/DotNetMuxerTests.cs | 2 +- src/Testing/test/ConditionalFactTest.cs | 4 ++-- src/Testing/test/ConditionalTheoryTest.cs | 2 +- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Shared/BenchmarkRunner/DefaultCoreConfig.cs b/src/Shared/BenchmarkRunner/DefaultCoreConfig.cs index 5e2bafd50636..a61833ab269d 100644 --- a/src/Shared/BenchmarkRunner/DefaultCoreConfig.cs +++ b/src/Shared/BenchmarkRunner/DefaultCoreConfig.cs @@ -31,7 +31,7 @@ public DefaultCoreConfig() #if NETCOREAPP2_1 .With(CsProjCoreToolchain.From(NetCoreAppSettings.NetCoreApp21)) #else - .With(CsProjCoreToolchain.From(new NetCoreAppSettings("netcoreapp2.2", null, ".NET Core 2.2"))) + .With(CsProjCoreToolchain.From(new NetCoreAppSettings("netcoreapp3.0", null, ".NET Core 3.0"))) #endif .With(new GcMode { Server = true }) .With(RunStrategy.Throughput)); diff --git a/src/Shared/CertificateGeneration/CertificateManager.cs b/src/Shared/CertificateGeneration/CertificateManager.cs index 952cf7c36d87..26639572e4e1 100644 --- a/src/Shared/CertificateGeneration/CertificateManager.cs +++ b/src/Shared/CertificateGeneration/CertificateManager.cs @@ -32,7 +32,7 @@ internal class CertificateManager private const string MacOSSystemKeyChain = "/Library/Keychains/System.keychain"; private static readonly string MacOSUserKeyChain = Environment.GetEnvironmentVariable("HOME") + "/Library/Keychains/login.keychain-db"; private const string MacOSFindCertificateCommandLine = "security"; -#if NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 +#if NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 || NETCOREAPP3_0 private static readonly string MacOSFindCertificateCommandLineArgumentsFormat = "find-certificate -c {0} -a -Z -p " + MacOSSystemKeyChain; #endif private const string MacOSFindCertificateOutputRegex = "SHA-1 hash: ([0-9A-Z]+)"; @@ -41,7 +41,7 @@ internal class CertificateManager private const string MacOSDeleteCertificateCommandLine = "sudo"; private const string MacOSDeleteCertificateCommandLineArgumentsFormat = "security delete-certificate -Z {0} {1}"; private const string MacOSTrustCertificateCommandLine = "sudo"; -#if NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 +#if NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 || NETCOREAPP3_0 private static readonly string MacOSTrustCertificateCommandLineArguments = "security add-trusted-cert -d -r trustRoot -k " + MacOSSystemKeyChain + " "; #endif private const int UserCancelledErrorCode = 1223; @@ -153,7 +153,7 @@ private static void DisposeCertificates(IEnumerable disposable } } -#if NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 +#if NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 || NETCOREAPP3_0 public X509Certificate2 CreateAspNetCoreHttpsDevelopmentCertificate(DateTimeOffset notBefore, DateTimeOffset notAfter, string subjectOverride, DiagnosticInformation diagnostics = null) { @@ -948,4 +948,4 @@ internal void Error(string preamble, Exception e) } } } -} \ No newline at end of file +} diff --git a/src/Shared/CertificateGeneration/EnsureCertificateResult.cs b/src/Shared/CertificateGeneration/EnsureCertificateResult.cs index 84c495249dbb..2106297faed9 100644 --- a/src/Shared/CertificateGeneration/EnsureCertificateResult.cs +++ b/src/Shared/CertificateGeneration/EnsureCertificateResult.cs @@ -1,7 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -#if NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 +#if NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 || NETCOREAPP3_0 namespace Microsoft.AspNetCore.Certificates.Generation { @@ -17,4 +17,4 @@ internal enum EnsureCertificateResult } } -#endif \ No newline at end of file +#endif diff --git a/src/Shared/test/Shared.Tests/CertificateManagerTests.cs b/src/Shared/test/Shared.Tests/CertificateManagerTests.cs index cd314383c90d..0979eba21bc1 100644 --- a/src/Shared/test/Shared.Tests/CertificateManagerTests.cs +++ b/src/Shared/test/Shared.Tests/CertificateManagerTests.cs @@ -1,7 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -#if NETCOREAPP2_2 +#if NETCOREAPP3_0 using System; using System.IO; @@ -285,4 +285,4 @@ public void EnsureAspNetCoreHttpsDevelopmentCertificate_CanRemoveCertificates() } } -#endif \ No newline at end of file +#endif diff --git a/src/Shared/test/Shared.Tests/DotNetMuxerTests.cs b/src/Shared/test/Shared.Tests/DotNetMuxerTests.cs index 92e06a8f7032..2f412e292ed8 100644 --- a/src/Shared/test/Shared.Tests/DotNetMuxerTests.cs +++ b/src/Shared/test/Shared.Tests/DotNetMuxerTests.cs @@ -1,7 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -#if NETCOREAPP2_2 +#if NETCOREAPP3_0 using System.IO; using System.Runtime.InteropServices; using Xunit; diff --git a/src/Testing/test/ConditionalFactTest.cs b/src/Testing/test/ConditionalFactTest.cs index a04eb1731db5..e136e782479c 100644 --- a/src/Testing/test/ConditionalFactTest.cs +++ b/src/Testing/test/ConditionalFactTest.cs @@ -29,7 +29,7 @@ public void ConditionalFactSkip() Assert.True(false, "This test should always be skipped."); } -#if NETCOREAPP2_2 +#if NETCOREAPP3_0 [ConditionalFact] [FrameworkSkipCondition(RuntimeFrameworks.CLR)] public void ThisTestMustRunOnCoreCLR() @@ -57,4 +57,4 @@ public void Dispose() } } } -} \ No newline at end of file +} diff --git a/src/Testing/test/ConditionalTheoryTest.cs b/src/Testing/test/ConditionalTheoryTest.cs index 1181f1365af1..6950dd412fc9 100644 --- a/src/Testing/test/ConditionalTheoryTest.cs +++ b/src/Testing/test/ConditionalTheoryTest.cs @@ -81,7 +81,7 @@ public void TestAlwaysRun() Assert.True(true); } -#if NETCOREAPP2_2 +#if NETCOREAPP3_0 [ConditionalTheory] [FrameworkSkipCondition(RuntimeFrameworks.CLR)] [MemberData(nameof(GetInts))] From 5261569b614ad389dc9c5c9740728e9aa94403e7 Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Fri, 2 Nov 2018 00:22:14 -0700 Subject: [PATCH 005/183] Reorganize source code in preparation to move into aspnet/Extensions Prior to reorganization, this source code was found in https://github.com/aspnet/DependencyInjection/tree/dotnet/extensions@7a283947c231b6585c8ac95e653950b660f3da96 \n\nCommit migrated from https://github.com/dotnet/extensions/commit/689c4a891cc0783d6c7ed1f9bab1e9854981e65b --- .../ActivatorUtilities/ActivatorUtilities.cs | 429 ++++++++++++++++++ .../ActivatorUtilitiesConstructorAttribute.cs | 26 ++ .../ActivatorUtilities/ObjectFactory.cs | 25 + .../ActivatorUtilities/sharedsources.props | 8 + .../ParameterDefaultValue.cs | 47 ++ 5 files changed, 535 insertions(+) create mode 100644 src/Shared/ActivatorUtilities/ActivatorUtilities.cs create mode 100644 src/Shared/ActivatorUtilities/ActivatorUtilitiesConstructorAttribute.cs create mode 100644 src/Shared/ActivatorUtilities/ObjectFactory.cs create mode 100644 src/Shared/ActivatorUtilities/sharedsources.props create mode 100644 src/Shared/ParameterDefaultValue/ParameterDefaultValue.cs diff --git a/src/Shared/ActivatorUtilities/ActivatorUtilities.cs b/src/Shared/ActivatorUtilities/ActivatorUtilities.cs new file mode 100644 index 000000000000..e2553ced1a2b --- /dev/null +++ b/src/Shared/ActivatorUtilities/ActivatorUtilities.cs @@ -0,0 +1,429 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Runtime.ExceptionServices; + +#if ActivatorUtilities_In_DependencyInjection +using Microsoft.Extensions.Internal; + +namespace Microsoft.Extensions.DependencyInjection +#else +namespace Microsoft.Extensions.Internal +#endif +{ + /// + /// Helper code for the various activator services. + /// + +#if ActivatorUtilities_In_DependencyInjection + public +#else + // Do not take a dependency on this class unless you are explicitly trying to avoid taking a + // dependency on Microsoft.AspNetCore.DependencyInjection.Abstractions. + internal +#endif + static class ActivatorUtilities + { + private static readonly MethodInfo GetServiceInfo = + GetMethodInfo>((sp, t, r, c) => GetService(sp, t, r, c)); + + /// + /// Instantiate a type with constructor arguments provided directly and/or from an . + /// + /// The service provider used to resolve dependencies + /// The type to activate + /// Constructor arguments not provided by the . + /// An activated object of type instanceType + public static object CreateInstance(IServiceProvider provider, Type instanceType, params object[] parameters) + { + int bestLength = -1; + var seenPreferred = false; + + ConstructorMatcher bestMatcher = null; + + if (!instanceType.GetTypeInfo().IsAbstract) + { + foreach (var constructor in instanceType + .GetTypeInfo() + .DeclaredConstructors + .Where(c => !c.IsStatic && c.IsPublic)) + { + var matcher = new ConstructorMatcher(constructor); + var isPreferred = constructor.IsDefined(typeof(ActivatorUtilitiesConstructorAttribute), false); + var length = matcher.Match(parameters); + + if (isPreferred) + { + if (seenPreferred) + { + ThrowMultipleCtorsMarkedWithAttributeException(); + } + + if (length == -1) + { + ThrowMarkedCtorDoesNotTakeAllProvidedArguments(); + } + } + + if (isPreferred || bestLength < length) + { + bestLength = length; + bestMatcher = matcher; + } + + seenPreferred |= isPreferred; + } + } + + if (bestMatcher == null) + { + var message = $"A suitable constructor for type '{instanceType}' could not be located. Ensure the type is concrete and services are registered for all parameters of a public constructor."; + throw new InvalidOperationException(message); + } + + return bestMatcher.CreateInstance(provider); + } + + /// + /// Create a delegate that will instantiate a type with constructor arguments provided directly + /// and/or from an . + /// + /// The type to activate + /// + /// The types of objects, in order, that will be passed to the returned function as its second parameter + /// + /// + /// A factory that will instantiate instanceType using an + /// and an argument array containing objects matching the types defined in argumentTypes + /// + public static ObjectFactory CreateFactory(Type instanceType, Type[] argumentTypes) + { + FindApplicableConstructor(instanceType, argumentTypes, out ConstructorInfo constructor, out int?[] parameterMap); + + var provider = Expression.Parameter(typeof(IServiceProvider), "provider"); + var argumentArray = Expression.Parameter(typeof(object[]), "argumentArray"); + var factoryExpressionBody = BuildFactoryExpression(constructor, parameterMap, provider, argumentArray); + + var factoryLamda = Expression.Lambda>( + factoryExpressionBody, provider, argumentArray); + + var result = factoryLamda.Compile(); + return result.Invoke; + } + + /// + /// Instantiate a type with constructor arguments provided directly and/or from an . + /// + /// The type to activate + /// The service provider used to resolve dependencies + /// Constructor arguments not provided by the . + /// An activated object of type T + public static T CreateInstance(IServiceProvider provider, params object[] parameters) + { + return (T)CreateInstance(provider, typeof(T), parameters); + } + + + /// + /// Retrieve an instance of the given type from the service provider. If one is not found then instantiate it directly. + /// + /// The type of the service + /// The service provider used to resolve dependencies + /// The resolved service or created instance + public static T GetServiceOrCreateInstance(IServiceProvider provider) + { + return (T)GetServiceOrCreateInstance(provider, typeof(T)); + } + + /// + /// Retrieve an instance of the given type from the service provider. If one is not found then instantiate it directly. + /// + /// The service provider + /// The type of the service + /// The resolved service or created instance + public static object GetServiceOrCreateInstance(IServiceProvider provider, Type type) + { + return provider.GetService(type) ?? CreateInstance(provider, type); + } + + private static MethodInfo GetMethodInfo(Expression expr) + { + var mc = (MethodCallExpression)expr.Body; + return mc.Method; + } + + private static object GetService(IServiceProvider sp, Type type, Type requiredBy, bool isDefaultParameterRequired) + { + var service = sp.GetService(type); + if (service == null && !isDefaultParameterRequired) + { + var message = $"Unable to resolve service for type '{type}' while attempting to activate '{requiredBy}'."; + throw new InvalidOperationException(message); + } + return service; + } + + private static Expression BuildFactoryExpression( + ConstructorInfo constructor, + int?[] parameterMap, + Expression serviceProvider, + Expression factoryArgumentArray) + { + var constructorParameters = constructor.GetParameters(); + var constructorArguments = new Expression[constructorParameters.Length]; + + for (var i = 0; i < constructorParameters.Length; i++) + { + var constructorParameter = constructorParameters[i]; + var parameterType = constructorParameter.ParameterType; + var hasDefaultValue = ParameterDefaultValue.TryGetDefaultValue(constructorParameter, out var defaultValue); + + if (parameterMap[i] != null) + { + constructorArguments[i] = Expression.ArrayAccess(factoryArgumentArray, Expression.Constant(parameterMap[i])); + } + else + { + var parameterTypeExpression = new Expression[] { serviceProvider, + Expression.Constant(parameterType, typeof(Type)), + Expression.Constant(constructor.DeclaringType, typeof(Type)), + Expression.Constant(hasDefaultValue) }; + constructorArguments[i] = Expression.Call(GetServiceInfo, parameterTypeExpression); + } + + // Support optional constructor arguments by passing in the default value + // when the argument would otherwise be null. + if (hasDefaultValue) + { + var defaultValueExpression = Expression.Constant(defaultValue); + constructorArguments[i] = Expression.Coalesce(constructorArguments[i], defaultValueExpression); + } + + constructorArguments[i] = Expression.Convert(constructorArguments[i], parameterType); + } + + return Expression.New(constructor, constructorArguments); + } + + private static void FindApplicableConstructor( + Type instanceType, + Type[] argumentTypes, + out ConstructorInfo matchingConstructor, + out int?[] parameterMap) + { + matchingConstructor = null; + parameterMap = null; + + if (!TryFindPreferredConstructor(instanceType, argumentTypes, ref matchingConstructor, ref parameterMap) && + !TryFindMatchingConstructor(instanceType, argumentTypes, ref matchingConstructor, ref parameterMap)) + { + var message = $"A suitable constructor for type '{instanceType}' could not be located. Ensure the type is concrete and services are registered for all parameters of a public constructor."; + throw new InvalidOperationException(message); + } + } + + // Tries to find constructor based on provided argument types + private static bool TryFindMatchingConstructor( + Type instanceType, + Type[] argumentTypes, + ref ConstructorInfo matchingConstructor, + ref int?[] parameterMap) + { + foreach (var constructor in instanceType.GetTypeInfo().DeclaredConstructors) + { + if (constructor.IsStatic || !constructor.IsPublic) + { + continue; + } + + if (TryCreateParameterMap(constructor.GetParameters(), argumentTypes, out int?[] tempParameterMap)) + { + if (matchingConstructor != null) + { + throw new InvalidOperationException($"Multiple constructors accepting all given argument types have been found in type '{instanceType}'. There should only be one applicable constructor."); + } + + matchingConstructor = constructor; + parameterMap = tempParameterMap; + } + } + + return matchingConstructor != null; + } + + // Tries to find constructor marked with ActivatorUtilitiesConstructorAttribute + private static bool TryFindPreferredConstructor( + Type instanceType, + Type[] argumentTypes, + ref ConstructorInfo matchingConstructor, + ref int?[] parameterMap) + { + var seenPreferred = false; + foreach (var constructor in instanceType.GetTypeInfo().DeclaredConstructors) + { + if (constructor.IsStatic || !constructor.IsPublic) + { + continue; + } + + if (constructor.IsDefined(typeof(ActivatorUtilitiesConstructorAttribute), false)) + { + if (seenPreferred) + { + ThrowMultipleCtorsMarkedWithAttributeException(); + } + + if (!TryCreateParameterMap(constructor.GetParameters(), argumentTypes, out int?[] tempParameterMap)) + { + ThrowMarkedCtorDoesNotTakeAllProvidedArguments(); + } + + matchingConstructor = constructor; + parameterMap = tempParameterMap; + seenPreferred = true; + } + } + + return matchingConstructor != null; + } + + // Creates an injective parameterMap from givenParameterTypes to assignable constructorParameters. + // Returns true if each given parameter type is assignable to a unique; otherwise, false. + private static bool TryCreateParameterMap(ParameterInfo[] constructorParameters, Type[] argumentTypes, out int?[] parameterMap) + { + parameterMap = new int?[constructorParameters.Length]; + + for (var i = 0; i < argumentTypes.Length; i++) + { + var foundMatch = false; + var givenParameter = argumentTypes[i].GetTypeInfo(); + + for (var j = 0; j < constructorParameters.Length; j++) + { + if (parameterMap[j] != null) + { + // This ctor parameter has already been matched + continue; + } + + if (constructorParameters[j].ParameterType.GetTypeInfo().IsAssignableFrom(givenParameter)) + { + foundMatch = true; + parameterMap[j] = i; + break; + } + } + + if (!foundMatch) + { + return false; + } + } + + return true; + } + + private class ConstructorMatcher + { + private readonly ConstructorInfo _constructor; + private readonly ParameterInfo[] _parameters; + private readonly object[] _parameterValues; + private readonly bool[] _parameterValuesSet; + + public ConstructorMatcher(ConstructorInfo constructor) + { + _constructor = constructor; + _parameters = _constructor.GetParameters(); + _parameterValuesSet = new bool[_parameters.Length]; + _parameterValues = new object[_parameters.Length]; + } + + public int Match(object[] givenParameters) + { + var applyIndexStart = 0; + var applyExactLength = 0; + for (var givenIndex = 0; givenIndex != givenParameters.Length; givenIndex++) + { + var givenType = givenParameters[givenIndex]?.GetType().GetTypeInfo(); + var givenMatched = false; + + for (var applyIndex = applyIndexStart; givenMatched == false && applyIndex != _parameters.Length; ++applyIndex) + { + if (_parameterValuesSet[applyIndex] == false && + _parameters[applyIndex].ParameterType.GetTypeInfo().IsAssignableFrom(givenType)) + { + givenMatched = true; + _parameterValuesSet[applyIndex] = true; + _parameterValues[applyIndex] = givenParameters[givenIndex]; + if (applyIndexStart == applyIndex) + { + applyIndexStart++; + if (applyIndex == givenIndex) + { + applyExactLength = applyIndex; + } + } + } + } + + if (givenMatched == false) + { + return -1; + } + } + return applyExactLength; + } + + public object CreateInstance(IServiceProvider provider) + { + for (var index = 0; index != _parameters.Length; index++) + { + if (_parameterValuesSet[index] == false) + { + var value = provider.GetService(_parameters[index].ParameterType); + if (value == null) + { + if (!ParameterDefaultValue.TryGetDefaultValue(_parameters[index], out var defaultValue)) + { + throw new InvalidOperationException($"Unable to resolve service for type '{_parameters[index].ParameterType}' while attempting to activate '{_constructor.DeclaringType}'."); + } + else + { + _parameterValues[index] = defaultValue; + } + } + else + { + _parameterValues[index] = value; + } + } + } + + try + { + return _constructor.Invoke(_parameterValues); + } + catch (TargetInvocationException ex) + { + ExceptionDispatchInfo.Capture(ex.InnerException).Throw(); + // The above line will always throw, but the compiler requires we throw explicitly. + throw; + } + } + } + + private static void ThrowMultipleCtorsMarkedWithAttributeException() + { + throw new InvalidOperationException($"Multiple constructors were marked with {nameof(ActivatorUtilitiesConstructorAttribute)}."); + } + + private static void ThrowMarkedCtorDoesNotTakeAllProvidedArguments() + { + throw new InvalidOperationException($"Constructor marked with {nameof(ActivatorUtilitiesConstructorAttribute)} does not accept all given argument types."); + } + } +} diff --git a/src/Shared/ActivatorUtilities/ActivatorUtilitiesConstructorAttribute.cs b/src/Shared/ActivatorUtilities/ActivatorUtilitiesConstructorAttribute.cs new file mode 100644 index 000000000000..67ffa13f6fc0 --- /dev/null +++ b/src/Shared/ActivatorUtilities/ActivatorUtilitiesConstructorAttribute.cs @@ -0,0 +1,26 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +#if ActivatorUtilities_In_DependencyInjection +namespace Microsoft.Extensions.DependencyInjection +#else +namespace Microsoft.Extensions.Internal +#endif +{ + /// + /// Marks the constructor to be used when activating type using . + /// + +#if ActivatorUtilities_In_DependencyInjection + public +#else + // Do not take a dependency on this class unless you are explicitly trying to avoid taking a + // dependency on Microsoft.AspNetCore.DependencyInjection.Abstractions. + internal +#endif + class ActivatorUtilitiesConstructorAttribute: Attribute + { + } +} diff --git a/src/Shared/ActivatorUtilities/ObjectFactory.cs b/src/Shared/ActivatorUtilities/ObjectFactory.cs new file mode 100644 index 000000000000..517247811ec7 --- /dev/null +++ b/src/Shared/ActivatorUtilities/ObjectFactory.cs @@ -0,0 +1,25 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +#if ActivatorUtilities_In_DependencyInjection +namespace Microsoft.Extensions.DependencyInjection +#else +namespace Microsoft.Extensions.Internal +#endif +{ + + /// + /// The result of . + /// + /// The to get service arguments from. + /// Additional constructor arguments. + /// The instantiated type. +#if ActivatorUtilities_In_DependencyInjection + public +#else + internal +#endif + delegate object ObjectFactory(IServiceProvider serviceProvider, object[] arguments); +} \ No newline at end of file diff --git a/src/Shared/ActivatorUtilities/sharedsources.props b/src/Shared/ActivatorUtilities/sharedsources.props new file mode 100644 index 000000000000..b35fe34b10f6 --- /dev/null +++ b/src/Shared/ActivatorUtilities/sharedsources.props @@ -0,0 +1,8 @@ + + + + true + $(ContentTargetFolders)\cs\netstandard1.0\ + + + diff --git a/src/Shared/ParameterDefaultValue/ParameterDefaultValue.cs b/src/Shared/ParameterDefaultValue/ParameterDefaultValue.cs new file mode 100644 index 000000000000..a71bad37b1ad --- /dev/null +++ b/src/Shared/ParameterDefaultValue/ParameterDefaultValue.cs @@ -0,0 +1,47 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Reflection; + +namespace Microsoft.Extensions.Internal +{ + internal class ParameterDefaultValue + { + public static bool TryGetDefaultValue(ParameterInfo parameter, out object defaultValue) + { + bool hasDefaultValue; + var tryToGetDefaultValue = true; + defaultValue = null; + + try + { + hasDefaultValue = parameter.HasDefaultValue; + } + catch (FormatException) when (parameter.ParameterType == typeof(DateTime)) + { + // Workaround for https://github.com/dotnet/corefx/issues/12338 + // If HasDefaultValue throws FormatException for DateTime + // we expect it to have default value + hasDefaultValue = true; + tryToGetDefaultValue = false; + } + + if (hasDefaultValue) + { + if (tryToGetDefaultValue) + { + defaultValue = parameter.DefaultValue; + } + + // Workaround for https://github.com/dotnet/corefx/issues/11797 + if (defaultValue == null && parameter.ParameterType.IsValueType) + { + defaultValue = Activator.CreateInstance(parameter.ParameterType); + } + } + + return hasDefaultValue; + } + } +} From 574a034ddd13f71c06586615b3a5b57884bb2afc Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Fri, 2 Nov 2018 03:25:45 -0700 Subject: [PATCH 006/183] Reorganize source code in preparation to move into aspnet/Extensions Prior to reorganization, this source code was found in https://github.com/aspnet/FileSystem/tree/dotnet/extensions@baebb8b0c672ab37bac72d7196da1b919d362cc5 \n\nCommit migrated from https://github.com/dotnet/extensions/commit/c087cadf1dfdbd2b8785ef764e5ef58a1a7e5ed0 --- src/FileProviders/Directory.Build.props | 8 + .../Embedded/src/EmbeddedFileProvider.cs | 181 ++++++++ .../Embedded/src/EmbeddedResourceFileInfo.cs | 94 ++++ .../src/EnumerableDirectoryContents.cs | 39 ++ .../src/Manifest/EmbeddedFilesManifest.cs | 91 ++++ .../src/Manifest/ManifestDirectory.cs | 127 ++++++ .../src/Manifest/ManifestDirectoryContents.cs | 72 +++ .../src/Manifest/ManifestDirectoryInfo.cs | 39 ++ .../Embedded/src/Manifest/ManifestEntry.cs | 34 ++ .../Embedded/src/Manifest/ManifestFile.cs | 31 ++ .../Embedded/src/Manifest/ManifestFileInfo.cs | 71 +++ .../Embedded/src/Manifest/ManifestParser.cs | 159 +++++++ .../src/Manifest/ManifestRootDirectory.cs | 16 + .../src/Manifest/ManifestSinkDirectory.cs | 22 + .../src/ManifestEmbeddedFileProvider.cs | 153 +++++++ ...t.Extensions.FileProviders.Embedded.csproj | 46 ++ ...t.Extensions.FileProviders.Embedded.nuspec | 33 ++ .../Embedded/src/Properties/AssemblyInfo.cs | 4 + .../Embedded/src/baseline.netcore.json | 343 ++++++++++++++ ...ft.Extensions.FileProviders.Embedded.props | 17 + ....Extensions.FileProviders.Embedded.targets | 69 +++ ...ft.Extensions.FileProviders.Embedded.props | 3 + ....Extensions.FileProviders.Embedded.targets | 3 + .../test/EmbeddedFileProviderTests.cs | 231 ++++++++++ src/FileProviders/Embedded/test/File.txt | 1 + .../Embedded/test/FileInfoComparer.cs | 34 ++ .../Manifest/EmbeddedFilesManifestTests.cs | 58 +++ .../test/Manifest/ManifestEntryTests.cs | 113 +++++ .../test/Manifest/ManifestParserTests.cs | 116 +++++ .../Embedded/test/Manifest/TestEntry.cs | 41 ++ .../test/ManifestEmbeddedFileProviderTests.cs | 428 ++++++++++++++++++ ...nsions.FileProviders.Embedded.Tests.csproj | 15 + .../Embedded/test/Resources/File.txt | 1 + .../ResourcesInSubdirectory/File3.txt | 1 + .../Embedded/test/TestAssembly.cs | 69 +++ .../Embedded/test/TestFileInfo.cs | 34 ++ src/FileProviders/Embedded/test/sub/File2.txt | 1 + .../Embedded/test/sub/dir/File3.txt | Bin 0 -> 6 bytes .../Manifest.MSBuildTask/src/EmbeddedItem.cs | 21 + .../Manifest.MSBuildTask/src/Entry.cs | 120 +++++ .../src/GenerateEmbeddedResourcesManifest.cs | 104 +++++ .../Manifest.MSBuildTask/src/Manifest.cs | 85 ++++ ...ileProviders.Embedded.Manifest.Task.csproj | 25 + .../GenerateEmbeddedResourcesManifestTest.cs | 388 ++++++++++++++++ ...oviders.Embedded.Manifest.Task.Test.csproj | 11 + .../test/SetExtensions.cs | 20 + 46 files changed, 3572 insertions(+) create mode 100644 src/FileProviders/Directory.Build.props create mode 100644 src/FileProviders/Embedded/src/EmbeddedFileProvider.cs create mode 100644 src/FileProviders/Embedded/src/EmbeddedResourceFileInfo.cs create mode 100644 src/FileProviders/Embedded/src/EnumerableDirectoryContents.cs create mode 100644 src/FileProviders/Embedded/src/Manifest/EmbeddedFilesManifest.cs create mode 100644 src/FileProviders/Embedded/src/Manifest/ManifestDirectory.cs create mode 100644 src/FileProviders/Embedded/src/Manifest/ManifestDirectoryContents.cs create mode 100644 src/FileProviders/Embedded/src/Manifest/ManifestDirectoryInfo.cs create mode 100644 src/FileProviders/Embedded/src/Manifest/ManifestEntry.cs create mode 100644 src/FileProviders/Embedded/src/Manifest/ManifestFile.cs create mode 100644 src/FileProviders/Embedded/src/Manifest/ManifestFileInfo.cs create mode 100644 src/FileProviders/Embedded/src/Manifest/ManifestParser.cs create mode 100644 src/FileProviders/Embedded/src/Manifest/ManifestRootDirectory.cs create mode 100644 src/FileProviders/Embedded/src/Manifest/ManifestSinkDirectory.cs create mode 100644 src/FileProviders/Embedded/src/ManifestEmbeddedFileProvider.cs create mode 100644 src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj create mode 100644 src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.nuspec create mode 100644 src/FileProviders/Embedded/src/Properties/AssemblyInfo.cs create mode 100644 src/FileProviders/Embedded/src/baseline.netcore.json create mode 100644 src/FileProviders/Embedded/src/build/netstandard2.0/Microsoft.Extensions.FileProviders.Embedded.props create mode 100644 src/FileProviders/Embedded/src/build/netstandard2.0/Microsoft.Extensions.FileProviders.Embedded.targets create mode 100644 src/FileProviders/Embedded/src/buildMultiTargeting/Microsoft.Extensions.FileProviders.Embedded.props create mode 100644 src/FileProviders/Embedded/src/buildMultiTargeting/Microsoft.Extensions.FileProviders.Embedded.targets create mode 100644 src/FileProviders/Embedded/test/EmbeddedFileProviderTests.cs create mode 100644 src/FileProviders/Embedded/test/File.txt create mode 100644 src/FileProviders/Embedded/test/FileInfoComparer.cs create mode 100644 src/FileProviders/Embedded/test/Manifest/EmbeddedFilesManifestTests.cs create mode 100644 src/FileProviders/Embedded/test/Manifest/ManifestEntryTests.cs create mode 100644 src/FileProviders/Embedded/test/Manifest/ManifestParserTests.cs create mode 100644 src/FileProviders/Embedded/test/Manifest/TestEntry.cs create mode 100644 src/FileProviders/Embedded/test/ManifestEmbeddedFileProviderTests.cs create mode 100644 src/FileProviders/Embedded/test/Microsoft.Extensions.FileProviders.Embedded.Tests.csproj create mode 100644 src/FileProviders/Embedded/test/Resources/File.txt create mode 100644 src/FileProviders/Embedded/test/Resources/ResourcesInSubdirectory/File3.txt create mode 100644 src/FileProviders/Embedded/test/TestAssembly.cs create mode 100644 src/FileProviders/Embedded/test/TestFileInfo.cs create mode 100644 src/FileProviders/Embedded/test/sub/File2.txt create mode 100644 src/FileProviders/Embedded/test/sub/dir/File3.txt create mode 100644 src/FileProviders/Manifest.MSBuildTask/src/EmbeddedItem.cs create mode 100644 src/FileProviders/Manifest.MSBuildTask/src/Entry.cs create mode 100644 src/FileProviders/Manifest.MSBuildTask/src/GenerateEmbeddedResourcesManifest.cs create mode 100644 src/FileProviders/Manifest.MSBuildTask/src/Manifest.cs create mode 100644 src/FileProviders/Manifest.MSBuildTask/src/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.csproj create mode 100644 src/FileProviders/Manifest.MSBuildTask/test/GenerateEmbeddedResourcesManifestTest.cs create mode 100644 src/FileProviders/Manifest.MSBuildTask/test/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Test.csproj create mode 100644 src/FileProviders/Manifest.MSBuildTask/test/SetExtensions.cs diff --git a/src/FileProviders/Directory.Build.props b/src/FileProviders/Directory.Build.props new file mode 100644 index 000000000000..bf4410dcb77e --- /dev/null +++ b/src/FileProviders/Directory.Build.props @@ -0,0 +1,8 @@ + + + + + true + files;filesystem + + diff --git a/src/FileProviders/Embedded/src/EmbeddedFileProvider.cs b/src/FileProviders/Embedded/src/EmbeddedFileProvider.cs new file mode 100644 index 000000000000..75f3f49e4937 --- /dev/null +++ b/src/FileProviders/Embedded/src/EmbeddedFileProvider.cs @@ -0,0 +1,181 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using Microsoft.Extensions.FileProviders.Embedded; +using Microsoft.Extensions.Primitives; + +namespace Microsoft.Extensions.FileProviders +{ + /// + /// Looks up files using embedded resources in the specified assembly. + /// This file provider is case sensitive. + /// + public class EmbeddedFileProvider : IFileProvider + { + private static readonly char[] _invalidFileNameChars = Path.GetInvalidFileNameChars() + .Where(c => c != '/' && c != '\\').ToArray(); + + private readonly Assembly _assembly; + private readonly string _baseNamespace; + private readonly DateTimeOffset _lastModified; + + /// + /// Initializes a new instance of the class using the specified + /// assembly with the base namespace defaulting to the assembly name. + /// + /// The assembly that contains the embedded resources. + public EmbeddedFileProvider(Assembly assembly) + : this(assembly, assembly?.GetName()?.Name) + { + } + + /// + /// Initializes a new instance of the class using the specified + /// assembly and base namespace. + /// + /// The assembly that contains the embedded resources. + /// The base namespace that contains the embedded resources. + public EmbeddedFileProvider(Assembly assembly, string baseNamespace) + { + if (assembly == null) + { + throw new ArgumentNullException("assembly"); + } + + _baseNamespace = string.IsNullOrEmpty(baseNamespace) ? string.Empty : baseNamespace + "."; + _assembly = assembly; + + _lastModified = DateTimeOffset.UtcNow; + + if (!string.IsNullOrEmpty(_assembly.Location)) + { + try + { + _lastModified = File.GetLastWriteTimeUtc(_assembly.Location); + } + catch (PathTooLongException) + { + } + catch (UnauthorizedAccessException) + { + } + } + } + + /// + /// Locates a file at the given path. + /// + /// The path that identifies the file. + /// + /// The file information. Caller must check Exists property. A if the file could + /// not be found. + /// + public IFileInfo GetFileInfo(string subpath) + { + if (string.IsNullOrEmpty(subpath)) + { + return new NotFoundFileInfo(subpath); + } + + var builder = new StringBuilder(_baseNamespace.Length + subpath.Length); + builder.Append(_baseNamespace); + + // Relative paths starting with a leading slash okay + if (subpath.StartsWith("/", StringComparison.Ordinal)) + { + builder.Append(subpath, 1, subpath.Length - 1); + } + else + { + builder.Append(subpath); + } + + for (var i = _baseNamespace.Length; i < builder.Length; i++) + { + if (builder[i] == '/' || builder[i] == '\\') + { + builder[i] = '.'; + } + } + + var resourcePath = builder.ToString(); + if (HasInvalidPathChars(resourcePath)) + { + return new NotFoundFileInfo(resourcePath); + } + + var name = Path.GetFileName(subpath); + if (_assembly.GetManifestResourceInfo(resourcePath) == null) + { + return new NotFoundFileInfo(name); + } + + return new EmbeddedResourceFileInfo(_assembly, resourcePath, name, _lastModified); + } + + /// + /// Enumerate a directory at the given path, if any. + /// This file provider uses a flat directory structure. Everything under the base namespace is considered to be one + /// directory. + /// + /// The path that identifies the directory + /// + /// Contents of the directory. Caller must check Exists property. A if no + /// resources were found that match + /// + public IDirectoryContents GetDirectoryContents(string subpath) + { + // The file name is assumed to be the remainder of the resource name. + if (subpath == null) + { + return NotFoundDirectoryContents.Singleton; + } + + // EmbeddedFileProvider only supports a flat file structure at the base namespace. + if (subpath.Length != 0 && !string.Equals(subpath, "/", StringComparison.Ordinal)) + { + return NotFoundDirectoryContents.Singleton; + } + + var entries = new List(); + + // TODO: The list of resources in an assembly isn't going to change. Consider caching. + var resources = _assembly.GetManifestResourceNames(); + for (var i = 0; i < resources.Length; i++) + { + var resourceName = resources[i]; + if (resourceName.StartsWith(_baseNamespace, StringComparison.Ordinal)) + { + entries.Add(new EmbeddedResourceFileInfo( + _assembly, + resourceName, + resourceName.Substring(_baseNamespace.Length), + _lastModified)); + } + } + + return new EnumerableDirectoryContents(entries); + } + + /// + /// Embedded files do not change. + /// + /// This parameter is ignored + /// A + public IChangeToken Watch(string pattern) + { + return NullChangeToken.Singleton; + } + + private static bool HasInvalidPathChars(string path) + { + return path.IndexOfAny(_invalidFileNameChars) != -1; + } + } +} diff --git a/src/FileProviders/Embedded/src/EmbeddedResourceFileInfo.cs b/src/FileProviders/Embedded/src/EmbeddedResourceFileInfo.cs new file mode 100644 index 000000000000..5dca527342b5 --- /dev/null +++ b/src/FileProviders/Embedded/src/EmbeddedResourceFileInfo.cs @@ -0,0 +1,94 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; +using System.Reflection; + +namespace Microsoft.Extensions.FileProviders.Embedded +{ + /// + /// Represents a file embedded in an assembly. + /// + public class EmbeddedResourceFileInfo : IFileInfo + { + private readonly Assembly _assembly; + private readonly string _resourcePath; + + private long? _length; + + /// + /// Initializes a new instance of for an assembly using as the base + /// + /// The assembly that contains the embedded resource + /// The path to the embedded resource + /// An arbitrary name for this instance + /// The to use for + public EmbeddedResourceFileInfo( + Assembly assembly, + string resourcePath, + string name, + DateTimeOffset lastModified) + { + _assembly = assembly; + _resourcePath = resourcePath; + Name = name; + LastModified = lastModified; + } + + /// + /// Always true. + /// + public bool Exists => true; + + /// + /// The length, in bytes, of the embedded resource + /// + public long Length + { + get + { + if (!_length.HasValue) + { + using (var stream = _assembly.GetManifestResourceStream(_resourcePath)) + { + _length = stream.Length; + } + } + return _length.Value; + } + } + + /// + /// Always null. + /// + public string PhysicalPath => null; + + /// + /// The name of embedded file + /// + public string Name { get; } + + /// + /// The time, in UTC, when the was created + /// + public DateTimeOffset LastModified { get; } + + /// + /// Always false. + /// + public bool IsDirectory => false; + + /// + public Stream CreateReadStream() + { + var stream = _assembly.GetManifestResourceStream(_resourcePath); + if (!_length.HasValue) + { + _length = stream.Length; + } + + return stream; + } + } +} diff --git a/src/FileProviders/Embedded/src/EnumerableDirectoryContents.cs b/src/FileProviders/Embedded/src/EnumerableDirectoryContents.cs new file mode 100644 index 000000000000..012723eba631 --- /dev/null +++ b/src/FileProviders/Embedded/src/EnumerableDirectoryContents.cs @@ -0,0 +1,39 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Microsoft.Extensions.FileProviders.Embedded +{ + internal class EnumerableDirectoryContents : IDirectoryContents + { + private readonly IEnumerable _entries; + + public EnumerableDirectoryContents(IEnumerable entries) + { + if (entries == null) + { + throw new ArgumentNullException(nameof(entries)); + } + + _entries = entries; + } + + public bool Exists + { + get { return true; } + } + + public IEnumerator GetEnumerator() + { + return _entries.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return _entries.GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/src/FileProviders/Embedded/src/Manifest/EmbeddedFilesManifest.cs b/src/FileProviders/Embedded/src/Manifest/EmbeddedFilesManifest.cs new file mode 100644 index 000000000000..f017b9b28961 --- /dev/null +++ b/src/FileProviders/Embedded/src/Manifest/EmbeddedFilesManifest.cs @@ -0,0 +1,91 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Diagnostics; +using System.IO; +using System.Linq; +using Microsoft.Extensions.Primitives; + +namespace Microsoft.Extensions.FileProviders.Embedded.Manifest +{ + internal class EmbeddedFilesManifest + { + private static readonly char[] _invalidFileNameChars = Path.GetInvalidFileNameChars() + .Where(c => c != Path.DirectorySeparatorChar && c != Path.AltDirectorySeparatorChar).ToArray(); + + private static readonly char[] _separators = new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }; + + private readonly ManifestDirectory _rootDirectory; + + internal EmbeddedFilesManifest(ManifestDirectory rootDirectory) + { + if (rootDirectory == null) + { + throw new ArgumentNullException(nameof(rootDirectory)); + } + + _rootDirectory = rootDirectory; + } + + internal ManifestEntry ResolveEntry(string path) + { + if (string.IsNullOrEmpty(path) || HasInvalidPathChars(path)) + { + return null; + } + + // trimmed is a string without leading nor trailing path separators + // so if we find an empty string while iterating over the segments + // we know for sure the path is invalid and we treat it as the above + // case by returning null. + // Examples of invalid paths are: //wwwroot /\wwwroot //wwwroot//jquery.js + var trimmed = RemoveLeadingAndTrailingDirectorySeparators(path); + // Paths consisting only of a single path separator like / or \ are ok. + if (trimmed.Length == 0) + { + return _rootDirectory; + } + + var tokenizer = new StringTokenizer(trimmed, _separators); + ManifestEntry currentEntry = _rootDirectory; + foreach (var segment in tokenizer) + { + if (segment.Equals("")) + { + return null; + } + + currentEntry = currentEntry.Traverse(segment); + } + + return currentEntry; + } + + private static StringSegment RemoveLeadingAndTrailingDirectorySeparators(string path) + { + Debug.Assert(path.Length > 0); + var start = Array.IndexOf(_separators, path[0]) == -1 ? 0 : 1; + if (start == path.Length) + { + return StringSegment.Empty; + } + + var end = Array.IndexOf(_separators, path[path.Length - 1]) == -1 ? path.Length : path.Length - 1; + var trimmed = new StringSegment(path, start, end - start); + return trimmed; + } + + internal EmbeddedFilesManifest Scope(string path) + { + if (ResolveEntry(path) is ManifestDirectory directory && directory != ManifestEntry.UnknownPath) + { + return new EmbeddedFilesManifest(directory.ToRootDirectory()); + } + + throw new InvalidOperationException($"Invalid path: '{path}'"); + } + + private static bool HasInvalidPathChars(string path) => path.IndexOfAny(_invalidFileNameChars) != -1; + } +} diff --git a/src/FileProviders/Embedded/src/Manifest/ManifestDirectory.cs b/src/FileProviders/Embedded/src/Manifest/ManifestDirectory.cs new file mode 100644 index 000000000000..b75653a0fb0f --- /dev/null +++ b/src/FileProviders/Embedded/src/Manifest/ManifestDirectory.cs @@ -0,0 +1,127 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using Microsoft.Extensions.Primitives; + +namespace Microsoft.Extensions.FileProviders.Embedded.Manifest +{ + internal class ManifestDirectory : ManifestEntry + { + protected ManifestDirectory(string name, ManifestEntry[] children) + : base(name) + { + if (children == null) + { + throw new ArgumentNullException(nameof(children)); + } + + Children = children; + } + + public IReadOnlyList Children { get; protected set; } + + public override ManifestEntry Traverse(StringSegment segment) + { + if (segment.Equals(".", StringComparison.Ordinal)) + { + return this; + } + + if (segment.Equals("..", StringComparison.Ordinal)) + { + return Parent; + } + + foreach (var child in Children) + { + if (segment.Equals(child.Name, StringComparison.OrdinalIgnoreCase)) + { + return child; + } + } + + return UnknownPath; + } + + public virtual ManifestDirectory ToRootDirectory() => CreateRootDirectory(CopyChildren()); + + public static ManifestDirectory CreateDirectory(string name, ManifestEntry[] children) + { + if (string.IsNullOrWhiteSpace(name)) + { + throw new ArgumentException($"'{nameof(name)}' must not be null, empty or whitespace.", nameof(name)); + } + + if (children == null) + { + throw new ArgumentNullException(nameof(children)); + } + + var result = new ManifestDirectory(name, children); + ValidateChildrenAndSetParent(children, result); + + return result; + } + + public static ManifestRootDirectory CreateRootDirectory(ManifestEntry[] children) + { + if (children == null) + { + throw new ArgumentNullException(nameof(children)); + } + + var result = new ManifestRootDirectory(children); + ValidateChildrenAndSetParent(children, result); + + return result; + } + + internal static void ValidateChildrenAndSetParent(ManifestEntry[] children, ManifestDirectory parent) + { + foreach (var child in children) + { + if (child == UnknownPath) + { + throw new InvalidOperationException($"Invalid entry type '{nameof(ManifestSinkDirectory)}'"); + } + + if (child is ManifestRootDirectory) + { + throw new InvalidOperationException($"Can't add a root folder as a child"); + } + + child.SetParent(parent); + } + } + + private ManifestEntry[] CopyChildren() + { + var list = new List(); + for (int i = 0; i < Children.Count; i++) + { + var child = Children[i]; + switch (child) + { + case ManifestSinkDirectory s: + case ManifestRootDirectory r: + throw new InvalidOperationException("Unexpected manifest node."); + case ManifestDirectory d: + var grandChildren = d.CopyChildren(); + var newDirectory = CreateDirectory(d.Name, grandChildren); + list.Add(newDirectory); + break; + case ManifestFile f: + var file = new ManifestFile(f.Name, f.ResourcePath); + list.Add(file); + break; + default: + throw new InvalidOperationException("Unexpected manifest node."); + } + } + + return list.ToArray(); + } + } +} diff --git a/src/FileProviders/Embedded/src/Manifest/ManifestDirectoryContents.cs b/src/FileProviders/Embedded/src/Manifest/ManifestDirectoryContents.cs new file mode 100644 index 000000000000..38903dd1967d --- /dev/null +++ b/src/FileProviders/Embedded/src/Manifest/ManifestDirectoryContents.cs @@ -0,0 +1,72 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace Microsoft.Extensions.FileProviders.Embedded.Manifest +{ + internal class ManifestDirectoryContents : IDirectoryContents + { + private readonly DateTimeOffset _lastModified; + private IFileInfo[] _entries; + + public ManifestDirectoryContents(Assembly assembly, ManifestDirectory directory, DateTimeOffset lastModified) + { + if (assembly == null) + { + throw new ArgumentNullException(nameof(assembly)); + } + + if (directory == null) + { + throw new ArgumentNullException(nameof(directory)); + } + + Assembly = assembly; + Directory = directory; + _lastModified = lastModified; + } + + public bool Exists => true; + + public Assembly Assembly { get; } + + public ManifestDirectory Directory { get; } + + public IEnumerator GetEnumerator() + { + return EnsureEntries().GetEnumerator(); + + IReadOnlyList EnsureEntries() => _entries = _entries ?? ResolveEntries().ToArray(); + + IEnumerable ResolveEntries() + { + if (Directory == ManifestEntry.UnknownPath) + { + yield break; + } + + foreach (var entry in Directory.Children) + { + switch (entry) + { + case ManifestFile f: + yield return new ManifestFileInfo(Assembly, f, _lastModified); + break; + case ManifestDirectory d: + yield return new ManifestDirectoryInfo(d, _lastModified); + break; + default: + throw new InvalidOperationException("Unknown entry type"); + } + } + } + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } +} diff --git a/src/FileProviders/Embedded/src/Manifest/ManifestDirectoryInfo.cs b/src/FileProviders/Embedded/src/Manifest/ManifestDirectoryInfo.cs new file mode 100644 index 000000000000..bfe850445d12 --- /dev/null +++ b/src/FileProviders/Embedded/src/Manifest/ManifestDirectoryInfo.cs @@ -0,0 +1,39 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; + +namespace Microsoft.Extensions.FileProviders.Embedded.Manifest +{ + internal class ManifestDirectoryInfo : IFileInfo + { + public ManifestDirectoryInfo(ManifestDirectory directory, DateTimeOffset lastModified) + { + if (directory == null) + { + throw new ArgumentNullException(nameof(directory)); + } + + Directory = directory; + LastModified = lastModified; + } + + public bool Exists => true; + + public long Length => -1; + + public string PhysicalPath => null; + + public string Name => Directory.Name; + + public DateTimeOffset LastModified { get; } + + public bool IsDirectory => true; + + public ManifestDirectory Directory { get; } + + public Stream CreateReadStream() => + throw new InvalidOperationException("Cannot create a stream for a directory."); + } +} diff --git a/src/FileProviders/Embedded/src/Manifest/ManifestEntry.cs b/src/FileProviders/Embedded/src/Manifest/ManifestEntry.cs new file mode 100644 index 000000000000..5c8ead2741a8 --- /dev/null +++ b/src/FileProviders/Embedded/src/Manifest/ManifestEntry.cs @@ -0,0 +1,34 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.Extensions.Primitives; + +namespace Microsoft.Extensions.FileProviders.Embedded.Manifest +{ + internal abstract class ManifestEntry + { + public ManifestEntry(string name) + { + Name = name; + } + + public ManifestEntry Parent { get; private set; } + + public string Name { get; } + + public static ManifestEntry UnknownPath { get; } = ManifestSinkDirectory.Instance; + + protected internal virtual void SetParent(ManifestDirectory directory) + { + if (Parent != null) + { + throw new InvalidOperationException("Directory already has a parent."); + } + + Parent = directory; + } + + public abstract ManifestEntry Traverse(StringSegment segment); + } +} diff --git a/src/FileProviders/Embedded/src/Manifest/ManifestFile.cs b/src/FileProviders/Embedded/src/Manifest/ManifestFile.cs new file mode 100644 index 000000000000..6dd89d34917a --- /dev/null +++ b/src/FileProviders/Embedded/src/Manifest/ManifestFile.cs @@ -0,0 +1,31 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.Extensions.Primitives; + +namespace Microsoft.Extensions.FileProviders.Embedded.Manifest +{ + internal class ManifestFile : ManifestEntry + { + public ManifestFile(string name, string resourcePath) + : base(name) + { + if (string.IsNullOrWhiteSpace(name)) + { + throw new ArgumentException($"'{nameof(name)}' must not be null, empty or whitespace.", nameof(name)); + } + + if (string.IsNullOrWhiteSpace(resourcePath)) + { + throw new ArgumentException($"'{nameof(resourcePath)}' must not be null, empty or whitespace.", nameof(resourcePath)); + } + + ResourcePath = resourcePath; + } + + public string ResourcePath { get; } + + public override ManifestEntry Traverse(StringSegment segment) => UnknownPath; + } +} diff --git a/src/FileProviders/Embedded/src/Manifest/ManifestFileInfo.cs b/src/FileProviders/Embedded/src/Manifest/ManifestFileInfo.cs new file mode 100644 index 000000000000..2329c16f85e5 --- /dev/null +++ b/src/FileProviders/Embedded/src/Manifest/ManifestFileInfo.cs @@ -0,0 +1,71 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; +using System.Reflection; + +namespace Microsoft.Extensions.FileProviders.Embedded.Manifest +{ + internal class ManifestFileInfo : IFileInfo + { + private long? _length; + + public ManifestFileInfo(Assembly assembly, ManifestFile file, DateTimeOffset lastModified) + { + if (assembly == null) + { + throw new ArgumentNullException(nameof(assembly)); + } + + if (file == null) + { + throw new ArgumentNullException(nameof(file)); + } + + Assembly = assembly; + ManifestFile = file; + LastModified = lastModified; + } + + public Assembly Assembly { get; } + + public ManifestFile ManifestFile { get; } + + public bool Exists => true; + + public long Length => EnsureLength(); + + public string PhysicalPath => null; + + public string Name => ManifestFile.Name; + + public DateTimeOffset LastModified { get; } + + public bool IsDirectory => false; + + private long EnsureLength() + { + if (_length == null) + { + using (var stream = Assembly.GetManifestResourceStream(ManifestFile.ResourcePath)) + { + _length = stream.Length; + } + } + + return _length.Value; + } + + public Stream CreateReadStream() + { + var stream = Assembly.GetManifestResourceStream(ManifestFile.ResourcePath); + if (!_length.HasValue) + { + _length = stream.Length; + } + + return stream; + } + } +} diff --git a/src/FileProviders/Embedded/src/Manifest/ManifestParser.cs b/src/FileProviders/Embedded/src/Manifest/ManifestParser.cs new file mode 100644 index 000000000000..a478b747cabb --- /dev/null +++ b/src/FileProviders/Embedded/src/Manifest/ManifestParser.cs @@ -0,0 +1,159 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Xml; +using System.Xml.Linq; + +namespace Microsoft.Extensions.FileProviders.Embedded.Manifest +{ + internal static class ManifestParser + { + private static readonly string DefaultManifestName = "Microsoft.Extensions.FileProviders.Embedded.Manifest.xml"; + + public static EmbeddedFilesManifest Parse(Assembly assembly) + { + return Parse(assembly, DefaultManifestName); + } + + public static EmbeddedFilesManifest Parse(Assembly assembly, string name) + { + if (assembly == null) + { + throw new ArgumentNullException(nameof(assembly)); + } + + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + + var stream = assembly.GetManifestResourceStream(name); + if (stream == null) + { + throw new InvalidOperationException($"Could not load the embedded file manifest " + + $"'{name}' for assembly '{assembly.GetName().Name}'."); + } + + var document = XDocument.Load(stream); + + var manifest = EnsureElement(document, "Manifest"); + var manifestVersion = EnsureElement(manifest, "ManifestVersion"); + var version = EnsureText(manifestVersion); + if (!string.Equals("1.0", version, StringComparison.Ordinal)) + { + throw new InvalidOperationException($"The embedded file manifest '{name}' for " + + $"assembly '{assembly.GetName().Name}' specifies an unsupported file format" + + $" version: '{version}'."); + } + var fileSystem = EnsureElement(manifest, "FileSystem"); + + var entries = fileSystem.Elements(); + var entriesList = new List(); + foreach (var element in entries) + { + var entry = BuildEntry(element); + entriesList.Add(entry); + } + + ValidateEntries(entriesList); + + var rootDirectory = ManifestDirectory.CreateRootDirectory(entriesList.ToArray()); + + return new EmbeddedFilesManifest(rootDirectory); + + } + + private static void ValidateEntries(List entriesList) + { + for (int i = 0; i < entriesList.Count - 1; i++) + { + for (int j = i + 1; j < entriesList.Count; j++) + { + if (string.Equals(entriesList[i].Name, entriesList[j].Name, StringComparison.OrdinalIgnoreCase)) + { + throw new InvalidOperationException( + "Found two entries with the same name but different casing:" + + $" '{entriesList[i].Name}' and '{entriesList[j]}'"); + } + } + } + } + + private static ManifestEntry BuildEntry(XElement element) + { + RuntimeHelpers.EnsureSufficientExecutionStack(); + if (element.NodeType != XmlNodeType.Element) + { + throw new InvalidOperationException($"Invalid manifest format. Expected a 'File' or a 'Directory' node:" + + $" '{element.ToString()}'"); + } + + if (string.Equals(element.Name.LocalName, "File", StringComparison.Ordinal)) + { + var entryName = EnsureName(element); + var path = EnsureElement(element, "ResourcePath"); + var pathValue = EnsureText(path); + return new ManifestFile(entryName, pathValue); + } + + if (string.Equals(element.Name.LocalName, "Directory", StringComparison.Ordinal)) + { + var directoryName = EnsureName(element); + var children = new List(); + foreach (var child in element.Elements()) + { + children.Add(BuildEntry(child)); + } + + ValidateEntries(children); + + return ManifestDirectory.CreateDirectory(directoryName, children.ToArray()); + } + + throw new InvalidOperationException($"Invalid manifest format.Expected a 'File' or a 'Directory' node. " + + $"Got '{element.Name.LocalName}' instead."); + } + + private static XElement EnsureElement(XContainer container, string elementName) + { + var element = container.Element(elementName); + if (element == null) + { + throw new InvalidOperationException($"Invalid manifest format. Missing '{elementName}' element name"); + } + + return element; + } + + private static string EnsureName(XElement element) + { + var value = element.Attribute("Name")?.Value; + if (value == null) + { + throw new InvalidOperationException($"Invalid manifest format. '{element.Name}' must contain a 'Name' attribute."); + } + + return value; + } + + private static string EnsureText(XElement element) + { + if (element.Elements().Count() == 0 && + !element.IsEmpty && + element.Nodes().Count() == 1 && + element.FirstNode.NodeType == XmlNodeType.Text) + { + return element.Value; + } + + throw new InvalidOperationException( + $"Invalid manifest format. '{element.Name.LocalName}' must contain " + + $"a text value. '{element.Value}'"); + } + } +} diff --git a/src/FileProviders/Embedded/src/Manifest/ManifestRootDirectory.cs b/src/FileProviders/Embedded/src/Manifest/ManifestRootDirectory.cs new file mode 100644 index 000000000000..1e5999e90623 --- /dev/null +++ b/src/FileProviders/Embedded/src/Manifest/ManifestRootDirectory.cs @@ -0,0 +1,16 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.Extensions.FileProviders.Embedded.Manifest +{ + internal class ManifestRootDirectory : ManifestDirectory + { + public ManifestRootDirectory(ManifestEntry[] children) + : base(name: null, children: children) + { + SetParent(ManifestSinkDirectory.Instance); + } + + public override ManifestDirectory ToRootDirectory() => this; + } +} diff --git a/src/FileProviders/Embedded/src/Manifest/ManifestSinkDirectory.cs b/src/FileProviders/Embedded/src/Manifest/ManifestSinkDirectory.cs new file mode 100644 index 000000000000..f14908534f55 --- /dev/null +++ b/src/FileProviders/Embedded/src/Manifest/ManifestSinkDirectory.cs @@ -0,0 +1,22 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.Extensions.Primitives; + +namespace Microsoft.Extensions.FileProviders.Embedded.Manifest +{ + internal class ManifestSinkDirectory : ManifestDirectory + { + private ManifestSinkDirectory() + : base(name: null, children: Array.Empty()) + { + SetParent(this); + Children = new[] { this }; + } + + public static ManifestDirectory Instance { get; } = new ManifestSinkDirectory(); + + public override ManifestEntry Traverse(StringSegment segment) => this; + } +} diff --git a/src/FileProviders/Embedded/src/ManifestEmbeddedFileProvider.cs b/src/FileProviders/Embedded/src/ManifestEmbeddedFileProvider.cs new file mode 100644 index 000000000000..f639a2a812c1 --- /dev/null +++ b/src/FileProviders/Embedded/src/ManifestEmbeddedFileProvider.cs @@ -0,0 +1,153 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; +using System.Reflection; +using Microsoft.Extensions.FileProviders.Embedded.Manifest; +using Microsoft.Extensions.Primitives; + +namespace Microsoft.Extensions.FileProviders +{ + /// + /// An embedded file provider that uses a manifest compiled in the assembly to + /// reconstruct the original paths of the embedded files when they were embedded + /// into the assembly. + /// + public class ManifestEmbeddedFileProvider : IFileProvider + { + private readonly DateTimeOffset _lastModified; + + /// + /// Initializes a new instance of . + /// + /// The assembly containing the embedded files. + public ManifestEmbeddedFileProvider(Assembly assembly) + : this(assembly, ManifestParser.Parse(assembly), ResolveLastModified(assembly)) { } + + /// + /// Initializes a new instance of . + /// + /// The assembly containing the embedded files. + /// The relative path from the root of the manifest to use as root for the provider. + public ManifestEmbeddedFileProvider(Assembly assembly, string root) + : this(assembly, root, ResolveLastModified(assembly)) + { + } + + /// + /// Initializes a new instance of . + /// + /// The assembly containing the embedded files. + /// The relative path from the root of the manifest to use as root for the provider. + /// The LastModified date to use on the instances + /// returned by this . + public ManifestEmbeddedFileProvider(Assembly assembly, string root, DateTimeOffset lastModified) + : this(assembly, ManifestParser.Parse(assembly).Scope(root), lastModified) + { + } + + /// + /// Initializes a new instance of . + /// + /// The assembly containing the embedded files. + /// The relative path from the root of the manifest to use as root for the provider. + /// The name of the embedded resource containing the manifest. + /// The LastModified date to use on the instances + /// returned by this . + public ManifestEmbeddedFileProvider(Assembly assembly, string root, string manifestName, DateTimeOffset lastModified) + : this(assembly, ManifestParser.Parse(assembly, manifestName).Scope(root), lastModified) + { + } + + internal ManifestEmbeddedFileProvider(Assembly assembly, EmbeddedFilesManifest manifest, DateTimeOffset lastModified) + { + if (assembly == null) + { + throw new ArgumentNullException(nameof(assembly)); + } + + if (manifest == null) + { + throw new ArgumentNullException(nameof(manifest)); + } + + Assembly = assembly; + Manifest = manifest; + _lastModified = lastModified; + } + + /// + /// Gets the for this provider. + /// + public Assembly Assembly { get; } + + internal EmbeddedFilesManifest Manifest { get; } + + /// + public IDirectoryContents GetDirectoryContents(string subpath) + { + var entry = Manifest.ResolveEntry(subpath); + if (entry == null || entry == ManifestEntry.UnknownPath) + { + return NotFoundDirectoryContents.Singleton; + } + + if (!(entry is ManifestDirectory directory)) + { + return NotFoundDirectoryContents.Singleton; + } + + return new ManifestDirectoryContents(Assembly, directory, _lastModified); + } + + /// + public IFileInfo GetFileInfo(string subpath) + { + var entry = Manifest.ResolveEntry(subpath); + switch (entry) + { + case null: + return new NotFoundFileInfo(subpath); + case ManifestFile f: + return new ManifestFileInfo(Assembly, f, _lastModified); + case ManifestDirectory d when d != ManifestEntry.UnknownPath: + return new NotFoundFileInfo(d.Name); + } + + return new NotFoundFileInfo(subpath); + } + + /// + public IChangeToken Watch(string filter) + { + if (filter == null) + { + throw new ArgumentNullException(nameof(filter)); + } + + return NullChangeToken.Singleton; + } + + private static DateTimeOffset ResolveLastModified(Assembly assembly) + { + var result = DateTimeOffset.UtcNow; + + if (!string.IsNullOrEmpty(assembly.Location)) + { + try + { + result = File.GetLastWriteTimeUtc(assembly.Location); + } + catch (PathTooLongException) + { + } + catch (UnauthorizedAccessException) + { + } + } + + return result; + } + } +} diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj new file mode 100644 index 000000000000..d7ca20b469de --- /dev/null +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj @@ -0,0 +1,46 @@ + + + + Microsoft.Extensions.FileProviders + File provider for files in embedded resources for Microsoft.Extensions.FileProviders. + netstandard2.0 + $(MSBuildProjectName).nuspec + + + + + + + + + + + + id=$(PackageId); + version=$(PackageVersion); + authors=$(Authors); + description=$(Description); + tags=$(PackageTags.Replace(';', ' ')); + licenseUrl=$(PackageLicenseUrl); + projectUrl=$(PackageProjectUrl); + iconUrl=$(PackageIconUrl); + repositoryUrl=$(RepositoryUrl); + repositoryCommit=$(RepositoryCommit); + copyright=$(Copyright); + targetframework=$(TargetFramework); + AssemblyName=$(AssemblyName); + + OutputBinary=@(BuiltProjectOutputGroupOutput); + OutputSymbol=@(DebugSymbolsProjectOutputGroupOutput); + OutputDocumentation=@(DocumentationProjectOutputGroupOutput); + + + TaskAssemblyNetStandard=..\..\Manifest.MSBuildTask\src\bin\$(Configuration)\netstandard1.5\$(AssemblyName).Manifest.Task.dll; + TaskSymbolNetStandard=..\..\Manifest.MSBuildTask\src\bin\$(Configuration)\netstandard1.5\$(AssemblyName).Manifest.Task.pdb; + TaskAssemblyNet461=..\..\Manifest.MSBuildTask\src\bin\$(Configuration)\net461\$(AssemblyName).Manifest.Task.dll; + TaskSymbolNet461=..\..\Manifest.MSBuildTask\src\bin\$(Configuration)\net461\$(AssemblyName).Manifest.Task.pdb; + + + + + diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.nuspec b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.nuspec new file mode 100644 index 000000000000..0cc5ed823a6e --- /dev/null +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.nuspec @@ -0,0 +1,33 @@ + + + + $id$ + $version$ + $authors$ + true + $licenseUrl$ + $projectUrl$ + $iconUrl$ + $description$ + $copyright$ + $tags$ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/FileProviders/Embedded/src/Properties/AssemblyInfo.cs b/src/FileProviders/Embedded/src/Properties/AssemblyInfo.cs new file mode 100644 index 000000000000..610a7fa706f1 --- /dev/null +++ b/src/FileProviders/Embedded/src/Properties/AssemblyInfo.cs @@ -0,0 +1,4 @@ + +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Microsoft.Extensions.FileProviders.Embedded.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] \ No newline at end of file diff --git a/src/FileProviders/Embedded/src/baseline.netcore.json b/src/FileProviders/Embedded/src/baseline.netcore.json new file mode 100644 index 000000000000..821969ea0b51 --- /dev/null +++ b/src/FileProviders/Embedded/src/baseline.netcore.json @@ -0,0 +1,343 @@ +{ + "AssemblyIdentity": "Microsoft.Extensions.FileProviders.Embedded, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "Types": [ + { + "Name": "Microsoft.Extensions.FileProviders.EmbeddedFileProvider", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.Extensions.FileProviders.IFileProvider" + ], + "Members": [ + { + "Kind": "Method", + "Name": "GetFileInfo", + "Parameters": [ + { + "Name": "subpath", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.Extensions.FileProviders.IFileInfo", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileProvider", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetDirectoryContents", + "Parameters": [ + { + "Name": "subpath", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.Extensions.FileProviders.IDirectoryContents", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileProvider", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Watch", + "Parameters": [ + { + "Name": "pattern", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.Extensions.Primitives.IChangeToken", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileProvider", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "assembly", + "Type": "System.Reflection.Assembly" + } + ], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "assembly", + "Type": "System.Reflection.Assembly" + }, + { + "Name": "baseNamespace", + "Type": "System.String" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.Extensions.FileProviders.ManifestEmbeddedFileProvider", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.Extensions.FileProviders.IFileProvider" + ], + "Members": [ + { + "Kind": "Method", + "Name": "get_Assembly", + "Parameters": [], + "ReturnType": "System.Reflection.Assembly", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetDirectoryContents", + "Parameters": [ + { + "Name": "subpath", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.Extensions.FileProviders.IDirectoryContents", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileProvider", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetFileInfo", + "Parameters": [ + { + "Name": "subpath", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.Extensions.FileProviders.IFileInfo", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileProvider", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Watch", + "Parameters": [ + { + "Name": "filter", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.Extensions.Primitives.IChangeToken", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileProvider", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "assembly", + "Type": "System.Reflection.Assembly" + } + ], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "assembly", + "Type": "System.Reflection.Assembly" + }, + { + "Name": "root", + "Type": "System.String" + } + ], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "assembly", + "Type": "System.Reflection.Assembly" + }, + { + "Name": "root", + "Type": "System.String" + }, + { + "Name": "lastModified", + "Type": "System.DateTimeOffset" + } + ], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "assembly", + "Type": "System.Reflection.Assembly" + }, + { + "Name": "root", + "Type": "System.String" + }, + { + "Name": "manifestName", + "Type": "System.String" + }, + { + "Name": "lastModified", + "Type": "System.DateTimeOffset" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.Extensions.FileProviders.Embedded.EmbeddedResourceFileInfo", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.Extensions.FileProviders.IFileInfo" + ], + "Members": [ + { + "Kind": "Method", + "Name": "get_Exists", + "Parameters": [], + "ReturnType": "System.Boolean", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileInfo", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Length", + "Parameters": [], + "ReturnType": "System.Int64", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileInfo", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_PhysicalPath", + "Parameters": [], + "ReturnType": "System.String", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileInfo", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Name", + "Parameters": [], + "ReturnType": "System.String", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileInfo", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_LastModified", + "Parameters": [], + "ReturnType": "System.DateTimeOffset", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileInfo", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_IsDirectory", + "Parameters": [], + "ReturnType": "System.Boolean", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileInfo", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CreateReadStream", + "Parameters": [], + "ReturnType": "System.IO.Stream", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileInfo", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "assembly", + "Type": "System.Reflection.Assembly" + }, + { + "Name": "resourcePath", + "Type": "System.String" + }, + { + "Name": "name", + "Type": "System.String" + }, + { + "Name": "lastModified", + "Type": "System.DateTimeOffset" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + } + ] +} \ No newline at end of file diff --git a/src/FileProviders/Embedded/src/build/netstandard2.0/Microsoft.Extensions.FileProviders.Embedded.props b/src/FileProviders/Embedded/src/build/netstandard2.0/Microsoft.Extensions.FileProviders.Embedded.props new file mode 100644 index 000000000000..e913e173214f --- /dev/null +++ b/src/FileProviders/Embedded/src/build/netstandard2.0/Microsoft.Extensions.FileProviders.Embedded.props @@ -0,0 +1,17 @@ + + + false + Microsoft.Extensions.FileProviders.Embedded.Manifest.xml + + + + <_FileProviderTaskFolder Condition="'$(MSBuildRuntimeType)' == 'Core'">netstandard1.5 + <_FileProviderTaskFolder Condition="'$(MSBuildRuntimeType)' != 'Core'">net461 + <_FileProviderTaskAssembly>$(MSBuildThisFileDirectory)..\..\tasks\$(_FileProviderTaskFolder)\Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.dll + + + + + diff --git a/src/FileProviders/Embedded/src/build/netstandard2.0/Microsoft.Extensions.FileProviders.Embedded.targets b/src/FileProviders/Embedded/src/build/netstandard2.0/Microsoft.Extensions.FileProviders.Embedded.targets new file mode 100644 index 000000000000..83505d7fead7 --- /dev/null +++ b/src/FileProviders/Embedded/src/build/netstandard2.0/Microsoft.Extensions.FileProviders.Embedded.targets @@ -0,0 +1,69 @@ + + + _CalculateEmbeddedFilesManifestInputs;$(PrepareResourceNamesDependsOn) + + + + + + <_GeneratedManifestFile>$(IntermediateOutputPath)$(EmbeddedFilesManifestFileName) + + + + <_FilesForManifest Include="@(EmbeddedResource)" /> + <_FilesForManifest Remove="@(EmbeddedResource->WithMetadataValue('ExcludeFromManifest','true'))" /> + + + + + + + + + + + + <_GeneratedManifestInfoInputsCacheFile>$(IntermediateOutputPath)$(MSBuildProjectName).EmbeddedFilesManifest.cache + + + + + + + + + + + + + + + + + + <_FilesForManifest Remove="@(_FilesForManifest)" /> + <_FilesForManifest Include="@(EmbeddedResource)" /> + <_FilesForManifest Remove="@(EmbeddedResource->WithMetadataValue('ExcludeFromManifest','true'))" /> + + + + + diff --git a/src/FileProviders/Embedded/src/buildMultiTargeting/Microsoft.Extensions.FileProviders.Embedded.props b/src/FileProviders/Embedded/src/buildMultiTargeting/Microsoft.Extensions.FileProviders.Embedded.props new file mode 100644 index 000000000000..87296f28f36e --- /dev/null +++ b/src/FileProviders/Embedded/src/buildMultiTargeting/Microsoft.Extensions.FileProviders.Embedded.props @@ -0,0 +1,3 @@ + + + diff --git a/src/FileProviders/Embedded/src/buildMultiTargeting/Microsoft.Extensions.FileProviders.Embedded.targets b/src/FileProviders/Embedded/src/buildMultiTargeting/Microsoft.Extensions.FileProviders.Embedded.targets new file mode 100644 index 000000000000..9191097036d6 --- /dev/null +++ b/src/FileProviders/Embedded/src/buildMultiTargeting/Microsoft.Extensions.FileProviders.Embedded.targets @@ -0,0 +1,3 @@ + + + diff --git a/src/FileProviders/Embedded/test/EmbeddedFileProviderTests.cs b/src/FileProviders/Embedded/test/EmbeddedFileProviderTests.cs new file mode 100644 index 000000000000..cb9598a1b4f4 --- /dev/null +++ b/src/FileProviders/Embedded/test/EmbeddedFileProviderTests.cs @@ -0,0 +1,231 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Linq; +using System.Reflection; +using Microsoft.AspNetCore.Testing; +using Xunit; + +namespace Microsoft.Extensions.FileProviders.Embedded.Tests +{ + public class EmbeddedFileProviderTests + { + private static readonly string Namespace = typeof(EmbeddedFileProviderTests).Namespace; + + [Fact] + public void ConstructorWithNullAssemblyThrowsArgumentException() + { + Assert.Throws(() => new EmbeddedFileProvider(null)); + } + + [Fact] + public void GetFileInfo_ReturnsNotFoundFileInfo_IfFileDoesNotExist() + { + // Arrange + var provider = new EmbeddedFileProvider(GetType().GetTypeInfo().Assembly); + + // Act + var fileInfo = provider.GetFileInfo("DoesNotExist.Txt"); + + // Assert + Assert.NotNull(fileInfo); + Assert.False(fileInfo.Exists); + } + + [Theory] + [InlineData("File.txt")] + [InlineData("/File.txt")] + public void GetFileInfo_ReturnsFilesAtRoot(string filePath) + { + // Arrange + var provider = new EmbeddedFileProvider(GetType().GetTypeInfo().Assembly); + var expectedFileLength = 8; + + // Act + var fileInfo = provider.GetFileInfo(filePath); + + // Assert + Assert.NotNull(fileInfo); + Assert.True(fileInfo.Exists); + Assert.NotEqual(default(DateTimeOffset), fileInfo.LastModified); + Assert.Equal(expectedFileLength, fileInfo.Length); + Assert.False(fileInfo.IsDirectory); + Assert.Null(fileInfo.PhysicalPath); + Assert.Equal("File.txt", fileInfo.Name); + } + + [Fact] + public void GetFileInfo_ReturnsNotFoundFileInfo_IfFileDoesNotExistUnderSpecifiedNamespace() + { + // Arrange + var provider = new EmbeddedFileProvider(GetType().GetTypeInfo().Assembly, Namespace + ".SubNamespace"); + + // Act + var fileInfo = provider.GetFileInfo("File.txt"); + + // Assert + Assert.NotNull(fileInfo); + Assert.False(fileInfo.Exists); + } + + [Fact] + public void GetFileInfo_ReturnsNotFoundIfPathStartsWithBackSlash() + { + // Arrange + var provider = new EmbeddedFileProvider(GetType().GetTypeInfo().Assembly); + + // Act + var fileInfo = provider.GetFileInfo("\\File.txt"); + + // Assert + Assert.NotNull(fileInfo); + Assert.False(fileInfo.Exists); + } + + public static TheoryData GetFileInfo_LocatesFilesUnderSpecifiedNamespaceData + { + get + { + var theoryData = new TheoryData + { + "ResourcesInSubdirectory/File3.txt" + }; + + if (TestPlatformHelper.IsWindows) + { + theoryData.Add("ResourcesInSubdirectory\\File3.txt"); + } + + return theoryData; + } + } + + [Theory] + [MemberData(nameof(GetFileInfo_LocatesFilesUnderSpecifiedNamespaceData))] + public void GetFileInfo_LocatesFilesUnderSpecifiedNamespace(string path) + { + // Arrange + var provider = new EmbeddedFileProvider(GetType().GetTypeInfo().Assembly, Namespace + ".Resources"); + + // Act + var fileInfo = provider.GetFileInfo(path); + + // Assert + Assert.NotNull(fileInfo); + Assert.True(fileInfo.Exists); + Assert.NotEqual(default(DateTimeOffset), fileInfo.LastModified); + Assert.True(fileInfo.Length > 0); + Assert.False(fileInfo.IsDirectory); + Assert.Null(fileInfo.PhysicalPath); + Assert.Equal("File3.txt", fileInfo.Name); + } + + public static TheoryData GetFileInfo_LocatesFilesUnderSubDirectoriesData + { + get + { + var theoryData = new TheoryData + { + "Resources/File.txt" + }; + + if (TestPlatformHelper.IsWindows) + { + theoryData.Add("Resources\\File.txt"); + } + + return theoryData; + } + } + + [Theory] + [MemberData(nameof(GetFileInfo_LocatesFilesUnderSubDirectoriesData))] + public void GetFileInfo_LocatesFilesUnderSubDirectories(string path) + { + // Arrange + var provider = new EmbeddedFileProvider(GetType().GetTypeInfo().Assembly); + + // Act + var fileInfo = provider.GetFileInfo(path); + + // Assert + Assert.NotNull(fileInfo); + Assert.True(fileInfo.Exists); + Assert.NotEqual(default(DateTimeOffset), fileInfo.LastModified); + Assert.True(fileInfo.Length > 0); + Assert.False(fileInfo.IsDirectory); + Assert.Null(fileInfo.PhysicalPath); + Assert.Equal("File.txt", fileInfo.Name); + } + + [Theory] + [InlineData("")] + [InlineData("/")] + public void GetDirectoryContents_ReturnsAllFilesInFileSystem(string path) + { + // Arrange + var provider = new EmbeddedFileProvider(GetType().GetTypeInfo().Assembly, Namespace + ".Resources"); + + // Act + var files = provider.GetDirectoryContents(path); + + // Assert + Assert.Collection(files.OrderBy(f => f.Name, StringComparer.Ordinal), + file => Assert.Equal("File.txt", file.Name), + file => Assert.Equal("ResourcesInSubdirectory.File3.txt", file.Name)); + + Assert.False(provider.GetDirectoryContents("file").Exists); + Assert.False(provider.GetDirectoryContents("file/").Exists); + Assert.False(provider.GetDirectoryContents("file.txt").Exists); + Assert.False(provider.GetDirectoryContents("file/txt").Exists); + } + + [Fact] + public void GetDirectoryContents_ReturnsEmptySequence_IfResourcesDoNotExistUnderNamespace() + { + // Arrange + var provider = new EmbeddedFileProvider(GetType().GetTypeInfo().Assembly, "Unknown.Namespace"); + + // Act + var files = provider.GetDirectoryContents(string.Empty); + + // Assert + Assert.NotNull(files); + Assert.True(files.Exists); + Assert.Empty(files); + } + + [Theory] + [InlineData("Resources")] + [InlineData("/Resources")] + public void GetDirectoryContents_ReturnsNotFoundDirectoryContents_IfHierarchicalPathIsSpecified(string path) + { + // Arrange + var provider = new EmbeddedFileProvider(GetType().GetTypeInfo().Assembly); + + // Act + var files = provider.GetDirectoryContents(path); + + // Assert + Assert.NotNull(files); + Assert.False(files.Exists); + Assert.Empty(files); + } + + [Fact] + public void Watch_ReturnsNoOpTrigger() + { + // Arange + var provider = new EmbeddedFileProvider(GetType().GetTypeInfo().Assembly); + + // Act + var token = provider.Watch("Resources/File.txt"); + + // Assert + Assert.NotNull(token); + Assert.False(token.ActiveChangeCallbacks); + Assert.False(token.HasChanged); + } + } +} \ No newline at end of file diff --git a/src/FileProviders/Embedded/test/File.txt b/src/FileProviders/Embedded/test/File.txt new file mode 100644 index 000000000000..357323fbfa83 --- /dev/null +++ b/src/FileProviders/Embedded/test/File.txt @@ -0,0 +1 @@ +Hello \ No newline at end of file diff --git a/src/FileProviders/Embedded/test/FileInfoComparer.cs b/src/FileProviders/Embedded/test/FileInfoComparer.cs new file mode 100644 index 000000000000..1b4b69b4c1fb --- /dev/null +++ b/src/FileProviders/Embedded/test/FileInfoComparer.cs @@ -0,0 +1,34 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; + +namespace Microsoft.Extensions.FileProviders +{ + internal class FileInfoComparer : IEqualityComparer + { + public static FileInfoComparer Instance { get; set; } = new FileInfoComparer(); + + public bool Equals(IFileInfo x, IFileInfo y) + { + if (x == null && y == null) + { + return true; + } + + if ((x == null && y != null) || (x != null && y == null)) + { + return false; + } + + return x.Exists == y.Exists && + x.IsDirectory == y.IsDirectory && + x.Length == y.Length && + string.Equals(x.Name, y.Name, StringComparison.Ordinal) && + string.Equals(x.PhysicalPath, y.PhysicalPath, StringComparison.Ordinal); + } + + public int GetHashCode(IFileInfo obj) => 0; + } +} diff --git a/src/FileProviders/Embedded/test/Manifest/EmbeddedFilesManifestTests.cs b/src/FileProviders/Embedded/test/Manifest/EmbeddedFilesManifestTests.cs new file mode 100644 index 000000000000..107491d34a7d --- /dev/null +++ b/src/FileProviders/Embedded/test/Manifest/EmbeddedFilesManifestTests.cs @@ -0,0 +1,58 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Xunit; + +namespace Microsoft.Extensions.FileProviders.Embedded.Manifest +{ + public class EmbeddedFilesManifestTests + { + [Theory] + [InlineData("/wwwroot//jquery.validate.js")] + [InlineData("//wwwroot/jquery.validate.js")] + public void ResolveEntry_IgnoresInvalidPaths(string path) + { + // Arrange + var manifest = new EmbeddedFilesManifest( + ManifestDirectory.CreateRootDirectory( + new[] + { + ManifestDirectory.CreateDirectory("wwwroot", + new[] + { + new ManifestFile("jquery.validate.js","wwwroot.jquery.validate.js") + }) + })); + // Act + var entry = manifest.ResolveEntry(path); + + // Assert + Assert.Null(entry); + } + + [Theory] + [InlineData("/")] + [InlineData("./")] + [InlineData("/wwwroot/jquery.validate.js")] + [InlineData("/wwwroot/")] + public void ResolveEntry_AllowsSingleDirectorySeparator(string path) + { + // Arrange + var manifest = new EmbeddedFilesManifest( + ManifestDirectory.CreateRootDirectory( + new[] + { + ManifestDirectory.CreateDirectory("wwwroot", + new[] + { + new ManifestFile("jquery.validate.js","wwwroot.jquery.validate.js") + }) + })); + // Act + var entry = manifest.ResolveEntry(path); + + // Assert + Assert.NotNull(entry); + } + } +} diff --git a/src/FileProviders/Embedded/test/Manifest/ManifestEntryTests.cs b/src/FileProviders/Embedded/test/Manifest/ManifestEntryTests.cs new file mode 100644 index 000000000000..dc1a7e1cdda2 --- /dev/null +++ b/src/FileProviders/Embedded/test/Manifest/ManifestEntryTests.cs @@ -0,0 +1,113 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Xunit; + +namespace Microsoft.Extensions.FileProviders.Embedded.Manifest +{ + public class ManifestEntryTests + { + [Fact] + public void TraversingAFile_ReturnsUnknownPath() + { + // Arrange + var file = new ManifestFile("a", "a.b.c"); + + // Act + var result = file.Traverse("."); + + // Assert + Assert.Equal(ManifestEntry.UnknownPath, result); + } + + [Fact] + public void TraversingANonExistingFile_ReturnsUnknownPath() + { + // Arrange + var directory = ManifestDirectory.CreateDirectory("a", Array.Empty()); + + // Act + var result = directory.Traverse("missing.txt"); + + // Assert + Assert.Equal(ManifestEntry.UnknownPath, result); + } + + [Fact] + public void TraversingWithDot_ReturnsSelf() + { + // Arrange + var directory = ManifestDirectory.CreateDirectory("a", Array.Empty()); + + // Act + var result = directory.Traverse("."); + + // Assert + Assert.Same(directory, result); + } + + [Fact] + public void TraversingWithDotDot_ReturnsParent() + { + // Arrange + var childDirectory = ManifestDirectory.CreateDirectory("b", Array.Empty()); + var directory = ManifestDirectory.CreateDirectory("a", new[] { childDirectory }); + + // Act + var result = childDirectory.Traverse(".."); + + // Assert + Assert.Equal(directory, result); + } + + [Fact] + public void TraversingRootDirectoryWithDotDot_ReturnsSinkDirectory() + { + // Arrange + var directory = ManifestDirectory.CreateRootDirectory(Array.Empty()); + + // Act + var result = directory.Traverse(".."); + + // Assert + Assert.Equal(ManifestEntry.UnknownPath, result); + } + + [Fact] + public void ScopingAFolderAndTryingToGetAScopedFile_ReturnsSinkDirectory() + { + // Arrange + var directory = ManifestDirectory.CreateRootDirectory(new[] { + ManifestDirectory.CreateDirectory("a", + new[] { new ManifestFile("test1.txt", "text.txt") }), + ManifestDirectory.CreateDirectory("b", + new[] { new ManifestFile("test2.txt", "test2.txt") }) }); + + var newRoot = ((ManifestDirectory)directory.Traverse("a")).ToRootDirectory(); + + // Act + var result = newRoot.Traverse("../b/test.txt"); + + // Assert + Assert.Same(ManifestEntry.UnknownPath, result); + } + + [Theory] + [InlineData("..")] + [InlineData(".")] + [InlineData("file.txt")] + [InlineData("folder")] + public void TraversingUnknownPath_ReturnsSinkDirectory(string path) + { + // Arrange + var directory = ManifestEntry.UnknownPath; + + // Act + var result = directory.Traverse(path); + + // Assert + Assert.Equal(ManifestEntry.UnknownPath, result); + } + } +} diff --git a/src/FileProviders/Embedded/test/Manifest/ManifestParserTests.cs b/src/FileProviders/Embedded/test/Manifest/ManifestParserTests.cs new file mode 100644 index 000000000000..e4edc8bde0e4 --- /dev/null +++ b/src/FileProviders/Embedded/test/Manifest/ManifestParserTests.cs @@ -0,0 +1,116 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Xunit; + +namespace Microsoft.Extensions.FileProviders.Embedded.Manifest +{ + public class ManifestParserTests + { + [Fact] + public void Parse_UsesDefaultManifestNameForManifest() + { + // Arrange + var assembly = new TestAssembly( + TestEntry.Directory("unused", + TestEntry.File("sample.txt"))); + + // Act + var manifest = ManifestParser.Parse(assembly); + + // Assert + Assert.NotNull(manifest); + } + + [Fact] + public void Parse_FindsManifestWithCustomName() + { + // Arrange + var assembly = new TestAssembly( + TestEntry.Directory("unused", + TestEntry.File("sample.txt")), + manifestName: "Manifest.xml"); + + // Act + var manifest = ManifestParser.Parse(assembly, "Manifest.xml"); + + // Assert + Assert.NotNull(manifest); + } + + [Fact] + public void Parse_ThrowsForEntriesWithDifferentCasing() + { + // Arrange + var assembly = new TestAssembly( + TestEntry.Directory("unused", + TestEntry.File("sample.txt"), + TestEntry.File("SAMPLE.TXT"))); + + // Act & Assert + Assert.Throws(() => ManifestParser.Parse(assembly)); + } + + [Theory] + [MemberData(nameof(MalformedManifests))] + public void Parse_ThrowsForInvalidManifests(string invalidManifest) + { + // Arrange + var assembly = new TestAssembly(invalidManifest); + + // Act & Assert + Assert.Throws(() => ManifestParser.Parse(assembly)); + } + + public static TheoryData MalformedManifests => + new TheoryData + { + "", + "", + "", + "2.0", + "2.0", + @"1.0 +path", + + @"1.0 +", + + @"1.0 +sample.txt", + + @"1.0 +", + + @"1.0 +" + }; + + [Theory] + [MemberData(nameof(ManifestsWithAdditionalData))] + public void Parse_IgnoresAdditionalDataOnFileAndDirectoryNodes(string manifest) + { + // Arrange + var assembly = new TestAssembly(manifest); + + // Act + var result = ManifestParser.Parse(assembly); + + // Assert + Assert.NotNull(result); + } + + public static TheoryData ManifestsWithAdditionalData => + new TheoryData + { + @"1.0 +", + + @"1.0 + +path1234 +" + }; + } +} diff --git a/src/FileProviders/Embedded/test/Manifest/TestEntry.cs b/src/FileProviders/Embedded/test/Manifest/TestEntry.cs new file mode 100644 index 000000000000..aaaf881469f9 --- /dev/null +++ b/src/FileProviders/Embedded/test/Manifest/TestEntry.cs @@ -0,0 +1,41 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Linq; +using System.Xml.Linq; + +namespace Microsoft.Extensions.FileProviders.Embedded.Manifest +{ + class TestEntry + { + public bool IsFile => ResourcePath != null; + public string Name { get; set; } + public TestEntry[] Children { get; set; } + public string ResourcePath { get; set; } + + public static TestEntry Directory(string name, params TestEntry[] entries) => + new TestEntry() { Name = name, Children = entries }; + + public static TestEntry File(string name, string path = null) => + new TestEntry() { Name = name, ResourcePath = path ?? name }; + + public XElement ToXElement() => IsFile ? + new XElement("File", new XAttribute("Name", Name), new XElement("ResourcePath", ResourcePath)) : + new XElement("Directory", new XAttribute("Name", Name), Children.Select(c => c.ToXElement())); + + public IEnumerable GetFiles() + { + if (IsFile) + { + return Enumerable.Empty(); + } + + var files = Children.Where(c => c.IsFile).ToArray(); + var otherFiles = Children.Where(c => !c.IsFile).SelectMany(d => d.GetFiles()).ToArray(); + + return files.Concat(otherFiles).ToArray(); + } + + } +} diff --git a/src/FileProviders/Embedded/test/ManifestEmbeddedFileProviderTests.cs b/src/FileProviders/Embedded/test/ManifestEmbeddedFileProviderTests.cs new file mode 100644 index 000000000000..a973c9df003f --- /dev/null +++ b/src/FileProviders/Embedded/test/ManifestEmbeddedFileProviderTests.cs @@ -0,0 +1,428 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; +using System.Linq; +using Microsoft.Extensions.FileProviders.Embedded.Manifest; +using Xunit; + +namespace Microsoft.Extensions.FileProviders +{ + public class ManifestEmbeddedFileProviderTests + { + [Fact] + public void GetFileInfo_CanResolveSimpleFiles() + { + // Arrange + var assembly = new TestAssembly( + TestEntry.Directory("unused", + TestEntry.File("jquery.validate.js"), + TestEntry.File("jquery.min.js"), + TestEntry.File("site.css"))); + + // Act + var provider = new ManifestEmbeddedFileProvider(assembly); + + // Assert + var jqueryValidate = provider.GetFileInfo("jquery.validate.js"); + Assert.True(jqueryValidate.Exists); + Assert.False(jqueryValidate.IsDirectory); + Assert.Equal("jquery.validate.js", jqueryValidate.Name); + Assert.Null(jqueryValidate.PhysicalPath); + Assert.Equal(0, jqueryValidate.Length); + + var jqueryMin = provider.GetFileInfo("jquery.min.js"); + Assert.True(jqueryMin.Exists); + Assert.False(jqueryMin.IsDirectory); + Assert.Equal("jquery.min.js", jqueryMin.Name); + Assert.Null(jqueryMin.PhysicalPath); + Assert.Equal(0, jqueryMin.Length); + + var siteCss = provider.GetFileInfo("site.css"); + Assert.True(siteCss.Exists); + Assert.False(siteCss.IsDirectory); + Assert.Equal("site.css", siteCss.Name); + Assert.Null(siteCss.PhysicalPath); + Assert.Equal(0, siteCss.Length); + } + + [Fact] + public void GetFileInfo_CanResolveFilesInsideAFolder() + { + // Arrange + var assembly = new TestAssembly( + TestEntry.Directory("unused", + TestEntry.Directory("wwwroot", + TestEntry.File("jquery.validate.js"), + TestEntry.File("jquery.min.js"), + TestEntry.File("site.css")))); + + // Act + var provider = new ManifestEmbeddedFileProvider(assembly); + + // Assert + var jqueryValidate = provider.GetFileInfo(Path.Combine("wwwroot", "jquery.validate.js")); + Assert.True(jqueryValidate.Exists); + Assert.False(jqueryValidate.IsDirectory); + Assert.Equal("jquery.validate.js", jqueryValidate.Name); + Assert.Null(jqueryValidate.PhysicalPath); + Assert.Equal(0, jqueryValidate.Length); + + var jqueryMin = provider.GetFileInfo(Path.Combine("wwwroot", "jquery.min.js")); + Assert.True(jqueryMin.Exists); + Assert.False(jqueryMin.IsDirectory); + Assert.Equal("jquery.min.js", jqueryMin.Name); + Assert.Null(jqueryMin.PhysicalPath); + Assert.Equal(0, jqueryMin.Length); + + var siteCss = provider.GetFileInfo(Path.Combine("wwwroot", "site.css")); + Assert.True(siteCss.Exists); + Assert.False(siteCss.IsDirectory); + Assert.Equal("site.css", siteCss.Name); + Assert.Null(siteCss.PhysicalPath); + Assert.Equal(0, siteCss.Length); + } + + [Fact] + public void GetFileInfo_ResolveNonExistingFile_ReturnsNotFoundFileInfo() + { + // Arrange + var assembly = new TestAssembly( + TestEntry.Directory("unused", + TestEntry.Directory("wwwroot", + TestEntry.File("jquery.validate.js"), + TestEntry.File("jquery.min.js"), + TestEntry.File("site.css")))); + + var provider = new ManifestEmbeddedFileProvider(assembly); + + // Act + var file = provider.GetFileInfo("some/non/existing/file.txt"); + + // Assert + Assert.IsType(file); + } + + [Fact] + public void GetFileInfo_ResolveNonExistingDirectory_ReturnsNotFoundFileInfo() + { + // Arrange + var assembly = new TestAssembly( + TestEntry.Directory("unused", + TestEntry.Directory("wwwroot", + TestEntry.File("jquery.validate.js"), + TestEntry.File("jquery.min.js"), + TestEntry.File("site.css")))); + + var provider = new ManifestEmbeddedFileProvider(assembly); + + // Act + var file = provider.GetFileInfo("some"); + + // Assert + Assert.IsType(file); + } + + [Fact] + public void GetFileInfo_ResolveExistingDirectory_ReturnsNotFoundFileInfo() + { + // Arrange + var assembly = new TestAssembly( + TestEntry.Directory("unused", + TestEntry.Directory("wwwroot", + TestEntry.File("jquery.validate.js"), + TestEntry.File("jquery.min.js"), + TestEntry.File("site.css")))); + + var provider = new ManifestEmbeddedFileProvider(assembly); + + // Act + var file = provider.GetFileInfo("wwwroot"); + + // Assert + Assert.IsType(file); + } + + [Theory] + [InlineData("WWWROOT", "JQUERY.VALIDATE.JS")] + [InlineData("WwWRoOT", "JQuERY.VALiDATE.js")] + public void GetFileInfo_ResolvesFiles_WithDifferentCasing(string folder, string file) + { + // Arrange + var assembly = new TestAssembly( + TestEntry.Directory("unused", + TestEntry.Directory("wwwroot", + TestEntry.File("jquery.validate.js"), + TestEntry.File("jquery.min.js"), + TestEntry.File("site.css")))); + + // Act + var provider = new ManifestEmbeddedFileProvider(assembly); + + // Assert + var jqueryValidate = provider.GetFileInfo(Path.Combine(folder, file)); + Assert.True(jqueryValidate.Exists); + Assert.False(jqueryValidate.IsDirectory); + Assert.Equal("jquery.validate.js", jqueryValidate.Name); + Assert.Null(jqueryValidate.PhysicalPath); + Assert.Equal(0, jqueryValidate.Length); + } + + [Fact] + public void GetFileInfo_AllowsLeadingDots_OnThePath() + { + // Arrange + var assembly = new TestAssembly( + TestEntry.Directory("unused", + TestEntry.Directory("wwwroot", + TestEntry.File("jquery.validate.js"), + TestEntry.File("jquery.min.js"), + TestEntry.File("site.css")))); + + // Act + var provider = new ManifestEmbeddedFileProvider(assembly); + + // Assert + var jqueryValidate = provider.GetFileInfo(Path.Combine(".", "wwwroot", "jquery.validate.js")); + Assert.True(jqueryValidate.Exists); + Assert.False(jqueryValidate.IsDirectory); + Assert.Equal("jquery.validate.js", jqueryValidate.Name); + Assert.Null(jqueryValidate.PhysicalPath); + Assert.Equal(0, jqueryValidate.Length); + } + + [Fact] + public void GetFileInfo_EscapingFromTheRootFolder_ReturnsNotFound() + { + // Arrange + var assembly = new TestAssembly( + TestEntry.Directory("unused", + TestEntry.Directory("wwwroot", + TestEntry.File("jquery.validate.js"), + TestEntry.File("jquery.min.js"), + TestEntry.File("site.css")))); + + // Act + var provider = new ManifestEmbeddedFileProvider(assembly); + + // Assert + var jqueryValidate = provider.GetFileInfo(Path.Combine("..", "wwwroot", "jquery.validate.js")); + Assert.IsType(jqueryValidate); + } + + [Theory] + [InlineData("wwwroot/jquery?validate.js")] + [InlineData("wwwroot/jquery*validate.js")] + [InlineData("wwwroot/jquery:validate.js")] + [InlineData("wwwroot/jqueryvalidate.js")] + [InlineData("wwwroot/jquery\0validate.js")] + public void GetFileInfo_ReturnsNotFoundfileInfo_ForPathsWithInvalidCharacters(string path) + { + // Arrange + var assembly = new TestAssembly( + TestEntry.Directory("unused", + TestEntry.Directory("wwwroot", + TestEntry.File("jquery.validate.js"), + TestEntry.File("jquery.min.js"), + TestEntry.File("site.css")))); + + // Act + var provider = new ManifestEmbeddedFileProvider(assembly); + + // Assert + var file = provider.GetFileInfo(path); + Assert.IsType(file); + Assert.Equal(path, file.Name); + } + + [Fact] + public void GetDirectoryContents_CanEnumerateExistingFolders() + { + // Arrange + var assembly = new TestAssembly( + TestEntry.Directory("unused", + TestEntry.Directory("wwwroot", + TestEntry.File("jquery.validate.js"), + TestEntry.File("jquery.min.js"), + TestEntry.File("site.css")))); + + var provider = new ManifestEmbeddedFileProvider(assembly); + + var expectedContents = new[] + { + CreateTestFileInfo("jquery.validate.js"), + CreateTestFileInfo("jquery.min.js"), + CreateTestFileInfo("site.css") + }; + + // Act + var contents = provider.GetDirectoryContents("wwwroot").ToArray(); + + // Assert + Assert.Equal(expectedContents, contents, FileInfoComparer.Instance); + } + + [Fact] + public void GetDirectoryContents_EnumeratesOnlyAGivenLevel() + { + // Arrange + var assembly = new TestAssembly( + TestEntry.Directory("unused", + TestEntry.Directory("wwwroot", + TestEntry.File("jquery.validate.js"), + TestEntry.File("jquery.min.js"), + TestEntry.File("site.css")))); + + var provider = new ManifestEmbeddedFileProvider(assembly); + + var expectedContents = new[] + { + CreateTestFileInfo("wwwroot", isDirectory: true) + }; + + // Act + var contents = provider.GetDirectoryContents(".").ToArray(); + + // Assert + Assert.Equal(expectedContents, contents, FileInfoComparer.Instance); + } + + [Fact] + public void GetDirectoryContents_EnumeratesFilesAndDirectoriesOnAGivenPath() + { + // Arrange + var assembly = new TestAssembly( + TestEntry.Directory("unused", + TestEntry.Directory("wwwroot"), + TestEntry.File("site.css"))); + + var provider = new ManifestEmbeddedFileProvider(assembly); + + var expectedContents = new[] + { + CreateTestFileInfo("wwwroot", isDirectory: true), + CreateTestFileInfo("site.css") + }; + + // Act + var contents = provider.GetDirectoryContents(".").ToArray(); + + // Assert + Assert.Equal(expectedContents, contents, FileInfoComparer.Instance); + } + + [Fact] + public void GetDirectoryContents_ReturnsNoEntries_ForNonExistingDirectories() + { + // Arrange + var assembly = new TestAssembly( + TestEntry.Directory("unused", + TestEntry.Directory("wwwroot"), + TestEntry.File("site.css"))); + + var provider = new ManifestEmbeddedFileProvider(assembly); + + // Act + var contents = provider.GetDirectoryContents("non-existing"); + + // Assert + Assert.IsType(contents); + } + + [Fact] + public void GetDirectoryContents_ReturnsNoEntries_ForFilePaths() + { + // Arrange + var assembly = new TestAssembly( + TestEntry.Directory("unused", + TestEntry.Directory("wwwroot"), + TestEntry.File("site.css"))); + + var provider = new ManifestEmbeddedFileProvider(assembly); + + // Act + var contents = provider.GetDirectoryContents("site.css"); + + // Assert + Assert.IsType(contents); + } + + [Theory] + [InlineData("wwwro*t")] + [InlineData("wwwro?t")] + [InlineData("wwwro:t")] + [InlineData("wwwrot")] + [InlineData("wwwro\0t")] + public void GetDirectoryContents_ReturnsNotFoundDirectoryContents_ForPathsWithInvalidCharacters(string path) + { + // Arrange + var assembly = new TestAssembly( + TestEntry.Directory("unused", + TestEntry.Directory("wwwroot", + TestEntry.File("jquery.validate.js"), + TestEntry.File("jquery.min.js"), + TestEntry.File("site.css")))); + + // Act + var provider = new ManifestEmbeddedFileProvider(assembly); + + // Assert + var directory = provider.GetDirectoryContents(path); + Assert.IsType(directory); + } + + [Fact] + public void Contructor_CanScopeManifestToAFolder() + { + // Arrange + var assembly = new TestAssembly( + TestEntry.Directory("unused", + TestEntry.Directory("wwwroot", + TestEntry.File("jquery.validate.js")), + TestEntry.File("site.css"))); + + var provider = new ManifestEmbeddedFileProvider(assembly); + var scopedProvider = new ManifestEmbeddedFileProvider(assembly, provider.Manifest.Scope("wwwroot"), DateTimeOffset.UtcNow); + + // Act + var jqueryValidate = scopedProvider.GetFileInfo("jquery.validate.js"); + + // Assert + Assert.True(jqueryValidate.Exists); + Assert.False(jqueryValidate.IsDirectory); + Assert.Equal("jquery.validate.js", jqueryValidate.Name); + Assert.Null(jqueryValidate.PhysicalPath); + Assert.Equal(0, jqueryValidate.Length); + } + + [Theory] + [InlineData("wwwroot/jquery.validate.js")] + [InlineData("../wwwroot/jquery.validate.js")] + [InlineData("site.css")] + [InlineData("../site.css")] + public void ScopedFileProvider_DoesNotReturnFilesOutOfScope(string path) + { + // Arrange + var assembly = new TestAssembly( + TestEntry.Directory("unused", + TestEntry.Directory("wwwroot", + TestEntry.File("jquery.validate.js")), + TestEntry.File("site.css"))); + + var provider = new ManifestEmbeddedFileProvider(assembly); + var scopedProvider = new ManifestEmbeddedFileProvider(assembly, provider.Manifest.Scope("wwwroot"), DateTimeOffset.UtcNow); + + // Act + var jqueryValidate = scopedProvider.GetFileInfo(path); + + // Assert + Assert.IsType(jqueryValidate); + } + + private IFileInfo CreateTestFileInfo(string name, bool isDirectory = false) => + new TestFileInfo(name, isDirectory); + } +} diff --git a/src/FileProviders/Embedded/test/Microsoft.Extensions.FileProviders.Embedded.Tests.csproj b/src/FileProviders/Embedded/test/Microsoft.Extensions.FileProviders.Embedded.Tests.csproj new file mode 100644 index 000000000000..8703c9c508ed --- /dev/null +++ b/src/FileProviders/Embedded/test/Microsoft.Extensions.FileProviders.Embedded.Tests.csproj @@ -0,0 +1,15 @@ + + + + $(StandardTestTfms) + + + + + + + + + + + diff --git a/src/FileProviders/Embedded/test/Resources/File.txt b/src/FileProviders/Embedded/test/Resources/File.txt new file mode 100644 index 000000000000..d498f3700625 --- /dev/null +++ b/src/FileProviders/Embedded/test/Resources/File.txt @@ -0,0 +1 @@ +Resources-Hello \ No newline at end of file diff --git a/src/FileProviders/Embedded/test/Resources/ResourcesInSubdirectory/File3.txt b/src/FileProviders/Embedded/test/Resources/ResourcesInSubdirectory/File3.txt new file mode 100644 index 000000000000..8651decea694 --- /dev/null +++ b/src/FileProviders/Embedded/test/Resources/ResourcesInSubdirectory/File3.txt @@ -0,0 +1 @@ +Hello3 diff --git a/src/FileProviders/Embedded/test/TestAssembly.cs b/src/FileProviders/Embedded/test/TestAssembly.cs new file mode 100644 index 000000000000..4917bd60ed0a --- /dev/null +++ b/src/FileProviders/Embedded/test/TestAssembly.cs @@ -0,0 +1,69 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Xml; +using System.Xml.Linq; +using Microsoft.Extensions.FileProviders.Embedded.Manifest; + +namespace Microsoft.Extensions.FileProviders +{ + internal class TestAssembly : Assembly + { + public TestAssembly(string manifest, string manifestName = "Microsoft.Extensions.FileProviders.Embedded.Manifest.xml") + { + ManifestStream = new MemoryStream(); + using (var writer = new StreamWriter(ManifestStream, Encoding.UTF8, 1024, leaveOpen: true)) + { + writer.Write(manifest); + } + + ManifestStream.Seek(0, SeekOrigin.Begin); + ManifestName = manifestName; + } + + public TestAssembly(TestEntry entry, string manifestName = "Microsoft.Extensions.FileProviders.Embedded.Manifest.xml") + { + ManifestName = manifestName; + + var manifest = new XDocument( + new XDeclaration("1.0", "utf-8", "yes"), + new XElement("Manifest", + new XElement("ManifestVersion", "1.0"), + new XElement("FileSystem", entry.Children.Select(c => c.ToXElement())))); + + ManifestStream = new MemoryStream(); + using (var writer = XmlWriter.Create(ManifestStream, new XmlWriterSettings { CloseOutput = false })) + { + manifest.WriteTo(writer); + } + + ManifestStream.Seek(0, SeekOrigin.Begin); + Files = entry.GetFiles().Select(f => f.ResourcePath).ToArray(); + } + + public string ManifestName { get; } + public MemoryStream ManifestStream { get; private set; } + public string[] Files { get; private set; } + + public override Stream GetManifestResourceStream(string name) + { + if (string.Equals(ManifestName, name)) + { + return ManifestStream; + } + + return Files.Contains(name) ? Stream.Null : null; + } + + public override string Location => null; + + public override AssemblyName GetName() + { + return new AssemblyName("TestAssembly"); + } + } +} diff --git a/src/FileProviders/Embedded/test/TestFileInfo.cs b/src/FileProviders/Embedded/test/TestFileInfo.cs new file mode 100644 index 000000000000..d410a3b5e782 --- /dev/null +++ b/src/FileProviders/Embedded/test/TestFileInfo.cs @@ -0,0 +1,34 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; + +namespace Microsoft.Extensions.FileProviders +{ + internal class TestFileInfo : IFileInfo + { + private readonly string _name; + private readonly bool _isDirectory; + + public TestFileInfo(string name, bool isDirectory) + { + _name = name; + _isDirectory = isDirectory; + } + + public bool Exists => true; + + public long Length => _isDirectory ? -1 : 0; + + public string PhysicalPath => null; + + public string Name => _name; + + public DateTimeOffset LastModified => throw new NotImplementedException(); + + public bool IsDirectory => _isDirectory; + + public Stream CreateReadStream() => Stream.Null; + } +} diff --git a/src/FileProviders/Embedded/test/sub/File2.txt b/src/FileProviders/Embedded/test/sub/File2.txt new file mode 100644 index 000000000000..e8ecfad88419 --- /dev/null +++ b/src/FileProviders/Embedded/test/sub/File2.txt @@ -0,0 +1 @@ +Hello2 diff --git a/src/FileProviders/Embedded/test/sub/dir/File3.txt b/src/FileProviders/Embedded/test/sub/dir/File3.txt new file mode 100644 index 0000000000000000000000000000000000000000..49cc8ef0e116cef009fe0bd72473a964bbd07f9b GIT binary patch literal 6 NcmezWkC%aq0RRg=0u=xN literal 0 HcmV?d00001 diff --git a/src/FileProviders/Manifest.MSBuildTask/src/EmbeddedItem.cs b/src/FileProviders/Manifest.MSBuildTask/src/EmbeddedItem.cs new file mode 100644 index 000000000000..c2dbd58ed222 --- /dev/null +++ b/src/FileProviders/Manifest.MSBuildTask/src/EmbeddedItem.cs @@ -0,0 +1,21 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.Extensions.FileProviders.Embedded.Manifest.Task +{ + public class EmbeddedItem : IEquatable + { + public string ManifestFilePath { get; set; } + + public string AssemblyResourceName { get; set; } + + public bool Equals(EmbeddedItem other) => + string.Equals(ManifestFilePath, other?.ManifestFilePath, StringComparison.Ordinal) && + string.Equals(AssemblyResourceName, other?.AssemblyResourceName, StringComparison.Ordinal); + + public override bool Equals(object obj) => Equals(obj as EmbeddedItem); + public override int GetHashCode() => ManifestFilePath.GetHashCode() ^ AssemblyResourceName.GetHashCode(); + } +} diff --git a/src/FileProviders/Manifest.MSBuildTask/src/Entry.cs b/src/FileProviders/Manifest.MSBuildTask/src/Entry.cs new file mode 100644 index 000000000000..40c815fde4d7 --- /dev/null +++ b/src/FileProviders/Manifest.MSBuildTask/src/Entry.cs @@ -0,0 +1,120 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Internal +{ + /// + /// This type is for internal uses only and is not meant to be consumed by any other library. + /// + [DebuggerDisplay("{Name,nq}")] + public class Entry : IEquatable + { + public bool IsFile { get; private set; } + + public string Name { get; private set; } + + public string AssemblyResourceName { get; private set; } + + public ISet Children { get; } = new SortedSet(NameComparer.Instance); + + public static Entry Directory(string name) => + new Entry { Name = name }; + + public static Entry File(string name, string assemblyResourceName) => + new Entry { Name = name, AssemblyResourceName = assemblyResourceName, IsFile = true }; + + internal void AddChild(Entry child) + { + if (IsFile) + { + throw new InvalidOperationException("Tried to add children to a file."); + } + + if (Children.Contains(child)) + { + throw new InvalidOperationException($"An item with the name '{child.Name}' already exists."); + } + + Children.Add(child); + } + + internal Entry GetDirectory(string currentSegment) + { + if (IsFile) + { + throw new InvalidOperationException("Tried to get a directory from a file."); + } + + foreach (var child in Children) + { + if (child.HasName(currentSegment)) + { + if (child.IsFile) + { + throw new InvalidOperationException("Tried to find a directory but found a file instead"); + } + else + { + return child; + } + } + } + + return null; + } + + public bool Equals(Entry other) + { + if (other == null || !other.HasName(Name) || other.IsFile != IsFile) + { + return false; + } + + if (IsFile) + { + return string.Equals(other.AssemblyResourceName, AssemblyResourceName, StringComparison.Ordinal); + } + else + { + return SameChildren(Children, other.Children); + } + } + + private bool HasName(string currentSegment) + { + return string.Equals(Name, currentSegment, StringComparison.Ordinal); + } + + private bool SameChildren(ISet left, ISet right) + { + if (left.Count != right.Count) + { + return false; + } + + var le = left.GetEnumerator(); + var re = right.GetEnumerator(); + while (le.MoveNext() && re.MoveNext()) + { + if (!le.Current.Equals(re.Current)) + { + return false; + } + } + + return true; + } + + private class NameComparer : IComparer + { + public static NameComparer Instance { get; } = new NameComparer(); + + public int Compare(Entry x, Entry y) => + string.Compare(x?.Name, y?.Name, StringComparison.Ordinal); + } + } +} diff --git a/src/FileProviders/Manifest.MSBuildTask/src/GenerateEmbeddedResourcesManifest.cs b/src/FileProviders/Manifest.MSBuildTask/src/GenerateEmbeddedResourcesManifest.cs new file mode 100644 index 000000000000..3a62d3d5e341 --- /dev/null +++ b/src/FileProviders/Manifest.MSBuildTask/src/GenerateEmbeddedResourcesManifest.cs @@ -0,0 +1,104 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; +using System.Linq; +using System.Text; +using System.Xml; +using Microsoft.Build.Framework; + +namespace Microsoft.Extensions.FileProviders.Embedded.Manifest.Task +{ + /// + /// Task for generating a manifest file out of the embedded resources in an + /// assembly. + /// + public class GenerateEmbeddedResourcesManifest : Microsoft.Build.Utilities.Task + { + private const string LogicalName = "LogicalName"; + private const string ManifestResourceName = "ManifestResourceName"; + private const string TargetPath = "TargetPath"; + + [Required] + public ITaskItem[] EmbeddedFiles { get; set; } + + [Required] + public string ManifestFile { get; set; } + + /// + public override bool Execute() + { + var processedItems = CreateEmbeddedItems(EmbeddedFiles); + + var manifest = BuildManifest(processedItems); + + var document = manifest.ToXmlDocument(); + + var settings = new XmlWriterSettings() + { + Encoding = Encoding.UTF8, + CloseOutput = true + }; + + using (var xmlWriter = GetXmlWriter(settings)) + { + document.WriteTo(xmlWriter); + } + + return true; + } + + protected virtual XmlWriter GetXmlWriter(XmlWriterSettings settings) + { + if (settings == null) + { + throw new ArgumentNullException(nameof(settings)); + } + + var fileStream = new FileStream(ManifestFile, FileMode.Create); + return XmlWriter.Create(fileStream, settings); + } + + public EmbeddedItem[] CreateEmbeddedItems(params ITaskItem[] items) + { + if (items == null) + { + throw new ArgumentNullException(nameof(items)); + } + + return items.Select(er => new EmbeddedItem + { + ManifestFilePath = GetManifestPath(er), + AssemblyResourceName = GetAssemblyResourceName(er) + }).ToArray(); + } + + public Manifest BuildManifest(EmbeddedItem[] processedItems) + { + if (processedItems == null) + { + throw new ArgumentNullException(nameof(processedItems)); + } + + var manifest = new Manifest(); + foreach (var item in processedItems) + { + manifest.AddElement(item.ManifestFilePath, item.AssemblyResourceName); + } + + return manifest; + } + + private string GetManifestPath(ITaskItem taskItem) => string.Equals(taskItem.GetMetadata(LogicalName), taskItem.GetMetadata(ManifestResourceName)) ? + taskItem.GetMetadata(TargetPath) : + NormalizePath(taskItem.GetMetadata(LogicalName)); + + private string GetAssemblyResourceName(ITaskItem taskItem) => string.Equals(taskItem.GetMetadata(LogicalName), taskItem.GetMetadata(ManifestResourceName)) ? + taskItem.GetMetadata(ManifestResourceName) : + taskItem.GetMetadata(LogicalName); + + private string NormalizePath(string path) => Path.DirectorySeparatorChar == '\\' ? + path.Replace("/", "\\") : path.Replace("\\", "/"); + } +} diff --git a/src/FileProviders/Manifest.MSBuildTask/src/Manifest.cs b/src/FileProviders/Manifest.MSBuildTask/src/Manifest.cs new file mode 100644 index 000000000000..86e99477ff47 --- /dev/null +++ b/src/FileProviders/Manifest.MSBuildTask/src/Manifest.cs @@ -0,0 +1,85 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Xml.Linq; +using Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Internal; + +namespace Microsoft.Extensions.FileProviders.Embedded.Manifest.Task +{ + public class Manifest + { + public Entry Root { get; set; } = Entry.Directory(""); + + public void AddElement(string originalPath, string assemblyResourceName) + { + if (originalPath == null) + { + throw new System.ArgumentNullException(nameof(originalPath)); + } + + if (assemblyResourceName == null) + { + throw new System.ArgumentNullException(nameof(assemblyResourceName)); + } + + var paths = originalPath.Split(Path.DirectorySeparatorChar); + var current = Root; + for (int i = 0; i < paths.Length - 1; i++) + { + var currentSegment = paths[i]; + var next = current.GetDirectory(currentSegment); + if (next == null) + { + next = Entry.Directory(currentSegment); + current.AddChild(next); + } + current = next; + } + + current.AddChild(Entry.File(paths[paths.Length - 1], assemblyResourceName)); + } + + public XDocument ToXmlDocument() + { + var document = new XDocument(new XDeclaration("1.0", "utf-8", "yes")); + var root = new XElement(ElementNames.Root, + new XElement(ElementNames.ManifestVersion, "1.0"), + new XElement(ElementNames.FileSystem, + Root.Children.Select(e => BuildNode(e)))); + + document.Add(root); + + return document; + } + + private XElement BuildNode(Entry entry) + { + if (entry.IsFile) + { + return new XElement(ElementNames.File, + new XAttribute(ElementNames.Name, entry.Name), + new XElement(ElementNames.ResourcePath, entry.AssemblyResourceName)); + } + else + { + var directory = new XElement(ElementNames.Directory, new XAttribute(ElementNames.Name, entry.Name)); + directory.Add(entry.Children.Select(c => BuildNode(c))); + return directory; + } + } + + private class ElementNames + { + public static readonly string Directory = "Directory"; + public static readonly string Name = "Name"; + public static readonly string FileSystem = "FileSystem"; + public static readonly string Root = "Manifest"; + public static readonly string File = "File"; + public static readonly string ResourcePath = "ResourcePath"; + public static readonly string ManifestVersion = "ManifestVersion"; + } + } +} diff --git a/src/FileProviders/Manifest.MSBuildTask/src/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.csproj b/src/FileProviders/Manifest.MSBuildTask/src/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.csproj new file mode 100644 index 000000000000..70784650de3a --- /dev/null +++ b/src/FileProviders/Manifest.MSBuildTask/src/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.csproj @@ -0,0 +1,25 @@ + + + + MSBuild task to generate a manifest that can be used by Microsoft.Extensions.FileProviders.Embedded to preserve + metadata of the files embedded in the assembly at compilation time. + netstandard1.5;net461 + false + false + false + false + + + + + + + + + + + + + + + diff --git a/src/FileProviders/Manifest.MSBuildTask/test/GenerateEmbeddedResourcesManifestTest.cs b/src/FileProviders/Manifest.MSBuildTask/test/GenerateEmbeddedResourcesManifestTest.cs new file mode 100644 index 000000000000..c7285913af11 --- /dev/null +++ b/src/FileProviders/Manifest.MSBuildTask/test/GenerateEmbeddedResourcesManifestTest.cs @@ -0,0 +1,388 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Xml; +using System.Xml.Linq; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Internal; +using Xunit; + +namespace Microsoft.Extensions.FileProviders.Embedded.Manifest.Task +{ + public class GenerateEmbeddedResourcesManifestTest + { + [Fact] + public void CreateEmbeddedItems_MapsMetadataFromEmbeddedResources_UsesTheTargetPath() + { + // Arrange + var task = new TestGenerateEmbeddedResourcesManifest(); + var embeddedFiles = CreateEmbeddedResource( + CreateMetadata(@"lib\js\jquery.validate.js")); + + var expectedItems = new[] + { + CreateEmbeddedItem(@"lib\js\jquery.validate.js","lib.js.jquery.validate.js") + }; + + // Act + var embeddedItems = task.CreateEmbeddedItems(embeddedFiles); + + // Assert + Assert.Equal(expectedItems, embeddedItems); + } + + [Fact] + public void CreateEmbeddedItems_MapsMetadataFromEmbeddedResources_WithLogicalName() + { + // Arrange + var task = new TestGenerateEmbeddedResourcesManifest(); + var DirectorySeparator = (Path.DirectorySeparatorChar == '\\' ? '/' : '\\'); + var embeddedFiles = CreateEmbeddedResource( + CreateMetadata("site.css", null, "site.css"), + CreateMetadata("lib/jquery.validate.js", null, $"dist{DirectorySeparator}jquery.validate.js")); + + var expectedItems = new[] + { + CreateEmbeddedItem("site.css","site.css"), + CreateEmbeddedItem(Path.Combine("dist","jquery.validate.js"),$"dist{DirectorySeparator}jquery.validate.js") + }; + + // Act + var embeddedItems = task.CreateEmbeddedItems(embeddedFiles); + + // Assert + Assert.Equal(expectedItems, embeddedItems); + } + + [Fact] + public void BuildManifest_CanCreatesManifest_ForTopLevelFiles() + { + // Arrange + var task = new TestGenerateEmbeddedResourcesManifest(); + var embeddedFiles = CreateEmbeddedResource( + CreateMetadata("jquery.validate.js"), + CreateMetadata("jquery.min.js"), + CreateMetadata("Site.css")); + + var manifestFiles = task.CreateEmbeddedItems(embeddedFiles); + + var expectedManifest = new Manifest() + { + Root = Entry.Directory("").AddRange( + Entry.File("jquery.validate.js", "jquery.validate.js"), + Entry.File("jquery.min.js", "jquery.min.js"), + Entry.File("Site.css", "Site.css")) + }; + + // Act + var manifest = task.BuildManifest(manifestFiles); + + // Assert + Assert.Equal(expectedManifest, manifest, ManifestComparer.Instance); + } + + [Fact] + public void BuildManifest_CanCreatesManifest_ForFilesWithinAFolder() + { + // Arrange + var task = new TestGenerateEmbeddedResourcesManifest(); + var embeddedFiles = CreateEmbeddedResource( + CreateMetadata(Path.Combine("wwwroot", "js", "jquery.validate.js")), + CreateMetadata(Path.Combine("wwwroot", "js", "jquery.min.js")), + CreateMetadata(Path.Combine("wwwroot", "css", "Site.css")), + CreateMetadata(Path.Combine("Areas", "Identity", "Views", "Account", "Index.cshtml"))); + + var manifestFiles = task.CreateEmbeddedItems(embeddedFiles); + + var expectedManifest = new Manifest() + { + Root = Entry.Directory("").AddRange( + Entry.Directory("wwwroot").AddRange( + Entry.Directory("js").AddRange( + Entry.File("jquery.validate.js", "wwwroot.js.jquery.validate.js"), + Entry.File("jquery.min.js", "wwwroot.js.jquery.min.js")), + Entry.Directory("css").AddRange( + Entry.File("Site.css", "wwwroot.css.Site.css"))), + Entry.Directory("Areas").AddRange( + Entry.Directory("Identity").AddRange( + Entry.Directory("Views").AddRange( + Entry.Directory("Account").AddRange( + Entry.File("Index.cshtml", "Areas.Identity.Views.Account.Index.cshtml")))))) + }; + + // Act + var manifest = task.BuildManifest(manifestFiles); + + // Assert + Assert.Equal(expectedManifest, manifest, ManifestComparer.Instance); + } + + [Fact] + public void BuildManifest_RespectsEntriesWithLogicalName() + { + // Arrange + var task = new TestGenerateEmbeddedResourcesManifest(); + var embeddedFiles = CreateEmbeddedResource( + CreateMetadata("jquery.validate.js", null, @"wwwroot\lib\js\jquery.validate.js"), + CreateMetadata("jquery.min.js", null, @"wwwroot\lib/js\jquery.min.js"), + CreateMetadata("Site.css", null, "wwwroot/lib/css/site.css")); + var manifestFiles = task.CreateEmbeddedItems(embeddedFiles); + + var expectedManifest = new Manifest() + { + Root = Entry.Directory("").AddRange( + Entry.Directory("wwwroot").AddRange( + Entry.Directory("lib").AddRange( + Entry.Directory("js").AddRange( + Entry.File("jquery.validate.js", @"wwwroot\lib\js\jquery.validate.js"), + Entry.File("jquery.min.js", @"wwwroot\lib/js\jquery.min.js")), + Entry.Directory("css").AddRange( + Entry.File("site.css", "wwwroot/lib/css/site.css"))))) + }; + + // Act + var manifest = task.BuildManifest(manifestFiles); + + // Assert + Assert.Equal(expectedManifest, manifest, ManifestComparer.Instance); + } + + [Fact] + public void BuildManifest_SupportsFilesAndFoldersWithDifferentCasing() + { + // Arrange + var task = new TestGenerateEmbeddedResourcesManifest(); + var embeddedFiles = CreateEmbeddedResource( + CreateMetadata(Path.Combine("A", "b", "c.txt")), + CreateMetadata(Path.Combine("A", "B", "c.txt")), + CreateMetadata(Path.Combine("A", "B", "C.txt")), + CreateMetadata(Path.Combine("A", "b", "C.txt")), + CreateMetadata(Path.Combine("A", "d")), + CreateMetadata(Path.Combine("A", "D", "e.txt"))); + + var manifestFiles = task.CreateEmbeddedItems(embeddedFiles); + + var expectedManifest = new Manifest() + { + Root = Entry.Directory("").AddRange( + Entry.Directory("A").AddRange( + Entry.Directory("b").AddRange( + Entry.File("c.txt", @"A.b.c.txt"), + Entry.File("C.txt", @"A.b.C.txt")), + Entry.Directory("B").AddRange( + Entry.File("c.txt", @"A.B.c.txt"), + Entry.File("C.txt", @"A.B.C.txt")), + Entry.Directory("D").AddRange( + Entry.File("e.txt", "A.D.e.txt")), + Entry.File("d", "A.d"))) + }; + + // Act + var manifest = task.BuildManifest(manifestFiles); + + // Assert + Assert.Equal(expectedManifest, manifest, ManifestComparer.Instance); + } + + [Fact] + public void BuildManifest_ThrowsInvalidOperationException_WhenTryingToAddAFileWithTheSameNameAsAFolder() + { + // Arrange + var task = new TestGenerateEmbeddedResourcesManifest(); + var embeddedFiles = CreateEmbeddedResource( + CreateMetadata(Path.Combine("A", "b", "c.txt")), + CreateMetadata(Path.Combine("A", "b"))); + + var manifestFiles = task.CreateEmbeddedItems(embeddedFiles); + + // Act & Assert + Assert.Throws(() => task.BuildManifest(manifestFiles)); + } + + [Fact] + public void BuildManifest_ThrowsInvalidOperationException_WhenTryingToAddAFolderWithTheSameNameAsAFile() + { + // Arrange + var task = new TestGenerateEmbeddedResourcesManifest(); + var embeddedFiles = CreateEmbeddedResource( + CreateMetadata(Path.Combine("A", "b")), + CreateMetadata(Path.Combine("A", "b", "c.txt"))); + + var manifestFiles = task.CreateEmbeddedItems(embeddedFiles); + + // Act & Assert + Assert.Throws(() => task.BuildManifest(manifestFiles)); + } + + [Fact] + public void ToXmlDocument_GeneratesTheCorrectXmlDocument() + { + // Arrange + var manifest = new Manifest() + { + Root = Entry.Directory("").AddRange( + Entry.Directory("A").AddRange( + Entry.Directory("b").AddRange( + Entry.File("c.txt", @"A.b.c.txt"), + Entry.File("C.txt", @"A.b.C.txt")), + Entry.Directory("B").AddRange( + Entry.File("c.txt", @"A.B.c.txt"), + Entry.File("C.txt", @"A.B.C.txt")), + Entry.Directory("D").AddRange( + Entry.File("e.txt", "A.D.e.txt")), + Entry.File("d", "A.d"))) + }; + + var expectedDocument = new XDocument( + new XDeclaration("1.0", "utf-8", "yes"), + new XElement("Manifest", + new XElement("ManifestVersion", "1.0"), + new XElement("FileSystem", + new XElement("Directory", new XAttribute("Name", "A"), + new XElement("Directory", new XAttribute("Name", "B"), + new XElement("File", new XAttribute("Name", "C.txt"), new XElement("ResourcePath", "A.B.C.txt")), + new XElement("File", new XAttribute("Name", "c.txt"), new XElement("ResourcePath", "A.B.c.txt"))), + new XElement("Directory", new XAttribute("Name", "D"), + new XElement("File", new XAttribute("Name", "e.txt"), new XElement("ResourcePath", "A.D.e.txt"))), + new XElement("Directory", new XAttribute("Name", "b"), + new XElement("File", new XAttribute("Name", "C.txt"), new XElement("ResourcePath", "A.b.C.txt")), + new XElement("File", new XAttribute("Name", "c.txt"), new XElement("ResourcePath", "A.b.c.txt"))), + new XElement("File", new XAttribute("Name", "d"), new XElement("ResourcePath", "A.d")))))); + + // Act + var document = manifest.ToXmlDocument(); + + // Assert + Assert.Equal(expectedDocument.ToString(), document.ToString()); + } + + [Fact] + public void Execute_WritesManifest_ToOutputFile() + { + // Arrange + var task = new TestGenerateEmbeddedResourcesManifest(); + var embeddedFiles = CreateEmbeddedResource( + CreateMetadata(Path.Combine("A", "b", "c.txt")), + CreateMetadata(Path.Combine("A", "B", "c.txt")), + CreateMetadata(Path.Combine("A", "B", "C.txt")), + CreateMetadata(Path.Combine("A", "b", "C.txt")), + CreateMetadata(Path.Combine("A", "d")), + CreateMetadata(Path.Combine("A", "D", "e.txt"))); + + task.EmbeddedFiles = embeddedFiles; + task.ManifestFile = Path.Combine("obj", "debug", "netstandard2.0"); + + var expectedDocument = new XDocument( + new XDeclaration("1.0", "utf-8", "yes"), + new XElement("Manifest", + new XElement("ManifestVersion", "1.0"), + new XElement("FileSystem", + new XElement("Directory", new XAttribute("Name", "A"), + new XElement("Directory", new XAttribute("Name", "B"), + new XElement("File", new XAttribute("Name", "C.txt"), new XElement("ResourcePath", "A.B.C.txt")), + new XElement("File", new XAttribute("Name", "c.txt"), new XElement("ResourcePath", "A.B.c.txt"))), + new XElement("Directory", new XAttribute("Name", "D"), + new XElement("File", new XAttribute("Name", "e.txt"), new XElement("ResourcePath", "A.D.e.txt"))), + new XElement("Directory", new XAttribute("Name", "b"), + new XElement("File", new XAttribute("Name", "C.txt"), new XElement("ResourcePath", "A.b.C.txt")), + new XElement("File", new XAttribute("Name", "c.txt"), new XElement("ResourcePath", "A.b.c.txt"))), + new XElement("File", new XAttribute("Name", "d"), new XElement("ResourcePath", "A.d")))))); + + var expectedOutput = new MemoryStream(); + var writer = XmlWriter.Create(expectedOutput, new XmlWriterSettings { Encoding = Encoding.UTF8 }); + expectedDocument.WriteTo(writer); + writer.Flush(); + expectedOutput.Seek(0, SeekOrigin.Begin); + + // Act + task.Execute(); + + // Assert + task.Output.Seek(0, SeekOrigin.Begin); + using (var expectedReader = new StreamReader(expectedOutput)) + { + using (var reader = new StreamReader(task.Output)) + { + Assert.Equal(expectedReader.ReadToEnd(), reader.ReadToEnd()); + } + } + } + + private EmbeddedItem CreateEmbeddedItem(string manifestPath, string assemblyName) => + new EmbeddedItem + { + ManifestFilePath = manifestPath, + AssemblyResourceName = assemblyName + }; + + + public class TestGenerateEmbeddedResourcesManifest + : GenerateEmbeddedResourcesManifest + { + public TestGenerateEmbeddedResourcesManifest() + : this(new MemoryStream()) + { + } + + public TestGenerateEmbeddedResourcesManifest(Stream output) + { + Output = output; + } + + public Stream Output { get; } + + protected override XmlWriter GetXmlWriter(XmlWriterSettings settings) + { + settings.CloseOutput = false; + return XmlWriter.Create(Output, settings); + } + } + + private ITaskItem[] CreateEmbeddedResource(params IDictionary[] files) => + files.Select(f => CreateTaskItem(f)).ToArray(); + + private ITaskItem CreateTaskItem(IDictionary metadata) + { + var result = new TaskItem(); + foreach (var kvp in metadata) + { + result.SetMetadata(kvp.Key, kvp.Value); + } + + return result; + } + + private static IDictionary + CreateMetadata( + string targetPath, + string manifestResourceName = null, + string logicalName = null) => + new Dictionary + { + ["TargetPath"] = targetPath, + ["ManifestResourceName"] = manifestResourceName ?? targetPath.Replace("/", ".").Replace("\\", "."), + ["LogicalName"] = logicalName ?? targetPath.Replace("/", ".").Replace("\\", "."), + }; + + private class ManifestComparer : IEqualityComparer + { + public static IEqualityComparer Instance { get; } = new ManifestComparer(); + + public bool Equals(Manifest x, Manifest y) + { + return x.Root.Equals(y.Root); + } + + public int GetHashCode(Manifest obj) + { + return obj.Root.GetHashCode(); + } + } + } +} diff --git a/src/FileProviders/Manifest.MSBuildTask/test/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Test.csproj b/src/FileProviders/Manifest.MSBuildTask/test/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Test.csproj new file mode 100644 index 000000000000..b06f1b21761c --- /dev/null +++ b/src/FileProviders/Manifest.MSBuildTask/test/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Test.csproj @@ -0,0 +1,11 @@ + + + + $(StandardTestTfms) + + + + + + + diff --git a/src/FileProviders/Manifest.MSBuildTask/test/SetExtensions.cs b/src/FileProviders/Manifest.MSBuildTask/test/SetExtensions.cs new file mode 100644 index 000000000000..6b2c83a875fc --- /dev/null +++ b/src/FileProviders/Manifest.MSBuildTask/test/SetExtensions.cs @@ -0,0 +1,20 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Internal; + +namespace Microsoft.Extensions.FileProviders.Embedded.Manifest.Task +{ + internal static class SetExtensions + { + public static Entry AddRange(this Entry source, params Entry[] elements) + { + foreach (var element in elements) + { + source.Children.Add(element); + } + + return source; + } + } +} From aee5320ae91471899dc7f4dc08979096c3216612 Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Fri, 2 Nov 2018 17:25:46 -0700 Subject: [PATCH 007/183] Organize packages into different output paths based on whether they are "product" packages or not \n\nCommit migrated from https://github.com/dotnet/extensions/commit/dbb97a2e1376867fa0a64053fb1be7ab8c426540 --- src/ObjectPool/Directory.Build.props | 7 +++++++ src/Shared/CertificateGeneration/Directory.Build.props | 1 - 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 src/ObjectPool/Directory.Build.props diff --git a/src/ObjectPool/Directory.Build.props b/src/ObjectPool/Directory.Build.props new file mode 100644 index 000000000000..f25c1d90ce80 --- /dev/null +++ b/src/ObjectPool/Directory.Build.props @@ -0,0 +1,7 @@ + + + + + true + + diff --git a/src/Shared/CertificateGeneration/Directory.Build.props b/src/Shared/CertificateGeneration/Directory.Build.props index 63355f6faf94..f3f6fc4b350c 100644 --- a/src/Shared/CertificateGeneration/Directory.Build.props +++ b/src/Shared/CertificateGeneration/Directory.Build.props @@ -1,5 +1,4 @@ - From 376a6c9953e9e71d893e8be8dc36a018eb8929f1 Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Mon, 5 Nov 2018 13:09:10 -0800 Subject: [PATCH 008/183] Merge branch 'release/2.1' into release/2.2 \n\nCommit migrated from https://github.com/dotnet/extensions/commit/2d152804642f41aaeacc09307bcebb065131e75d --- .../src/Microsoft.Extensions.FileProviders.Embedded.csproj | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj index d7ca20b469de..ec2c10b56939 100644 --- a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj @@ -12,6 +12,12 @@ + + + + + + From 360b19ca09c7d4cbdc2d6a551ea0b60dc747e9eb Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Mon, 5 Nov 2018 13:12:14 -0800 Subject: [PATCH 009/183] Merge branch 'release/2.2' \n\nCommit migrated from https://github.com/dotnet/extensions/commit/04bac2f79c39eecba1ea29d035dc9a40e37167d6 --- .../Microsoft.Extensions.FileProviders.Embedded.csproj | 9 ++++++++- .../Microsoft.Extensions.FileProviders.Embedded.nuspec | 4 ++-- .../Microsoft.Extensions.FileProviders.Embedded.props | 2 +- ...ensions.FileProviders.Embedded.Manifest.Task.csproj | 10 ++-------- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj index ec2c10b56939..55284793fbcc 100644 --- a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj @@ -14,7 +14,7 @@ - + @@ -41,10 +41,17 @@ OutputDocumentation=@(DocumentationProjectOutputGroupOutput); +<<<<<<< HEAD:src/FS.Embedded/FS.Embedded.csproj + TaskAssemblyNetStandard=..\FS.Embedded.Manifest.Task\bin\$(Configuration)\netstandard2.0\$(AssemblyName).Manifest.Task.dll; + TaskSymbolNetStandard=..\FS.Embedded.Manifest.Task\bin\$(Configuration)\netstandard2.0\$(AssemblyName).Manifest.Task.pdb; + TaskAssemblyNet461=..\FS.Embedded.Manifest.Task\bin\$(Configuration)\net461\$(AssemblyName).Manifest.Task.dll; + TaskSymbolNet461=..\FS.Embedded.Manifest.Task\bin\$(Configuration)\net461\$(AssemblyName).Manifest.Task.pdb; +======= TaskAssemblyNetStandard=..\..\Manifest.MSBuildTask\src\bin\$(Configuration)\netstandard1.5\$(AssemblyName).Manifest.Task.dll; TaskSymbolNetStandard=..\..\Manifest.MSBuildTask\src\bin\$(Configuration)\netstandard1.5\$(AssemblyName).Manifest.Task.pdb; TaskAssemblyNet461=..\..\Manifest.MSBuildTask\src\bin\$(Configuration)\net461\$(AssemblyName).Manifest.Task.dll; TaskSymbolNet461=..\..\Manifest.MSBuildTask\src\bin\$(Configuration)\net461\$(AssemblyName).Manifest.Task.pdb; +>>>>>>> m22:src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.nuspec b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.nuspec index 0cc5ed823a6e..af7feca8fdc0 100644 --- a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.nuspec +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.nuspec @@ -25,8 +25,8 @@ - - + + diff --git a/src/FileProviders/Embedded/src/build/netstandard2.0/Microsoft.Extensions.FileProviders.Embedded.props b/src/FileProviders/Embedded/src/build/netstandard2.0/Microsoft.Extensions.FileProviders.Embedded.props index e913e173214f..66ea7b5c22cd 100644 --- a/src/FileProviders/Embedded/src/build/netstandard2.0/Microsoft.Extensions.FileProviders.Embedded.props +++ b/src/FileProviders/Embedded/src/build/netstandard2.0/Microsoft.Extensions.FileProviders.Embedded.props @@ -5,7 +5,7 @@ - <_FileProviderTaskFolder Condition="'$(MSBuildRuntimeType)' == 'Core'">netstandard1.5 + <_FileProviderTaskFolder Condition="'$(MSBuildRuntimeType)' == 'Core'">netstandard2.0 <_FileProviderTaskFolder Condition="'$(MSBuildRuntimeType)' != 'Core'">net461 <_FileProviderTaskAssembly>$(MSBuildThisFileDirectory)..\..\tasks\$(_FileProviderTaskFolder)\Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.dll diff --git a/src/FileProviders/Manifest.MSBuildTask/src/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.csproj b/src/FileProviders/Manifest.MSBuildTask/src/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.csproj index 70784650de3a..018cc98a8e65 100644 --- a/src/FileProviders/Manifest.MSBuildTask/src/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.csproj +++ b/src/FileProviders/Manifest.MSBuildTask/src/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.csproj @@ -3,23 +3,17 @@ MSBuild task to generate a manifest that can be used by Microsoft.Extensions.FileProviders.Embedded to preserve metadata of the files embedded in the assembly at compilation time. - netstandard1.5;net461 + netstandard2.0 false false false false - + - - - - - - From 09e84b256c4f994781e196e72e92fb8ff28c80d1 Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Mon, 5 Nov 2018 15:12:53 -0800 Subject: [PATCH 010/183] Reorganize source code in preparation to move into aspnet/Extensions Prior to reorganization, this source code was found in https://github.com/aspnet/Configuration/tree/dotnet/extensions@ef29dc86b970893147fd2a27d527f5a907af9fdd \n\nCommit migrated from https://github.com/dotnet/extensions/commit/a8900855a44534fe5ba28bfd28883c0e38d8da07 --- .../Directory.Build.props | 8 + ...eyPerFileConfigurationBuilderExtensions.cs | 42 +++ .../src/KeyPerFileConfigurationProvider.cs | 72 ++++ .../src/KeyPerFileConfigurationSource.cs | 48 +++ ...Extensions.Configuration.KeyPerFile.csproj | 14 + src/Configuration.KeyPerFile/src/README.md | 2 + .../test/KeyPerFileTests.cs | 308 ++++++++++++++++++ ...ions.Configuration.KeyPerFile.Tests.csproj | 11 + 8 files changed, 505 insertions(+) create mode 100644 src/Configuration.KeyPerFile/Directory.Build.props create mode 100644 src/Configuration.KeyPerFile/src/KeyPerFileConfigurationBuilderExtensions.cs create mode 100644 src/Configuration.KeyPerFile/src/KeyPerFileConfigurationProvider.cs create mode 100644 src/Configuration.KeyPerFile/src/KeyPerFileConfigurationSource.cs create mode 100644 src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj create mode 100644 src/Configuration.KeyPerFile/src/README.md create mode 100644 src/Configuration.KeyPerFile/test/KeyPerFileTests.cs create mode 100644 src/Configuration.KeyPerFile/test/Microsoft.Extensions.Configuration.KeyPerFile.Tests.csproj diff --git a/src/Configuration.KeyPerFile/Directory.Build.props b/src/Configuration.KeyPerFile/Directory.Build.props new file mode 100644 index 000000000000..2082380096b1 --- /dev/null +++ b/src/Configuration.KeyPerFile/Directory.Build.props @@ -0,0 +1,8 @@ + + + + + true + configuration + + diff --git a/src/Configuration.KeyPerFile/src/KeyPerFileConfigurationBuilderExtensions.cs b/src/Configuration.KeyPerFile/src/KeyPerFileConfigurationBuilderExtensions.cs new file mode 100644 index 000000000000..435ef9e1554d --- /dev/null +++ b/src/Configuration.KeyPerFile/src/KeyPerFileConfigurationBuilderExtensions.cs @@ -0,0 +1,42 @@ +using System; +using System.IO; +using Microsoft.Extensions.Configuration.KeyPerFile; +using Microsoft.Extensions.FileProviders; + +namespace Microsoft.Extensions.Configuration +{ + /// + /// Extension methods for registering with . + /// + public static class KeyPerFileConfigurationBuilderExtensions + { + /// + /// Adds configuration using files from a directory. File names are used as the key, + /// file contents are used as the value. + /// + /// The to add to. + /// The path to the directory. + /// Whether the directory is optional. + /// The . + public static IConfigurationBuilder AddKeyPerFile(this IConfigurationBuilder builder, string directoryPath, bool optional) + => builder.AddKeyPerFile(source => + { + // Only try to set the file provider if its not optional or the directory exists + if (!optional || Directory.Exists(directoryPath)) + { + source.FileProvider = new PhysicalFileProvider(directoryPath); + } + source.Optional = optional; + }); + + /// + /// Adds configuration using files from a directory. File names are used as the key, + /// file contents are used as the value. + /// + /// The to add to. + /// Configures the source. + /// The . + public static IConfigurationBuilder AddKeyPerFile(this IConfigurationBuilder builder, Action configureSource) + => builder.Add(configureSource); + } +} diff --git a/src/Configuration.KeyPerFile/src/KeyPerFileConfigurationProvider.cs b/src/Configuration.KeyPerFile/src/KeyPerFileConfigurationProvider.cs new file mode 100644 index 000000000000..474889574411 --- /dev/null +++ b/src/Configuration.KeyPerFile/src/KeyPerFileConfigurationProvider.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Microsoft.Extensions.Configuration.KeyPerFile +{ + /// + /// A that uses a directory's files as configuration key/values. + /// + public class KeyPerFileConfigurationProvider : ConfigurationProvider + { + KeyPerFileConfigurationSource Source { get; set; } + + /// + /// Initializes a new instance. + /// + /// The settings. + public KeyPerFileConfigurationProvider(KeyPerFileConfigurationSource source) + => Source = source ?? throw new ArgumentNullException(nameof(source)); + + private static string NormalizeKey(string key) + => key.Replace("__", ConfigurationPath.KeyDelimiter); + + private static string TrimNewLine(string value) + => value.EndsWith(Environment.NewLine) + ? value.Substring(0, value.Length - Environment.NewLine.Length) + : value; + + /// + /// Loads the docker secrets. + /// + public override void Load() + { + Data = new Dictionary(StringComparer.OrdinalIgnoreCase); + + if (Source.FileProvider == null) + { + if (Source.Optional) + { + return; + } + else + { + throw new DirectoryNotFoundException("A non-null file provider for the directory is required when this source is not optional."); + } + } + + var directory = Source.FileProvider.GetDirectoryContents("/"); + if (!directory.Exists && !Source.Optional) + { + throw new DirectoryNotFoundException("The root directory for the FileProvider doesn't exist and is not optional."); + } + + foreach (var file in directory) + { + if (file.IsDirectory) + { + continue; + } + + using (var stream = file.CreateReadStream()) + using (var streamReader = new StreamReader(stream)) + { + if (Source.IgnoreCondition == null || !Source.IgnoreCondition(file.Name)) + { + Data.Add(NormalizeKey(file.Name), TrimNewLine(streamReader.ReadToEnd())); + } + } + } + } + } +} diff --git a/src/Configuration.KeyPerFile/src/KeyPerFileConfigurationSource.cs b/src/Configuration.KeyPerFile/src/KeyPerFileConfigurationSource.cs new file mode 100644 index 000000000000..c32e948e828f --- /dev/null +++ b/src/Configuration.KeyPerFile/src/KeyPerFileConfigurationSource.cs @@ -0,0 +1,48 @@ +using System; +using System.IO; +using Microsoft.Extensions.FileProviders; + +namespace Microsoft.Extensions.Configuration.KeyPerFile +{ + /// + /// An used to configure . + /// + public class KeyPerFileConfigurationSource : IConfigurationSource + { + /// + /// Constructor; + /// + public KeyPerFileConfigurationSource() + => IgnoreCondition = s => IgnorePrefix != null && s.StartsWith(IgnorePrefix); + + /// + /// The FileProvider whos root "/" directory files will be used as configuration data. + /// + public IFileProvider FileProvider { get; set; } + + /// + /// Files that start with this prefix will be excluded. + /// Defaults to "ignore.". + /// + public string IgnorePrefix { get; set; } = "ignore."; + + /// + /// Used to determine if a file should be ignored using its name. + /// Defaults to using the IgnorePrefix. + /// + public Func IgnoreCondition { get; set; } + + /// + /// If false, will throw if the directory doesn't exist. + /// + public bool Optional { get; set; } + + /// + /// Builds the for this source. + /// + /// The . + /// A + public IConfigurationProvider Build(IConfigurationBuilder builder) + => new KeyPerFileConfigurationProvider(this); + } +} diff --git a/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj b/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj new file mode 100644 index 000000000000..4eb19f329375 --- /dev/null +++ b/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj @@ -0,0 +1,14 @@ + + + + Configuration provider that uses files in a directory for Microsoft.Extensions.Configuration. + netstandard2.0 + false + + + + + + + + diff --git a/src/Configuration.KeyPerFile/src/README.md b/src/Configuration.KeyPerFile/src/README.md new file mode 100644 index 000000000000..29952e9139dc --- /dev/null +++ b/src/Configuration.KeyPerFile/src/README.md @@ -0,0 +1,2 @@ + +This is a configuration provider that uses a directory's files as data. A file's name is the key and the contents are the value. diff --git a/src/Configuration.KeyPerFile/test/KeyPerFileTests.cs b/src/Configuration.KeyPerFile/test/KeyPerFileTests.cs new file mode 100644 index 000000000000..d55387a40418 --- /dev/null +++ b/src/Configuration.KeyPerFile/test/KeyPerFileTests.cs @@ -0,0 +1,308 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.Primitives; +using Xunit; + +namespace Microsoft.Extensions.Configuration.KeyPerFile.Test +{ + public class KeyPerFileTests + { + [Fact] + public void DoesNotThrowWhenOptionalAndNoSecrets() + { + new ConfigurationBuilder().AddKeyPerFile(o => o.Optional = true).Build(); + } + + [Fact] + public void DoesNotThrowWhenOptionalAndDirectoryDoesntExist() + { + new ConfigurationBuilder().AddKeyPerFile("nonexistent", true).Build(); + } + + [Fact] + public void ThrowsWhenNotOptionalAndDirectoryDoesntExist() + { + var e = Assert.Throws(() => new ConfigurationBuilder().AddKeyPerFile("nonexistent", false).Build()); + Assert.Contains("The directory name", e.Message); + } + + [Fact] + public void CanLoadMultipleSecrets() + { + var testFileProvider = new TestFileProvider( + new TestFile("Secret1", "SecretValue1"), + new TestFile("Secret2", "SecretValue2")); + + var config = new ConfigurationBuilder() + .AddKeyPerFile(o => o.FileProvider = testFileProvider) + .Build(); + + Assert.Equal("SecretValue1", config["Secret1"]); + Assert.Equal("SecretValue2", config["Secret2"]); + } + + [Fact] + public void CanLoadMultipleSecretsWithDirectory() + { + var testFileProvider = new TestFileProvider( + new TestFile("Secret1", "SecretValue1"), + new TestFile("Secret2", "SecretValue2"), + new TestFile("directory")); + + var config = new ConfigurationBuilder() + .AddKeyPerFile(o => o.FileProvider = testFileProvider) + .Build(); + + Assert.Equal("SecretValue1", config["Secret1"]); + Assert.Equal("SecretValue2", config["Secret2"]); + } + + [Fact] + public void CanLoadNestedKeys() + { + var testFileProvider = new TestFileProvider( + new TestFile("Secret0__Secret1__Secret2__Key", "SecretValue2"), + new TestFile("Secret0__Secret1__Key", "SecretValue1"), + new TestFile("Secret0__Key", "SecretValue0")); + + var config = new ConfigurationBuilder() + .AddKeyPerFile(o => o.FileProvider = testFileProvider) + .Build(); + + Assert.Equal("SecretValue0", config["Secret0:Key"]); + Assert.Equal("SecretValue1", config["Secret0:Secret1:Key"]); + Assert.Equal("SecretValue2", config["Secret0:Secret1:Secret2:Key"]); + } + + [Fact] + public void CanIgnoreFilesWithDefault() + { + var testFileProvider = new TestFileProvider( + new TestFile("ignore.Secret0", "SecretValue0"), + new TestFile("ignore.Secret1", "SecretValue1"), + new TestFile("Secret2", "SecretValue2")); + + var config = new ConfigurationBuilder() + .AddKeyPerFile(o => o.FileProvider = testFileProvider) + .Build(); + + Assert.Null(config["ignore.Secret0"]); + Assert.Null(config["ignore.Secret1"]); + Assert.Equal("SecretValue2", config["Secret2"]); + } + + [Fact] + public void CanTurnOffDefaultIgnorePrefixWithCondition() + { + var testFileProvider = new TestFileProvider( + new TestFile("ignore.Secret0", "SecretValue0"), + new TestFile("ignore.Secret1", "SecretValue1"), + new TestFile("Secret2", "SecretValue2")); + + var config = new ConfigurationBuilder() + .AddKeyPerFile(o => + { + o.FileProvider = testFileProvider; + o.IgnoreCondition = null; + }) + .Build(); + + Assert.Equal("SecretValue0", config["ignore.Secret0"]); + Assert.Equal("SecretValue1", config["ignore.Secret1"]); + Assert.Equal("SecretValue2", config["Secret2"]); + } + + [Fact] + public void CanIgnoreAllWithCondition() + { + var testFileProvider = new TestFileProvider( + new TestFile("Secret0", "SecretValue0"), + new TestFile("Secret1", "SecretValue1"), + new TestFile("Secret2", "SecretValue2")); + + var config = new ConfigurationBuilder() + .AddKeyPerFile(o => + { + o.FileProvider = testFileProvider; + o.IgnoreCondition = s => true; + }) + .Build(); + + Assert.Empty(config.AsEnumerable()); + } + + [Fact] + public void CanIgnoreFilesWithCustomIgnore() + { + var testFileProvider = new TestFileProvider( + new TestFile("meSecret0", "SecretValue0"), + new TestFile("meSecret1", "SecretValue1"), + new TestFile("Secret2", "SecretValue2")); + + var config = new ConfigurationBuilder() + .AddKeyPerFile(o => + { + o.FileProvider = testFileProvider; + o.IgnorePrefix = "me"; + }) + .Build(); + + Assert.Null(config["meSecret0"]); + Assert.Null(config["meSecret1"]); + Assert.Equal("SecretValue2", config["Secret2"]); + } + + [Fact] + public void CanUnIgnoreDefaultFiles() + { + var testFileProvider = new TestFileProvider( + new TestFile("ignore.Secret0", "SecretValue0"), + new TestFile("ignore.Secret1", "SecretValue1"), + new TestFile("Secret2", "SecretValue2")); + + var config = new ConfigurationBuilder() + .AddKeyPerFile(o => + { + o.FileProvider = testFileProvider; + o.IgnorePrefix = null; + }) + .Build(); + + Assert.Equal("SecretValue0", config["ignore.Secret0"]); + Assert.Equal("SecretValue1", config["ignore.Secret1"]); + Assert.Equal("SecretValue2", config["Secret2"]); + } + } + + class TestFileProvider : IFileProvider + { + IDirectoryContents _contents; + + public TestFileProvider(params IFileInfo[] files) + { + _contents = new TestDirectoryContents(files); + } + + public IDirectoryContents GetDirectoryContents(string subpath) + { + return _contents; + } + + public IFileInfo GetFileInfo(string subpath) + { + throw new NotImplementedException(); + } + + public IChangeToken Watch(string filter) + { + throw new NotImplementedException(); + } + } + + class TestDirectoryContents : IDirectoryContents + { + List _list; + + public TestDirectoryContents(params IFileInfo[] files) + { + _list = new List(files); + } + + public bool Exists + { + get + { + return true; + } + } + + public IEnumerator GetEnumerator() + { + return _list.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + //TODO: Probably need a directory and file type. + class TestFile : IFileInfo + { + private string _name; + private string _contents; + + public bool Exists + { + get + { + return true; + } + } + + public bool IsDirectory + { + get; + } + + public DateTimeOffset LastModified + { + get + { + throw new NotImplementedException(); + } + } + + public long Length + { + get + { + throw new NotImplementedException(); + } + } + + public string Name + { + get + { + return _name; + } + } + + public string PhysicalPath + { + get + { + throw new NotImplementedException(); + } + } + + public TestFile(string name) + { + _name = name; + IsDirectory = true; + } + + public TestFile(string name, string contents) + { + _name = name; + _contents = contents; + } + + public Stream CreateReadStream() + { + if(IsDirectory) + { + throw new InvalidOperationException("Cannot create stream from directory"); + } + + return new MemoryStream(Encoding.UTF8.GetBytes(_contents)); + } + } +} \ No newline at end of file diff --git a/src/Configuration.KeyPerFile/test/Microsoft.Extensions.Configuration.KeyPerFile.Tests.csproj b/src/Configuration.KeyPerFile/test/Microsoft.Extensions.Configuration.KeyPerFile.Tests.csproj new file mode 100644 index 000000000000..634056a345bd --- /dev/null +++ b/src/Configuration.KeyPerFile/test/Microsoft.Extensions.Configuration.KeyPerFile.Tests.csproj @@ -0,0 +1,11 @@ + + + + $(StandardTestTfms) + + + + + + + From ac89e3e9bfc5eec640c9a2fd68416a443ac667c3 Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Mon, 5 Nov 2018 16:20:37 -0800 Subject: [PATCH 011/183] Merge branch 'release/2.1' into release/2.2 \n\nCommit migrated from https://github.com/dotnet/extensions/commit/d94eb17013c924e4b932b89bbbe729fa2bf1899e --- src/Configuration.KeyPerFile/test/KeyPerFileTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Configuration.KeyPerFile/test/KeyPerFileTests.cs b/src/Configuration.KeyPerFile/test/KeyPerFileTests.cs index d55387a40418..d409c0eab087 100644 --- a/src/Configuration.KeyPerFile/test/KeyPerFileTests.cs +++ b/src/Configuration.KeyPerFile/test/KeyPerFileTests.cs @@ -28,7 +28,7 @@ public void DoesNotThrowWhenOptionalAndDirectoryDoesntExist() public void ThrowsWhenNotOptionalAndDirectoryDoesntExist() { var e = Assert.Throws(() => new ConfigurationBuilder().AddKeyPerFile("nonexistent", false).Build()); - Assert.Contains("The directory name", e.Message); + Assert.Contains("The path must be absolute.", e.Message); } [Fact] @@ -305,4 +305,4 @@ public Stream CreateReadStream() return new MemoryStream(Encoding.UTF8.GetBytes(_contents)); } } -} \ No newline at end of file +} From aa26d515be7c279263919b2c547d2153f6c5f88b Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Fri, 9 Nov 2018 15:53:41 -0800 Subject: [PATCH 012/183] Reorganize source code in preparation to move into aspnet/Extensions Prior to reorganization, this source code was found in https://github.com/aspnet/HtmlAbstractions/tree/dotnet/extensions@252ae0c434d93f5bfaf949c35edb9ef59730d0a3 \n\nCommit migrated from https://github.com/dotnet/extensions/commit/6c2bf2943469b133f25cb84363358cca55058466 --- .../src/EncoderServiceCollectionExtensions.cs | 83 +++ .../Microsoft.Extensions.WebEncoders.csproj | 18 + .../src/Testing/HtmlTestEncoder.cs | 104 ++++ .../src/Testing/JavaScriptTestEncoder.cs | 104 ++++ src/WebEncoders/src/Testing/UrlTestEncoder.cs | 104 ++++ src/WebEncoders/src/WebEncoderOptions.cs | 21 + src/WebEncoders/src/baseline.netcore.json | 564 ++++++++++++++++++ ...EncoderServiceCollectionExtensionsTests.cs | 90 +++ src/WebEncoders/test/HtmlTestEncoderTest.cs | 26 + ...rosoft.Extensions.WebEncoders.Tests.csproj | 12 + 10 files changed, 1126 insertions(+) create mode 100644 src/WebEncoders/src/EncoderServiceCollectionExtensions.cs create mode 100644 src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj create mode 100644 src/WebEncoders/src/Testing/HtmlTestEncoder.cs create mode 100644 src/WebEncoders/src/Testing/JavaScriptTestEncoder.cs create mode 100644 src/WebEncoders/src/Testing/UrlTestEncoder.cs create mode 100644 src/WebEncoders/src/WebEncoderOptions.cs create mode 100644 src/WebEncoders/src/baseline.netcore.json create mode 100644 src/WebEncoders/test/EncoderServiceCollectionExtensionsTests.cs create mode 100644 src/WebEncoders/test/HtmlTestEncoderTest.cs create mode 100755 src/WebEncoders/test/Microsoft.Extensions.WebEncoders.Tests.csproj diff --git a/src/WebEncoders/src/EncoderServiceCollectionExtensions.cs b/src/WebEncoders/src/EncoderServiceCollectionExtensions.cs new file mode 100644 index 000000000000..72f5e369a1cc --- /dev/null +++ b/src/WebEncoders/src/EncoderServiceCollectionExtensions.cs @@ -0,0 +1,83 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Text.Encodings.Web; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Options; +using Microsoft.Extensions.WebEncoders; + +namespace Microsoft.Extensions.DependencyInjection +{ + /// + /// Extension methods for setting up web encoding services in an . + /// + public static class EncoderServiceCollectionExtensions + { + /// + /// Adds , and + /// to the specified . + /// + /// The . + /// The so that additional calls can be chained. + public static IServiceCollection AddWebEncoders(this IServiceCollection services) + { + if (services == null) + { + throw new ArgumentNullException(nameof(services)); + } + + services.AddOptions(); + + // Register the default encoders + // We want to call the 'Default' property getters lazily since they perform static caching + services.TryAddSingleton( + CreateFactory(() => HtmlEncoder.Default, settings => HtmlEncoder.Create(settings))); + services.TryAddSingleton( + CreateFactory(() => JavaScriptEncoder.Default, settings => JavaScriptEncoder.Create(settings))); + services.TryAddSingleton( + CreateFactory(() => UrlEncoder.Default, settings => UrlEncoder.Create(settings))); + + return services; + } + + /// + /// Adds , and + /// to the specified . + /// + /// The . + /// An to configure the provided . + /// The so that additional calls can be chained. + public static IServiceCollection AddWebEncoders(this IServiceCollection services, Action setupAction) + { + if (services == null) + { + throw new ArgumentNullException(nameof(services)); + } + + if (setupAction == null) + { + throw new ArgumentNullException(nameof(setupAction)); + } + + services.AddWebEncoders(); + services.Configure(setupAction); + + return services; + } + + private static Func CreateFactory( + Func defaultFactory, + Func customSettingsFactory) + { + return serviceProvider => + { + var settings = serviceProvider + ?.GetService>() + ?.Value + ?.TextEncoderSettings; + return (settings != null) ? customSettingsFactory(settings) : defaultFactory(); + }; + } + } +} diff --git a/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj b/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj new file mode 100644 index 000000000000..18f96d9412b0 --- /dev/null +++ b/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj @@ -0,0 +1,18 @@ + + + + Contains registration and configuration APIs to add the core framework encoders to a dependency injection container. + netstandard2.0 + $(NoWarn);CS1591 + true + true + aspnetcore + + + + + + + + + diff --git a/src/WebEncoders/src/Testing/HtmlTestEncoder.cs b/src/WebEncoders/src/Testing/HtmlTestEncoder.cs new file mode 100644 index 000000000000..162ce4f6c1cb --- /dev/null +++ b/src/WebEncoders/src/Testing/HtmlTestEncoder.cs @@ -0,0 +1,104 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; +using System.Text.Encodings.Web; + +namespace Microsoft.Extensions.WebEncoders.Testing +{ + /// + /// Encoder used for unit testing. + /// + public sealed class HtmlTestEncoder : HtmlEncoder + { + public override int MaxOutputCharactersPerInputCharacter + { + get { return 1; } + } + + public override string Encode(string value) + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + if (value.Length == 0) + { + return string.Empty; + } + + return $"HtmlEncode[[{value}]]"; + } + + public override void Encode(TextWriter output, char[] value, int startIndex, int characterCount) + { + if (output == null) + { + throw new ArgumentNullException(nameof(output)); + } + + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + if (characterCount == 0) + { + return; + } + + output.Write("HtmlEncode[["); + output.Write(value, startIndex, characterCount); + output.Write("]]"); + } + + public override void Encode(TextWriter output, string value, int startIndex, int characterCount) + { + if (output == null) + { + throw new ArgumentNullException(nameof(output)); + } + + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + if (characterCount == 0) + { + return; + } + + output.Write("HtmlEncode[["); + output.Write(value.Substring(startIndex, characterCount)); + output.Write("]]"); + } + + public override bool WillEncode(int unicodeScalar) + { + return false; + } + + public override unsafe int FindFirstCharacterToEncode(char* text, int textLength) + { + return -1; + } + + public override unsafe bool TryEncodeUnicodeScalar( + int unicodeScalar, + char* buffer, + int bufferLength, + out int numberOfCharactersWritten) + { + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer)); + } + + numberOfCharactersWritten = 0; + return false; + } + } +} \ No newline at end of file diff --git a/src/WebEncoders/src/Testing/JavaScriptTestEncoder.cs b/src/WebEncoders/src/Testing/JavaScriptTestEncoder.cs new file mode 100644 index 000000000000..bef44616760c --- /dev/null +++ b/src/WebEncoders/src/Testing/JavaScriptTestEncoder.cs @@ -0,0 +1,104 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; +using System.Text.Encodings.Web; + +namespace Microsoft.Extensions.WebEncoders.Testing +{ + /// + /// Encoder used for unit testing. + /// + public class JavaScriptTestEncoder : JavaScriptEncoder + { + public override int MaxOutputCharactersPerInputCharacter + { + get { return 1; } + } + + public override string Encode(string value) + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + if (value.Length == 0) + { + return string.Empty; + } + + return $"JavaScriptEncode[[{value}]]"; + } + + public override void Encode(TextWriter output, char[] value, int startIndex, int characterCount) + { + if (output == null) + { + throw new ArgumentNullException(nameof(output)); + } + + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + if (characterCount == 0) + { + return; + } + + output.Write("JavaScriptEncode[["); + output.Write(value, startIndex, characterCount); + output.Write("]]"); + } + + public override void Encode(TextWriter output, string value, int startIndex, int characterCount) + { + if (output == null) + { + throw new ArgumentNullException(nameof(output)); + } + + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + if (characterCount == 0) + { + return; + } + + output.Write("JavaScriptEncode[["); + output.Write(value.Substring(startIndex, characterCount)); + output.Write("]]"); + } + + public override bool WillEncode(int unicodeScalar) + { + return false; + } + + public override unsafe int FindFirstCharacterToEncode(char* text, int textLength) + { + return -1; + } + + public override unsafe bool TryEncodeUnicodeScalar( + int unicodeScalar, + char* buffer, + int bufferLength, + out int numberOfCharactersWritten) + { + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer)); + } + + numberOfCharactersWritten = 0; + return false; + } + } +} \ No newline at end of file diff --git a/src/WebEncoders/src/Testing/UrlTestEncoder.cs b/src/WebEncoders/src/Testing/UrlTestEncoder.cs new file mode 100644 index 000000000000..295bda63e8d5 --- /dev/null +++ b/src/WebEncoders/src/Testing/UrlTestEncoder.cs @@ -0,0 +1,104 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; +using System.Text.Encodings.Web; + +namespace Microsoft.Extensions.WebEncoders.Testing +{ + /// + /// Encoder used for unit testing. + /// + public class UrlTestEncoder : UrlEncoder + { + public override int MaxOutputCharactersPerInputCharacter + { + get { return 1; } + } + + public override string Encode(string value) + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + if (value.Length == 0) + { + return string.Empty; + } + + return $"UrlEncode[[{value}]]"; + } + + public override void Encode(TextWriter output, char[] value, int startIndex, int characterCount) + { + if (output == null) + { + throw new ArgumentNullException(nameof(output)); + } + + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + if (characterCount == 0) + { + return; + } + + output.Write("UrlEncode[["); + output.Write(value, startIndex, characterCount); + output.Write("]]"); + } + + public override void Encode(TextWriter output, string value, int startIndex, int characterCount) + { + if (output == null) + { + throw new ArgumentNullException(nameof(output)); + } + + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + if (characterCount == 0) + { + return; + } + + output.Write("UrlEncode[["); + output.Write(value.Substring(startIndex, characterCount)); + output.Write("]]"); + } + + public override bool WillEncode(int unicodeScalar) + { + return false; + } + + public override unsafe int FindFirstCharacterToEncode(char* text, int textLength) + { + return -1; + } + + public override unsafe bool TryEncodeUnicodeScalar( + int unicodeScalar, + char* buffer, + int bufferLength, + out int numberOfCharactersWritten) + { + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer)); + } + + numberOfCharactersWritten = 0; + return false; + } + } +} \ No newline at end of file diff --git a/src/WebEncoders/src/WebEncoderOptions.cs b/src/WebEncoders/src/WebEncoderOptions.cs new file mode 100644 index 000000000000..2f5e770a0c37 --- /dev/null +++ b/src/WebEncoders/src/WebEncoderOptions.cs @@ -0,0 +1,21 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Text.Encodings.Web; + +namespace Microsoft.Extensions.WebEncoders +{ + /// + /// Specifies options common to all three encoders (HtmlEncode, JavaScriptEncode, UrlEncode). + /// + public sealed class WebEncoderOptions + { + /// + /// Specifies which code points are allowed to be represented unescaped by the encoders. + /// + /// + /// If this property is null, then the encoders will use their default allow lists. + /// + public TextEncoderSettings TextEncoderSettings { get; set; } + } +} diff --git a/src/WebEncoders/src/baseline.netcore.json b/src/WebEncoders/src/baseline.netcore.json new file mode 100644 index 000000000000..6da0ae07547e --- /dev/null +++ b/src/WebEncoders/src/baseline.netcore.json @@ -0,0 +1,564 @@ +{ + "AssemblyIdentity": "Microsoft.Extensions.WebEncoders, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "Types": [ + { + "Name": "Microsoft.Extensions.WebEncoders.WebEncoderOptions", + "Visibility": "Public", + "Kind": "Class", + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_TextEncoderSettings", + "Parameters": [], + "ReturnType": "System.Text.Encodings.Web.TextEncoderSettings", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_TextEncoderSettings", + "Parameters": [ + { + "Name": "value", + "Type": "System.Text.Encodings.Web.TextEncoderSettings" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.Extensions.WebEncoders.Testing.HtmlTestEncoder", + "Visibility": "Public", + "Kind": "Class", + "Sealed": true, + "BaseType": "System.Text.Encodings.Web.HtmlEncoder", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_MaxOutputCharactersPerInputCharacter", + "Parameters": [], + "ReturnType": "System.Int32", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Encode", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.String", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Encode", + "Parameters": [ + { + "Name": "output", + "Type": "System.IO.TextWriter" + }, + { + "Name": "value", + "Type": "System.Char[]" + }, + { + "Name": "startIndex", + "Type": "System.Int32" + }, + { + "Name": "characterCount", + "Type": "System.Int32" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Encode", + "Parameters": [ + { + "Name": "output", + "Type": "System.IO.TextWriter" + }, + { + "Name": "value", + "Type": "System.String" + }, + { + "Name": "startIndex", + "Type": "System.Int32" + }, + { + "Name": "characterCount", + "Type": "System.Int32" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "WillEncode", + "Parameters": [ + { + "Name": "unicodeScalar", + "Type": "System.Int32" + } + ], + "ReturnType": "System.Boolean", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindFirstCharacterToEncode", + "Parameters": [ + { + "Name": "text", + "Type": "System.Char*" + }, + { + "Name": "textLength", + "Type": "System.Int32" + } + ], + "ReturnType": "System.Int32", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "TryEncodeUnicodeScalar", + "Parameters": [ + { + "Name": "unicodeScalar", + "Type": "System.Int32" + }, + { + "Name": "buffer", + "Type": "System.Char*" + }, + { + "Name": "bufferLength", + "Type": "System.Int32" + }, + { + "Name": "numberOfCharactersWritten", + "Type": "System.Int32", + "Direction": "Out" + } + ], + "ReturnType": "System.Boolean", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.Extensions.WebEncoders.Testing.JavaScriptTestEncoder", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "System.Text.Encodings.Web.JavaScriptEncoder", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_MaxOutputCharactersPerInputCharacter", + "Parameters": [], + "ReturnType": "System.Int32", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Encode", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.String", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Encode", + "Parameters": [ + { + "Name": "output", + "Type": "System.IO.TextWriter" + }, + { + "Name": "value", + "Type": "System.Char[]" + }, + { + "Name": "startIndex", + "Type": "System.Int32" + }, + { + "Name": "characterCount", + "Type": "System.Int32" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Encode", + "Parameters": [ + { + "Name": "output", + "Type": "System.IO.TextWriter" + }, + { + "Name": "value", + "Type": "System.String" + }, + { + "Name": "startIndex", + "Type": "System.Int32" + }, + { + "Name": "characterCount", + "Type": "System.Int32" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "WillEncode", + "Parameters": [ + { + "Name": "unicodeScalar", + "Type": "System.Int32" + } + ], + "ReturnType": "System.Boolean", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindFirstCharacterToEncode", + "Parameters": [ + { + "Name": "text", + "Type": "System.Char*" + }, + { + "Name": "textLength", + "Type": "System.Int32" + } + ], + "ReturnType": "System.Int32", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "TryEncodeUnicodeScalar", + "Parameters": [ + { + "Name": "unicodeScalar", + "Type": "System.Int32" + }, + { + "Name": "buffer", + "Type": "System.Char*" + }, + { + "Name": "bufferLength", + "Type": "System.Int32" + }, + { + "Name": "numberOfCharactersWritten", + "Type": "System.Int32", + "Direction": "Out" + } + ], + "ReturnType": "System.Boolean", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.Extensions.WebEncoders.Testing.UrlTestEncoder", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "System.Text.Encodings.Web.UrlEncoder", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_MaxOutputCharactersPerInputCharacter", + "Parameters": [], + "ReturnType": "System.Int32", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Encode", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.String", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Encode", + "Parameters": [ + { + "Name": "output", + "Type": "System.IO.TextWriter" + }, + { + "Name": "value", + "Type": "System.Char[]" + }, + { + "Name": "startIndex", + "Type": "System.Int32" + }, + { + "Name": "characterCount", + "Type": "System.Int32" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Encode", + "Parameters": [ + { + "Name": "output", + "Type": "System.IO.TextWriter" + }, + { + "Name": "value", + "Type": "System.String" + }, + { + "Name": "startIndex", + "Type": "System.Int32" + }, + { + "Name": "characterCount", + "Type": "System.Int32" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "WillEncode", + "Parameters": [ + { + "Name": "unicodeScalar", + "Type": "System.Int32" + } + ], + "ReturnType": "System.Boolean", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindFirstCharacterToEncode", + "Parameters": [ + { + "Name": "text", + "Type": "System.Char*" + }, + { + "Name": "textLength", + "Type": "System.Int32" + } + ], + "ReturnType": "System.Int32", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "TryEncodeUnicodeScalar", + "Parameters": [ + { + "Name": "unicodeScalar", + "Type": "System.Int32" + }, + { + "Name": "buffer", + "Type": "System.Char*" + }, + { + "Name": "bufferLength", + "Type": "System.Int32" + }, + { + "Name": "numberOfCharactersWritten", + "Type": "System.Int32", + "Direction": "Out" + } + ], + "ReturnType": "System.Boolean", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.Extensions.DependencyInjection.EncoderServiceCollectionExtensions", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "AddWebEncoders", + "Parameters": [ + { + "Name": "services", + "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" + } + ], + "ReturnType": "Microsoft.Extensions.DependencyInjection.IServiceCollection", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddWebEncoders", + "Parameters": [ + { + "Name": "services", + "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" + }, + { + "Name": "setupAction", + "Type": "System.Action" + } + ], + "ReturnType": "Microsoft.Extensions.DependencyInjection.IServiceCollection", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + } + ] +} \ No newline at end of file diff --git a/src/WebEncoders/test/EncoderServiceCollectionExtensionsTests.cs b/src/WebEncoders/test/EncoderServiceCollectionExtensionsTests.cs new file mode 100644 index 000000000000..0178bba2d5b9 --- /dev/null +++ b/src/WebEncoders/test/EncoderServiceCollectionExtensionsTests.cs @@ -0,0 +1,90 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Text.Encodings.Web; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.WebEncoders.Testing; +using Xunit; + +namespace Microsoft.Extensions.WebEncoders +{ + public class EncoderServiceCollectionExtensionsTests + { + [Fact] + public void AddWebEncoders_WithoutOptions_RegistersDefaultEncoders() + { + // Arrange + var serviceCollection = new ServiceCollection(); + + // Act + serviceCollection.AddWebEncoders(); + + // Assert + var serviceProvider = serviceCollection.BuildServiceProvider(); + Assert.Same(HtmlEncoder.Default, serviceProvider.GetRequiredService()); // default encoder + Assert.Same(HtmlEncoder.Default, serviceProvider.GetRequiredService()); // as singleton instance + Assert.Same(JavaScriptEncoder.Default, serviceProvider.GetRequiredService()); // default encoder + Assert.Same(JavaScriptEncoder.Default, serviceProvider.GetRequiredService()); // as singleton instance + Assert.Same(UrlEncoder.Default, serviceProvider.GetRequiredService()); // default encoder + Assert.Same(UrlEncoder.Default, serviceProvider.GetRequiredService()); // as singleton instance + } + + [Fact] + public void AddWebEncoders_WithOptions_RegistersEncodersWithCustomCodeFilter() + { + // Arrange + var serviceCollection = new ServiceCollection(); + + // Act + serviceCollection.AddWebEncoders(options => + { + options.TextEncoderSettings = new TextEncoderSettings(); + options.TextEncoderSettings.AllowCharacters("ace".ToCharArray()); // only these three chars are allowed + }); + + // Assert + var serviceProvider = serviceCollection.BuildServiceProvider(); + + var htmlEncoder = serviceProvider.GetRequiredService(); + Assert.Equal("abcde", htmlEncoder.Encode("abcde")); + Assert.Same(htmlEncoder, serviceProvider.GetRequiredService()); // as singleton instance + + var javaScriptEncoder = serviceProvider.GetRequiredService(); + Assert.Equal(@"a\u0062c\u0064e", javaScriptEncoder.Encode("abcde")); + Assert.Same(javaScriptEncoder, serviceProvider.GetRequiredService()); // as singleton instance + + var urlEncoder = serviceProvider.GetRequiredService(); + Assert.Equal("a%62c%64e", urlEncoder.Encode("abcde")); + Assert.Same(urlEncoder, serviceProvider.GetRequiredService()); // as singleton instance + } + + [Fact] + public void AddWebEncoders_DoesNotOverrideExistingRegisteredEncoders() + { + // Arrange + var serviceCollection = new ServiceCollection(); + + // Act + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + // we don't register an existing URL encoder + serviceCollection.AddWebEncoders(options => + { + options.TextEncoderSettings = new TextEncoderSettings(); + options.TextEncoderSettings.AllowCharacters("ace".ToCharArray()); // only these three chars are allowed + }); + + // Assert + var serviceProvider = serviceCollection.BuildServiceProvider(); + + var htmlEncoder = serviceProvider.GetRequiredService(); + Assert.Equal("HtmlEncode[[abcde]]", htmlEncoder.Encode("abcde")); + + var javaScriptEncoder = serviceProvider.GetRequiredService(); + Assert.Equal("JavaScriptEncode[[abcde]]", javaScriptEncoder.Encode("abcde")); + + var urlEncoder = serviceProvider.GetRequiredService(); + Assert.Equal("a%62c%64e", urlEncoder.Encode("abcde")); + } + } +} diff --git a/src/WebEncoders/test/HtmlTestEncoderTest.cs b/src/WebEncoders/test/HtmlTestEncoderTest.cs new file mode 100644 index 000000000000..baafedc4de96 --- /dev/null +++ b/src/WebEncoders/test/HtmlTestEncoderTest.cs @@ -0,0 +1,26 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Xunit; + +namespace Microsoft.Extensions.WebEncoders.Testing +{ + public class HtmlTestEncoderTest + { + [Theory] + [InlineData("", "")] + [InlineData("abcd", "HtmlEncode[[abcd]]")] + [InlineData("<<''\"\">>", "HtmlEncode[[<<''\"\">>]]")] + public void StringEncode_EncodesAsExpected(string input, string expectedOutput) + { + // Arrange + var encoder = new HtmlTestEncoder(); + + // Act + var output = encoder.Encode(input); + + // Assert + Assert.Equal(expectedOutput, output); + } + } +} diff --git a/src/WebEncoders/test/Microsoft.Extensions.WebEncoders.Tests.csproj b/src/WebEncoders/test/Microsoft.Extensions.WebEncoders.Tests.csproj new file mode 100755 index 000000000000..729dc5c61a92 --- /dev/null +++ b/src/WebEncoders/test/Microsoft.Extensions.WebEncoders.Tests.csproj @@ -0,0 +1,12 @@ + + + + $(StandardTestTfms) + + + + + + + + From 83c88bd6bdede632ad38f753bf1e9a30b40ed7dc Mon Sep 17 00:00:00 2001 From: Gert Driesen Date: Tue, 13 Nov 2018 19:03:55 +0100 Subject: [PATCH 013/183] Eliminate use of method groups in PropertyHelper (dotnet/extensions#465) * Eliminate use of method groups, and use for loop to enumerate elements of array. Improves performance and reduces allocations. * PR feedback: Remove extra parentheses, and undo change from foreach to for loop. * PR feedback Revert change from for to foreach. * var-ify TryGetValue out parameter. \n\nCommit migrated from https://github.com/dotnet/extensions/commit/08adb5c2ca594683a43dfc1e6178fffc5e336456 --- src/Shared/PropertyHelper/PropertyHelper.cs | 22 +++++++++------------ 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/Shared/PropertyHelper/PropertyHelper.cs b/src/Shared/PropertyHelper/PropertyHelper.cs index f6aad151e529..f3641e03dc0d 100644 --- a/src/Shared/PropertyHelper/PropertyHelper.cs +++ b/src/Shared/PropertyHelper/PropertyHelper.cs @@ -7,7 +7,6 @@ using System.Diagnostics; using System.Linq; using System.Reflection; -using System.Runtime.CompilerServices; namespace Microsoft.Extensions.Internal { @@ -145,7 +144,7 @@ public static PropertyHelper[] GetProperties(TypeInfo typeInfo) /// public static PropertyHelper[] GetProperties(Type type) { - return GetProperties(type, CreateInstance, PropertiesCache); + return GetProperties(type, p => CreateInstance(p), PropertiesCache); } /// @@ -164,7 +163,7 @@ public static PropertyHelper[] GetProperties(Type type) /// public static PropertyHelper[] GetVisibleProperties(TypeInfo typeInfo) { - return GetVisibleProperties(typeInfo.AsType(), CreateInstance, PropertiesCache, VisiblePropertiesCache); + return GetVisibleProperties(typeInfo.AsType(), p => CreateInstance(p), PropertiesCache, VisiblePropertiesCache); } /// @@ -183,7 +182,7 @@ public static PropertyHelper[] GetVisibleProperties(TypeInfo typeInfo) /// public static PropertyHelper[] GetVisibleProperties(Type type) { - return GetVisibleProperties(type, CreateInstance, PropertiesCache, VisiblePropertiesCache); + return GetVisibleProperties(type, p => CreateInstance(p), PropertiesCache, VisiblePropertiesCache); } /// @@ -420,8 +419,7 @@ protected static PropertyHelper[] GetVisibleProperties( ConcurrentDictionary allPropertiesCache, ConcurrentDictionary visiblePropertiesCache) { - PropertyHelper[] result; - if (visiblePropertiesCache.TryGetValue(type, out result)) + if (visiblePropertiesCache.TryGetValue(type, out var result)) { return result; } @@ -497,18 +495,17 @@ protected static PropertyHelper[] GetProperties( // part of the sequence of properties returned by this method. type = Nullable.GetUnderlyingType(type) ?? type; - PropertyHelper[] helpers; - if (!cache.TryGetValue(type, out helpers)) + if (!cache.TryGetValue(type, out var helpers)) { // We avoid loading indexed properties using the Where statement. - var properties = type.GetRuntimeProperties().Where(IsInterestingProperty); + var properties = type.GetRuntimeProperties().Where(p => IsInterestingProperty(p)); var typeInfo = type.GetTypeInfo(); if (typeInfo.IsInterface) { // Reflection does not return information about inherited properties on the interface itself. properties = properties.Concat(typeInfo.ImplementedInterfaces.SelectMany( - interfaceType => interfaceType.GetRuntimeProperties().Where(IsInterestingProperty))); + interfaceType => interfaceType.GetRuntimeProperties().Where(p => IsInterestingProperty(p)))); } helpers = properties.Select(p => createPropertyHelper(p)).ToArray(); @@ -518,17 +515,16 @@ protected static PropertyHelper[] GetProperties( return helpers; } - private static bool IsInterestingProperty(PropertyInfo property) { // For improving application startup time, do not use GetIndexParameters() api early in this check as it // creates a copy of parameter array and also we would like to check for the presence of a get method // and short circuit asap. - return + return property.GetMethod != null && property.GetMethod.IsPublic && !property.GetMethod.IsStatic && - + // PropertyHelper can't work with ref structs. !IsRefStructProperty(property) && From ea14c9c86519396d52c51cfb5b8e8aeb7dacb0c1 Mon Sep 17 00:00:00 2001 From: Javier Calvarro Nelson Date: Tue, 13 Nov 2018 10:11:21 -0800 Subject: [PATCH 014/183] Change async void tests to async Task (dotnet/extensions#488) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/d2f4c6a2ed30be5fae6ec166bfb7a3073c9f1d17 --- .../Shared.Tests/ObjectMethodExecutorTest.cs | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/Shared/test/Shared.Tests/ObjectMethodExecutorTest.cs b/src/Shared/test/Shared.Tests/ObjectMethodExecutorTest.cs index 1c26ef1de157..fb9f82aad98c 100644 --- a/src/Shared/test/Shared.Tests/ObjectMethodExecutorTest.cs +++ b/src/Shared/test/Shared.Tests/ObjectMethodExecutorTest.cs @@ -155,7 +155,7 @@ public void GetDefaultValueForParameters_ThrowsIfNoneWereSupplied() } [Fact] - public async void TargetMethodReturningCustomAwaitableOfReferenceType_CanInvokeViaExecute() + public async Task TargetMethodReturningCustomAwaitableOfReferenceType_CanInvokeViaExecute() { // Arrange var executor = GetExecutorForMethod("CustomAwaitableOfReferenceTypeAsync"); @@ -171,7 +171,7 @@ public async void TargetMethodReturningCustomAwaitableOfReferenceType_CanInvokeV } [Fact] - public async void TargetMethodReturningCustomAwaitableOfValueType_CanInvokeViaExecute() + public async Task TargetMethodReturningCustomAwaitableOfValueType_CanInvokeViaExecute() { // Arrange var executor = GetExecutorForMethod("CustomAwaitableOfValueTypeAsync"); @@ -186,7 +186,7 @@ public async void TargetMethodReturningCustomAwaitableOfValueType_CanInvokeViaEx } [Fact] - public async void TargetMethodReturningCustomAwaitableOfReferenceType_CanInvokeViaExecuteAsync() + public async Task TargetMethodReturningCustomAwaitableOfReferenceType_CanInvokeViaExecuteAsync() { // Arrange var executor = GetExecutorForMethod("CustomAwaitableOfReferenceTypeAsync"); @@ -203,7 +203,7 @@ public async void TargetMethodReturningCustomAwaitableOfReferenceType_CanInvokeV } [Fact] - public async void TargetMethodReturningCustomAwaitableOfValueType_CanInvokeViaExecuteAsync() + public async Task TargetMethodReturningCustomAwaitableOfValueType_CanInvokeViaExecuteAsync() { // Arrange var executor = GetExecutorForMethod("CustomAwaitableOfValueTypeAsync"); @@ -220,7 +220,7 @@ public async void TargetMethodReturningCustomAwaitableOfValueType_CanInvokeViaEx } [Fact] - public async void TargetMethodReturningAwaitableOfVoidType_CanInvokeViaExecuteAsync() + public async Task TargetMethodReturningAwaitableOfVoidType_CanInvokeViaExecuteAsync() { // Arrange var executor = GetExecutorForMethod("VoidValueMethodAsync"); @@ -235,7 +235,7 @@ public async void TargetMethodReturningAwaitableOfVoidType_CanInvokeViaExecuteAs } [Fact] - public async void TargetMethodReturningAwaitableWithICriticalNotifyCompletion_UsesUnsafeOnCompleted() + public async Task TargetMethodReturningAwaitableWithICriticalNotifyCompletion_UsesUnsafeOnCompleted() { // Arrange var executor = GetExecutorForMethod("CustomAwaitableWithICriticalNotifyCompletion"); @@ -250,7 +250,7 @@ public async void TargetMethodReturningAwaitableWithICriticalNotifyCompletion_Us } [Fact] - public async void TargetMethodReturningAwaitableWithoutICriticalNotifyCompletion_UsesOnCompleted() + public async Task TargetMethodReturningAwaitableWithoutICriticalNotifyCompletion_UsesOnCompleted() { // Arrange var executor = GetExecutorForMethod("CustomAwaitableWithoutICriticalNotifyCompletion"); @@ -265,7 +265,7 @@ public async void TargetMethodReturningAwaitableWithoutICriticalNotifyCompletion } [Fact] - public async void TargetMethodReturningValueTaskOfValueType_CanBeInvokedViaExecute() + public async Task TargetMethodReturningValueTaskOfValueType_CanBeInvokedViaExecute() { // Arrange var executor = GetExecutorForMethod("ValueTaskOfValueType"); @@ -280,7 +280,7 @@ public async void TargetMethodReturningValueTaskOfValueType_CanBeInvokedViaExecu } [Fact] - public async void TargetMethodReturningValueTaskOfReferenceType_CanBeInvokedViaExecute() + public async Task TargetMethodReturningValueTaskOfReferenceType_CanBeInvokedViaExecute() { // Arrange var executor = GetExecutorForMethod("ValueTaskOfReferenceType"); @@ -295,7 +295,7 @@ public async void TargetMethodReturningValueTaskOfReferenceType_CanBeInvokedViaE } [Fact] - public async void TargetMethodReturningValueTaskOfValueType_CanBeInvokedViaExecuteAsync() + public async Task TargetMethodReturningValueTaskOfValueType_CanBeInvokedViaExecuteAsync() { // Arrange var executor = GetExecutorForMethod("ValueTaskOfValueType"); @@ -311,7 +311,7 @@ public async void TargetMethodReturningValueTaskOfValueType_CanBeInvokedViaExecu } [Fact] - public async void TargetMethodReturningValueTaskOfReferenceType_CanBeInvokedViaExecuteAsync() + public async Task TargetMethodReturningValueTaskOfReferenceType_CanBeInvokedViaExecuteAsync() { // Arrange var executor = GetExecutorForMethod("ValueTaskOfReferenceType"); @@ -326,7 +326,7 @@ public async void TargetMethodReturningValueTaskOfReferenceType_CanBeInvokedViaE } [Fact] - public async void TargetMethodReturningFSharpAsync_CanBeInvokedViaExecute() + public async Task TargetMethodReturningFSharpAsync_CanBeInvokedViaExecute() { // Arrange var executor = GetExecutorForMethod("FSharpAsyncMethod"); @@ -344,7 +344,7 @@ public async void TargetMethodReturningFSharpAsync_CanBeInvokedViaExecute() } [Fact] - public async void TargetMethodReturningFailingFSharpAsync_CanBeInvokedViaExecute() + public async Task TargetMethodReturningFailingFSharpAsync_CanBeInvokedViaExecute() { // Arrange var executor = GetExecutorForMethod("FSharpAsyncFailureMethod"); @@ -365,7 +365,7 @@ public async void TargetMethodReturningFailingFSharpAsync_CanBeInvokedViaExecute } [Fact] - public async void TargetMethodReturningFSharpAsync_CanBeInvokedViaExecuteAsync() + public async Task TargetMethodReturningFSharpAsync_CanBeInvokedViaExecuteAsync() { // Arrange var executor = GetExecutorForMethod("FSharpAsyncMethod"); @@ -380,7 +380,7 @@ public async void TargetMethodReturningFSharpAsync_CanBeInvokedViaExecuteAsync() } [Fact] - public async void TargetMethodReturningFailingFSharpAsync_CanBeInvokedViaExecuteAsync() + public async Task TargetMethodReturningFailingFSharpAsync_CanBeInvokedViaExecuteAsync() { // Arrange var executor = GetExecutorForMethod("FSharpAsyncFailureMethod"); From 88003f2c215bff9ca9cf3039f14de45d14ccf7fc Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Wed, 14 Nov 2018 08:43:07 -0800 Subject: [PATCH 015/183] Prepare repo to build 2.2.1 * Update dependencies to 2.2.0 rtm * Update branding to 2.2.1 * Update package baselines to 2.2.0 * Add a restore source for 2.2.0 RTM \n\nCommit migrated from https://github.com/dotnet/extensions/commit/913f74d590bcbdd1165d8faa1d99f3f11197aa4d --- src/Testing/src/Microsoft.AspNetCore.Testing.csproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Testing/src/Microsoft.AspNetCore.Testing.csproj b/src/Testing/src/Microsoft.AspNetCore.Testing.csproj index d9d9008dd2bd..64e0b3c4e1d6 100644 --- a/src/Testing/src/Microsoft.AspNetCore.Testing.csproj +++ b/src/Testing/src/Microsoft.AspNetCore.Testing.csproj @@ -8,6 +8,8 @@ aspnetcore false true + + true From 6c5d291b10806decdd8d70d8648012471cda47ca Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Fri, 16 Nov 2018 08:33:57 -0800 Subject: [PATCH 016/183] Remove internal types from Logging and Abstractions (dotnet/extensions#513) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/55518d79834d3319c91f40b449d028338b129ed6 --- src/Shared/BenchmarkRunner/DefaultCoreValidationConfig.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Shared/BenchmarkRunner/DefaultCoreValidationConfig.cs b/src/Shared/BenchmarkRunner/DefaultCoreValidationConfig.cs index 95fc725564db..5a90929cff8c 100644 --- a/src/Shared/BenchmarkRunner/DefaultCoreValidationConfig.cs +++ b/src/Shared/BenchmarkRunner/DefaultCoreValidationConfig.cs @@ -1,11 +1,6 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; -using System.Linq; -using System.Reflection; -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Running; using BenchmarkDotNet.Configs; using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Loggers; From ebb1165b14b0d8f0a6151d8d323f231529e6f67e Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Fri, 16 Nov 2018 13:57:43 -0800 Subject: [PATCH 017/183] Handle nullable enum default values (dotnet/extensions#531) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/33f839d585ae7664195008d8c9c5e317d5897510 --- .../ParameterDefaultValue.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/Shared/ParameterDefaultValue/ParameterDefaultValue.cs b/src/Shared/ParameterDefaultValue/ParameterDefaultValue.cs index a71bad37b1ad..dc635bb78997 100644 --- a/src/Shared/ParameterDefaultValue/ParameterDefaultValue.cs +++ b/src/Shared/ParameterDefaultValue/ParameterDefaultValue.cs @@ -8,6 +8,8 @@ namespace Microsoft.Extensions.Internal { internal class ParameterDefaultValue { + private static readonly Type _nullable = typeof(Nullable<>); + public static bool TryGetDefaultValue(ParameterInfo parameter, out object defaultValue) { bool hasDefaultValue; @@ -39,6 +41,19 @@ public static bool TryGetDefaultValue(ParameterInfo parameter, out object defaul { defaultValue = Activator.CreateInstance(parameter.ParameterType); } + + // Handle nullable enums + if (defaultValue != null && + parameter.ParameterType.IsGenericType && + parameter.ParameterType.GetGenericTypeDefinition() == _nullable + ) + { + var underlyingType = Nullable.GetUnderlyingType(parameter.ParameterType); + if (underlyingType != null && underlyingType.IsEnum) + { + defaultValue = Enum.ToObject(underlyingType, defaultValue); + } + } } return hasDefaultValue; From 6dc295b5782302ded80a5ae088d99d5868229818 Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Tue, 20 Nov 2018 20:49:10 -0800 Subject: [PATCH 018/183] Reorganize source code in preparation to move into aspnet/Extensions Prior to reorganization, this source code was found in https://github.com/aspnet/Diagnostics/tree/dotnet/extensions@c802d5ef5fba1ba8dfbcb8c3741af2ba15e9d1aa \n\nCommit migrated from https://github.com/dotnet/extensions/commit/a03861270c35c4c7f44c0914c01cdf24602973b2 --- .../Abstractions/src/HealthCheckContext.cs | 13 + .../src/HealthCheckRegistration.cs | 132 +++++ .../Abstractions/src/HealthCheckResult.cs | 88 +++ .../Abstractions/src/HealthReport.cs | 68 +++ .../Abstractions/src/HealthReportEntry.cs | 59 ++ .../Abstractions/src/HealthStatus.cs | 37 ++ .../Abstractions/src/IHealthCheck.cs | 23 + .../Abstractions/src/IHealthCheckPublisher.cs | 39 ++ ...agnostics.HealthChecks.Abstractions.csproj | 16 + .../Abstractions/src/baseline.netcore.json | 5 + .../src/DefaultHealthCheckService.cs | 304 ++++++++++ .../HealthChecks/src/DelegateHealthCheck.cs | 35 ++ .../HealthCheckServiceCollectionExtensions.cs | 33 ++ .../HealthChecksBuilder.cs | 33 ++ .../HealthChecksBuilderAddCheckExtensions.cs | 191 +++++++ .../HealthChecksBuilderDelegateExtensions.cs | 149 +++++ .../IHealthChecksBuilder.cs | 24 + .../HealthChecks/src/HealthCheckLogScope.cs | 48 ++ .../src/HealthCheckPublisherHostedService.cs | 262 +++++++++ .../src/HealthCheckPublisherOptions.cs | 84 +++ .../HealthChecks/src/HealthCheckService.cs | 61 ++ .../src/HealthCheckServiceOptions.cs | 18 + ...Extensions.Diagnostics.HealthChecks.csproj | 26 + .../src/Properties/AssemblyInfo.cs | 3 + .../HealthChecks/src/baseline.netcore.json | 5 + .../test/DefaultHealthCheckServiceTest.cs | 419 ++++++++++++++ .../HealthChecksBuilderTest.cs | 257 +++++++++ .../ServiceCollectionExtensionsTest.cs | 43 ++ .../HealthCheckPublisherHostedServiceTest.cs | 528 ++++++++++++++++++ .../HealthChecks/test/HealthReportTest.cs | 45 ++ ...ions.Diagnostics.HealthChecks.Tests.csproj | 12 + 31 files changed, 3060 insertions(+) create mode 100644 src/HealthChecks/Abstractions/src/HealthCheckContext.cs create mode 100644 src/HealthChecks/Abstractions/src/HealthCheckRegistration.cs create mode 100644 src/HealthChecks/Abstractions/src/HealthCheckResult.cs create mode 100644 src/HealthChecks/Abstractions/src/HealthReport.cs create mode 100644 src/HealthChecks/Abstractions/src/HealthReportEntry.cs create mode 100644 src/HealthChecks/Abstractions/src/HealthStatus.cs create mode 100644 src/HealthChecks/Abstractions/src/IHealthCheck.cs create mode 100644 src/HealthChecks/Abstractions/src/IHealthCheckPublisher.cs create mode 100644 src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj create mode 100644 src/HealthChecks/Abstractions/src/baseline.netcore.json create mode 100644 src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs create mode 100644 src/HealthChecks/HealthChecks/src/DelegateHealthCheck.cs create mode 100644 src/HealthChecks/HealthChecks/src/DependencyInjection/HealthCheckServiceCollectionExtensions.cs create mode 100644 src/HealthChecks/HealthChecks/src/DependencyInjection/HealthChecksBuilder.cs create mode 100644 src/HealthChecks/HealthChecks/src/DependencyInjection/HealthChecksBuilderAddCheckExtensions.cs create mode 100644 src/HealthChecks/HealthChecks/src/DependencyInjection/HealthChecksBuilderDelegateExtensions.cs create mode 100644 src/HealthChecks/HealthChecks/src/DependencyInjection/IHealthChecksBuilder.cs create mode 100644 src/HealthChecks/HealthChecks/src/HealthCheckLogScope.cs create mode 100644 src/HealthChecks/HealthChecks/src/HealthCheckPublisherHostedService.cs create mode 100644 src/HealthChecks/HealthChecks/src/HealthCheckPublisherOptions.cs create mode 100644 src/HealthChecks/HealthChecks/src/HealthCheckService.cs create mode 100644 src/HealthChecks/HealthChecks/src/HealthCheckServiceOptions.cs create mode 100644 src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj create mode 100644 src/HealthChecks/HealthChecks/src/Properties/AssemblyInfo.cs create mode 100644 src/HealthChecks/HealthChecks/src/baseline.netcore.json create mode 100644 src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs create mode 100644 src/HealthChecks/HealthChecks/test/DependencyInjection/HealthChecksBuilderTest.cs create mode 100644 src/HealthChecks/HealthChecks/test/DependencyInjection/ServiceCollectionExtensionsTest.cs create mode 100644 src/HealthChecks/HealthChecks/test/HealthCheckPublisherHostedServiceTest.cs create mode 100644 src/HealthChecks/HealthChecks/test/HealthReportTest.cs create mode 100644 src/HealthChecks/HealthChecks/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests.csproj diff --git a/src/HealthChecks/Abstractions/src/HealthCheckContext.cs b/src/HealthChecks/Abstractions/src/HealthCheckContext.cs new file mode 100644 index 000000000000..027451c0d243 --- /dev/null +++ b/src/HealthChecks/Abstractions/src/HealthCheckContext.cs @@ -0,0 +1,13 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.Extensions.Diagnostics.HealthChecks +{ + public sealed class HealthCheckContext + { + /// + /// Gets or sets the of the currently executing . + /// + public HealthCheckRegistration Registration { get; set; } + } +} diff --git a/src/HealthChecks/Abstractions/src/HealthCheckRegistration.cs b/src/HealthChecks/Abstractions/src/HealthCheckRegistration.cs new file mode 100644 index 000000000000..9291c388462f --- /dev/null +++ b/src/HealthChecks/Abstractions/src/HealthCheckRegistration.cs @@ -0,0 +1,132 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; + +namespace Microsoft.Extensions.Diagnostics.HealthChecks +{ + /// + /// Represent the registration information associated with an implementation. + /// + /// + /// + /// The health check registration is provided as a separate object so that application developers can customize + /// how health check implementations are configured. + /// + /// + /// The registration is provided to an implementation during execution through + /// . This allows a health check implementation to access named + /// options or perform other operations based on the registered name. + /// + /// + public sealed class HealthCheckRegistration + { + private Func _factory; + private string _name; + + /// + /// Creates a new for an existing instance. + /// + /// The health check name. + /// The instance. + /// + /// The that should be reported upon failure of the health check. If the provided value + /// is null, then will be reported. + /// + /// A list of tags that can be used for filtering health checks. + public HealthCheckRegistration(string name, IHealthCheck instance, HealthStatus? failureStatus, IEnumerable tags) + { + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + + if (instance == null) + { + throw new ArgumentNullException(nameof(instance)); + } + + Name = name; + FailureStatus = failureStatus ?? HealthStatus.Unhealthy; + Tags = new HashSet(tags ?? Array.Empty(), StringComparer.OrdinalIgnoreCase); + Factory = (_) => instance; + } + + /// + /// Creates a new for an existing instance. + /// + /// The health check name. + /// A delegate used to create the instance. + /// + /// The that should be reported when the health check reports a failure. If the provided value + /// is null, then will be reported. + /// + /// A list of tags that can be used for filtering health checks. + public HealthCheckRegistration( + string name, + Func factory, + HealthStatus? failureStatus, + IEnumerable tags) + { + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + + if (factory == null) + { + throw new ArgumentNullException(nameof(factory)); + } + + Name = name; + FailureStatus = failureStatus ?? HealthStatus.Unhealthy; + Tags = new HashSet(tags ?? Array.Empty(), StringComparer.OrdinalIgnoreCase); + Factory = factory; + } + + /// + /// Gets or sets a delegate used to create the instance. + /// + public Func Factory + { + get => _factory; + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + _factory = value; + } + } + + /// + /// Gets or sets the that should be reported upon failure of the health check. + /// + public HealthStatus FailureStatus { get; set; } + + /// + /// Gets or sets the health check name. + /// + public string Name + { + get => _name; + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + _name = value; + } + } + + /// + /// Gets a list of tags that can be used for filtering health checks. + /// + public ISet Tags { get; } + } +} diff --git a/src/HealthChecks/Abstractions/src/HealthCheckResult.cs b/src/HealthChecks/Abstractions/src/HealthCheckResult.cs new file mode 100644 index 000000000000..e01cb5aceba4 --- /dev/null +++ b/src/HealthChecks/Abstractions/src/HealthCheckResult.cs @@ -0,0 +1,88 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; + +namespace Microsoft.Extensions.Diagnostics.HealthChecks +{ + /// + /// Represents the result of a health check. + /// + public struct HealthCheckResult + { + private static readonly IReadOnlyDictionary _emptyReadOnlyDictionary = new Dictionary(); + + /// + /// Creates a new with the specified values for , + /// , , and . + /// + /// A value indicating the status of the component that was checked. + /// A human-readable description of the status of the component that was checked. + /// An representing the exception that was thrown when checking for status (if any). + /// Additional key-value pairs describing the health of the component. + public HealthCheckResult(HealthStatus status, string description = null, Exception exception = null, IReadOnlyDictionary data = null) + { + Status = status; + Description = description; + Exception = exception; + Data = data ?? _emptyReadOnlyDictionary; + } + + /// + /// Gets additional key-value pairs describing the health of the component. + /// + public IReadOnlyDictionary Data { get; } + + /// + /// Gets a human-readable description of the status of the component that was checked. + /// + public string Description { get; } + + /// + /// Gets an representing the exception that was thrown when checking for status (if any). + /// + public Exception Exception { get; } + + /// + /// Gets a value indicating the status of the component that was checked. + /// + public HealthStatus Status { get; } + + /// + /// Creates a representing a healthy component. + /// + /// A human-readable description of the status of the component that was checked. Optional. + /// Additional key-value pairs describing the health of the component. Optional. + /// A representing a healthy component. + public static HealthCheckResult Healthy(string description = null, IReadOnlyDictionary data = null) + { + return new HealthCheckResult(status: HealthStatus.Healthy, description, exception: null, data); + } + + + /// + /// Creates a representing a degraded component. + /// + /// A human-readable description of the status of the component that was checked. Optional. + /// An representing the exception that was thrown when checking for status. Optional. + /// Additional key-value pairs describing the health of the component. Optional. + /// A representing a degraged component. + public static HealthCheckResult Degraded(string description = null, Exception exception = null, IReadOnlyDictionary data = null) + { + return new HealthCheckResult(status: HealthStatus.Degraded, description, exception: null, data); + } + + /// + /// Creates a representing an unhealthy component. + /// + /// A human-readable description of the status of the component that was checked. Optional. + /// An representing the exception that was thrown when checking for status. Optional. + /// Additional key-value pairs describing the health of the component. Optional. + /// A representing an unhealthy component. + public static HealthCheckResult Unhealthy(string description = null, Exception exception = null, IReadOnlyDictionary data = null) + { + return new HealthCheckResult(status: HealthStatus.Unhealthy, description, exception, data); + } + } +} diff --git a/src/HealthChecks/Abstractions/src/HealthReport.cs b/src/HealthChecks/Abstractions/src/HealthReport.cs new file mode 100644 index 000000000000..91ed798811d5 --- /dev/null +++ b/src/HealthChecks/Abstractions/src/HealthReport.cs @@ -0,0 +1,68 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; + +namespace Microsoft.Extensions.Diagnostics.HealthChecks +{ + /// + /// Represents the result of executing a group of instances. + /// + public sealed class HealthReport + { + /// + /// Create a new from the specified results. + /// + /// A containing the results from each health check. + /// A value indicating the time the health check service took to execute. + public HealthReport(IReadOnlyDictionary entries, TimeSpan totalDuration) + { + Entries = entries; + Status = CalculateAggregateStatus(entries.Values); + TotalDuration = totalDuration; + } + + /// + /// A containing the results from each health check. + /// + /// + /// The keys in this dictionary map the name of each executed health check to a for the + /// result data retruned from the corresponding health check. + /// + public IReadOnlyDictionary Entries { get; } + + /// + /// Gets a representing the aggregate status of all the health checks. The value of + /// will be the most servere status reported by a health check. If no checks were executed, the value is always . + /// + public HealthStatus Status { get; } + + /// + /// Gets the time the health check service took to execute. + /// + public TimeSpan TotalDuration { get; } + + private HealthStatus CalculateAggregateStatus(IEnumerable entries) + { + // This is basically a Min() check, but we know the possible range, so we don't need to walk the whole list + var currentValue = HealthStatus.Healthy; + foreach (var entry in entries) + { + if (currentValue > entry.Status) + { + currentValue = entry.Status; + } + + if (currentValue == HealthStatus.Unhealthy) + { + // Game over, man! Game over! + // (We hit the worst possible status, so there's no need to keep iterating) + return currentValue; + } + } + + return currentValue; + } + } +} diff --git a/src/HealthChecks/Abstractions/src/HealthReportEntry.cs b/src/HealthChecks/Abstractions/src/HealthReportEntry.cs new file mode 100644 index 000000000000..6e7d6c6b8e55 --- /dev/null +++ b/src/HealthChecks/Abstractions/src/HealthReportEntry.cs @@ -0,0 +1,59 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; + +namespace Microsoft.Extensions.Diagnostics.HealthChecks +{ + /// + /// Represents an entry in a . Corresponds to the result of a single . + /// + public struct HealthReportEntry + { + private static readonly IReadOnlyDictionary _emptyReadOnlyDictionary = new Dictionary(); + + /// + /// Creates a new with the specified values for , , + /// , and . + /// + /// A value indicating the health status of the component that was checked. + /// A human-readable description of the status of the component that was checked. + /// A value indicating the health execution duration. + /// An representing the exception that was thrown when checking for status (if any). + /// Additional key-value pairs describing the health of the component. + public HealthReportEntry(HealthStatus status, string description, TimeSpan duration, Exception exception, IReadOnlyDictionary data) + { + Status = status; + Description = description; + Duration = duration; + Exception = exception; + Data = data ?? _emptyReadOnlyDictionary; + } + + /// + /// Gets additional key-value pairs describing the health of the component. + /// + public IReadOnlyDictionary Data { get; } + + /// + /// Gets a human-readable description of the status of the component that was checked. + /// + public string Description { get; } + + /// + /// Gets the health check execution duration. + /// + public TimeSpan Duration { get; } + + /// + /// Gets an representing the exception that was thrown when checking for status (if any). + /// + public Exception Exception { get; } + + /// + /// Gets the health status of the component that was checked. + /// + public HealthStatus Status { get; } + } +} diff --git a/src/HealthChecks/Abstractions/src/HealthStatus.cs b/src/HealthChecks/Abstractions/src/HealthStatus.cs new file mode 100644 index 000000000000..61b76d54fa19 --- /dev/null +++ b/src/HealthChecks/Abstractions/src/HealthStatus.cs @@ -0,0 +1,37 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.Extensions.Diagnostics.HealthChecks +{ + /// + /// Represents the reported status of a health check result. + /// + /// + /// + /// A status of should be considered the default value for a failing health check. Application + /// developers may configure a health check to report a different status as desired. + /// + /// + /// The values of this enum or ordered from least healthy to most healthy. So is + /// greater than but less than . + /// + /// + public enum HealthStatus + { + /// + /// Indicates that the health check determined that the component was unhealthy, or an unhandled + /// exception was thrown while executing the health check. + /// + Unhealthy = 0, + + /// + /// Indicates that the health check determined that the component was in a degraded state. + /// + Degraded = 1, + + /// + /// Indicates that the health check determined that the component was healthy. + /// + Healthy = 2, + } +} diff --git a/src/HealthChecks/Abstractions/src/IHealthCheck.cs b/src/HealthChecks/Abstractions/src/IHealthCheck.cs new file mode 100644 index 000000000000..1b69953b67c6 --- /dev/null +++ b/src/HealthChecks/Abstractions/src/IHealthCheck.cs @@ -0,0 +1,23 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.Extensions.Diagnostics.HealthChecks +{ + /// + /// Represents a health check, which can be used to check the status of a component in the application, such as a backend service, database or some internal + /// state. + /// + public interface IHealthCheck + { + /// + /// Runs the health check, returning the status of the component being checked. + /// + /// A context object associated with the current execution. + /// A that can be used to cancel the health check. + /// A that completes when the health check has finished, yielding the status of the component being checked. + Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default); + } +} diff --git a/src/HealthChecks/Abstractions/src/IHealthCheckPublisher.cs b/src/HealthChecks/Abstractions/src/IHealthCheckPublisher.cs new file mode 100644 index 000000000000..f1809c4bb893 --- /dev/null +++ b/src/HealthChecks/Abstractions/src/IHealthCheckPublisher.cs @@ -0,0 +1,39 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.Extensions.Diagnostics.HealthChecks +{ + /// + /// Represents a publisher of information. + /// + /// + /// + /// The default health checks implementation provided an IHostedService implementation that can + /// be used to execute health checks at regular intervals and provide the resulting + /// data to all registered instances. + /// + /// + /// To provide an implementation, register an instance or type as a singleton + /// service in the dependency injection container. + /// + /// + /// instances are provided with a after executing + /// health checks in a background thread. The use of depend on hosting in + /// an application using IWebHost or generic host (IHost). Execution of + /// instance is not related to execution of health checks via a middleware. + /// + /// + public interface IHealthCheckPublisher + { + /// + /// Publishes the provided . + /// + /// The . The result of executing a set of health checks. + /// The . + /// A which will complete when publishing is complete. + Task PublishAsync(HealthReport report, CancellationToken cancellationToken); + } +} diff --git a/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj b/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj new file mode 100644 index 000000000000..b95d66f7b38a --- /dev/null +++ b/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj @@ -0,0 +1,16 @@ + + + + Abstractions for defining health checks in .NET applications + +Commonly Used Types +Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck + + Microsoft.Extensions.Diagnostics.HealthChecks + netstandard2.0 + $(NoWarn);CS1591 + true + diagnostics;healthchecks + + + diff --git a/src/HealthChecks/Abstractions/src/baseline.netcore.json b/src/HealthChecks/Abstractions/src/baseline.netcore.json new file mode 100644 index 000000000000..871db4c089ea --- /dev/null +++ b/src/HealthChecks/Abstractions/src/baseline.netcore.json @@ -0,0 +1,5 @@ +{ + "AssemblyIdentity": "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "Types": [ + ] +} \ No newline at end of file diff --git a/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs b/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs new file mode 100644 index 000000000000..d5d71d9cb49e --- /dev/null +++ b/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs @@ -0,0 +1,304 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Internal; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace Microsoft.Extensions.Diagnostics.HealthChecks +{ + internal class DefaultHealthCheckService : HealthCheckService + { + private readonly IServiceScopeFactory _scopeFactory; + private readonly IOptions _options; + private readonly ILogger _logger; + + public DefaultHealthCheckService( + IServiceScopeFactory scopeFactory, + IOptions options, + ILogger logger) + { + _scopeFactory = scopeFactory ?? throw new ArgumentNullException(nameof(scopeFactory)); + _options = options ?? throw new ArgumentNullException(nameof(options)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + + // We're specifically going out of our way to do this at startup time. We want to make sure you + // get any kind of health-check related error as early as possible. Waiting until someone + // actually tries to **run** health checks would be real baaaaad. + ValidateRegistrations(_options.Value.Registrations); + } + public override async Task CheckHealthAsync( + Func predicate, + CancellationToken cancellationToken = default) + { + var registrations = _options.Value.Registrations; + + using (var scope = _scopeFactory.CreateScope()) + { + var context = new HealthCheckContext(); + var entries = new Dictionary(StringComparer.OrdinalIgnoreCase); + + var totalTime = ValueStopwatch.StartNew(); + Log.HealthCheckProcessingBegin(_logger); + + foreach (var registration in registrations) + { + if (predicate != null && !predicate(registration)) + { + continue; + } + + cancellationToken.ThrowIfCancellationRequested(); + + var healthCheck = registration.Factory(scope.ServiceProvider); + + // If the health check does things like make Database queries using EF or backend HTTP calls, + // it may be valuable to know that logs it generates are part of a health check. So we start a scope. + using (_logger.BeginScope(new HealthCheckLogScope(registration.Name))) + { + var stopwatch = ValueStopwatch.StartNew(); + context.Registration = registration; + + Log.HealthCheckBegin(_logger, registration); + + HealthReportEntry entry; + try + { + var result = await healthCheck.CheckHealthAsync(context, cancellationToken); + var duration = stopwatch.GetElapsedTime(); + + entry = new HealthReportEntry( + status: result.Status, + description: result.Description, + duration: duration, + exception: result.Exception, + data: result.Data); + + Log.HealthCheckEnd(_logger, registration, entry, duration); + Log.HealthCheckData(_logger, registration, entry); + } + + // Allow cancellation to propagate. + catch (Exception ex) when (ex as OperationCanceledException == null) + { + var duration = stopwatch.GetElapsedTime(); + entry = new HealthReportEntry( + status: HealthStatus.Unhealthy, + description: ex.Message, + duration: duration, + exception: ex, + data: null); + + Log.HealthCheckError(_logger, registration, ex, duration); + } + + entries[registration.Name] = entry; + } + } + + var totalElapsedTime = totalTime.GetElapsedTime(); + var report = new HealthReport(entries, totalElapsedTime); + Log.HealthCheckProcessingEnd(_logger, report.Status, totalElapsedTime); + return report; + } + } + + private static void ValidateRegistrations(IEnumerable registrations) + { + // Scan the list for duplicate names to provide a better error if there are duplicates. + var duplicateNames = registrations + .GroupBy(c => c.Name, StringComparer.OrdinalIgnoreCase) + .Where(g => g.Count() > 1) + .Select(g => g.Key) + .ToList(); + + if (duplicateNames.Count > 0) + { + throw new ArgumentException($"Duplicate health checks were registered with the name(s): {string.Join(", ", duplicateNames)}", nameof(registrations)); + } + } + + internal static class EventIds + { + public static readonly EventId HealthCheckProcessingBegin = new EventId(100, "HealthCheckProcessingBegin"); + public static readonly EventId HealthCheckProcessingEnd = new EventId(101, "HealthCheckProcessingEnd"); + + public static readonly EventId HealthCheckBegin = new EventId(102, "HealthCheckBegin"); + public static readonly EventId HealthCheckEnd = new EventId(103, "HealthCheckEnd"); + public static readonly EventId HealthCheckError = new EventId(104, "HealthCheckError"); + public static readonly EventId HealthCheckData = new EventId(105, "HealthCheckData"); + } + + private static class Log + { + private static readonly Action _healthCheckProcessingBegin = LoggerMessage.Define( + LogLevel.Debug, + EventIds.HealthCheckProcessingBegin, + "Running health checks"); + + private static readonly Action _healthCheckProcessingEnd = LoggerMessage.Define( + LogLevel.Debug, + EventIds.HealthCheckProcessingEnd, + "Health check processing completed after {ElapsedMilliseconds}ms with combined status {HealthStatus}"); + + private static readonly Action _healthCheckBegin = LoggerMessage.Define( + LogLevel.Debug, + EventIds.HealthCheckBegin, + "Running health check {HealthCheckName}"); + + // These are separate so they can have different log levels + private static readonly string HealthCheckEndText = "Health check {HealthCheckName} completed after {ElapsedMilliseconds}ms with status {HealthStatus} and '{HealthCheckDescription}'"; + + private static readonly Action _healthCheckEndHealthy = LoggerMessage.Define( + LogLevel.Debug, + EventIds.HealthCheckEnd, + HealthCheckEndText); + + private static readonly Action _healthCheckEndDegraded = LoggerMessage.Define( + LogLevel.Warning, + EventIds.HealthCheckEnd, + HealthCheckEndText); + + private static readonly Action _healthCheckEndUnhealthy = LoggerMessage.Define( + LogLevel.Error, + EventIds.HealthCheckEnd, + HealthCheckEndText); + + private static readonly Action _healthCheckEndFailed = LoggerMessage.Define( + LogLevel.Error, + EventIds.HealthCheckEnd, + HealthCheckEndText); + + private static readonly Action _healthCheckError = LoggerMessage.Define( + LogLevel.Error, + EventIds.HealthCheckError, + "Health check {HealthCheckName} threw an unhandled exception after {ElapsedMilliseconds}ms"); + + public static void HealthCheckProcessingBegin(ILogger logger) + { + _healthCheckProcessingBegin(logger, null); + } + + public static void HealthCheckProcessingEnd(ILogger logger, HealthStatus status, TimeSpan duration) + { + _healthCheckProcessingEnd(logger, duration.TotalMilliseconds, status, null); + } + + public static void HealthCheckBegin(ILogger logger, HealthCheckRegistration registration) + { + _healthCheckBegin(logger, registration.Name, null); + } + + public static void HealthCheckEnd(ILogger logger, HealthCheckRegistration registration, HealthReportEntry entry, TimeSpan duration) + { + switch (entry.Status) + { + case HealthStatus.Healthy: + _healthCheckEndHealthy(logger, registration.Name, duration.TotalMilliseconds, entry.Status, entry.Description, null); + break; + + case HealthStatus.Degraded: + _healthCheckEndDegraded(logger, registration.Name, duration.TotalMilliseconds, entry.Status, entry.Description, null); + break; + + case HealthStatus.Unhealthy: + _healthCheckEndUnhealthy(logger, registration.Name, duration.TotalMilliseconds, entry.Status, entry.Description, null); + break; + } + } + + public static void HealthCheckError(ILogger logger, HealthCheckRegistration registration, Exception exception, TimeSpan duration) + { + _healthCheckError(logger, registration.Name, duration.TotalMilliseconds, exception); + } + + public static void HealthCheckData(ILogger logger, HealthCheckRegistration registration, HealthReportEntry entry) + { + if (entry.Data.Count > 0 && logger.IsEnabled(LogLevel.Debug)) + { + logger.Log( + LogLevel.Debug, + EventIds.HealthCheckData, + new HealthCheckDataLogValue(registration.Name, entry.Data), + null, + (state, ex) => state.ToString()); + } + } + } + + internal class HealthCheckDataLogValue : IReadOnlyList> + { + private readonly string _name; + private readonly List> _values; + + private string _formatted; + + public HealthCheckDataLogValue(string name, IReadOnlyDictionary values) + { + _name = name; + _values = values.ToList(); + + // We add the name as a kvp so that you can filter by health check name in the logs. + // This is the same parameter name used in the other logs. + _values.Add(new KeyValuePair("HealthCheckName", name)); + } + + public KeyValuePair this[int index] + { + get + { + if (index < 0 || index >= Count) + { + throw new IndexOutOfRangeException(nameof(index)); + } + + return _values[index]; + } + } + + public int Count => _values.Count; + + public IEnumerator> GetEnumerator() + { + return _values.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return _values.GetEnumerator(); + } + + public override string ToString() + { + if (_formatted == null) + { + var builder = new StringBuilder(); + builder.AppendLine($"Health check data for {_name}:"); + + var values = _values; + for (var i = 0; i < values.Count; i++) + { + var kvp = values[i]; + builder.Append(" "); + builder.Append(kvp.Key); + builder.Append(": "); + + builder.AppendLine(kvp.Value?.ToString()); + } + + _formatted = builder.ToString(); + } + + return _formatted; + } + } + } +} diff --git a/src/HealthChecks/HealthChecks/src/DelegateHealthCheck.cs b/src/HealthChecks/HealthChecks/src/DelegateHealthCheck.cs new file mode 100644 index 000000000000..94069fd7d154 --- /dev/null +++ b/src/HealthChecks/HealthChecks/src/DelegateHealthCheck.cs @@ -0,0 +1,35 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.Extensions.Diagnostics.HealthChecks +{ + /// + /// A simple implementation of which uses a provided delegate to + /// implement the check. + /// + internal sealed class DelegateHealthCheck : IHealthCheck + { + private readonly Func> _check; + + /// + /// Create an instance of from the specified delegate. + /// + /// A delegate which provides the code to execute when the health check is run. + public DelegateHealthCheck(Func> check) + { + _check = check ?? throw new ArgumentNullException(nameof(check)); + } + + /// + /// Runs the health check, returning the status of the component being checked. + /// + /// A context object associated with the current execution. + /// A that can be used to cancel the health check. + /// A that completes when the health check has finished, yielding the status of the component being checked. + public Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) => _check(cancellationToken); + } +} diff --git a/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthCheckServiceCollectionExtensions.cs b/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthCheckServiceCollectionExtensions.cs new file mode 100644 index 000000000000..d6df03d2aecb --- /dev/null +++ b/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthCheckServiceCollectionExtensions.cs @@ -0,0 +1,33 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using Microsoft.Extensions.Hosting; + +namespace Microsoft.Extensions.DependencyInjection +{ + /// + /// Provides extension methods for registering in an . + /// + public static class HealthCheckServiceCollectionExtensions + { + /// + /// Adds the to the container, using the provided delegate to register + /// health checks. + /// + /// + /// This operation is idempotent - multiple invocations will still only result in a single + /// instance in the . It can be invoked + /// multiple times in order to get access to the in multiple places. + /// + /// The to add the to. + /// An instance of from which health checks can be registered. + public static IHealthChecksBuilder AddHealthChecks(this IServiceCollection services) + { + services.TryAddSingleton(); + services.TryAddSingleton(); + return new HealthChecksBuilder(services); + } + } +} diff --git a/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthChecksBuilder.cs b/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthChecksBuilder.cs new file mode 100644 index 000000000000..231dd5171721 --- /dev/null +++ b/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthChecksBuilder.cs @@ -0,0 +1,33 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.Extensions.Diagnostics.HealthChecks; + +namespace Microsoft.Extensions.DependencyInjection +{ + internal class HealthChecksBuilder : IHealthChecksBuilder + { + public HealthChecksBuilder(IServiceCollection services) + { + Services = services; + } + + public IServiceCollection Services { get; } + + public IHealthChecksBuilder Add(HealthCheckRegistration registration) + { + if (registration == null) + { + throw new ArgumentNullException(nameof(registration)); + } + + Services.Configure(options => + { + options.Registrations.Add(registration); + }); + + return this; + } + } +} diff --git a/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthChecksBuilderAddCheckExtensions.cs b/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthChecksBuilderAddCheckExtensions.cs new file mode 100644 index 000000000000..95088890542c --- /dev/null +++ b/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthChecksBuilderAddCheckExtensions.cs @@ -0,0 +1,191 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using Microsoft.Extensions.Diagnostics.HealthChecks; + +namespace Microsoft.Extensions.DependencyInjection +{ + /// + /// Provides basic extension methods for registering instances in an . + /// + public static class HealthChecksBuilderAddCheckExtensions + { + /// + /// Adds a new health check with the specified name and implementation. + /// + /// The . + /// The name of the health check. + /// An instance. + /// + /// The that should be reported when the health check reports a failure. If the provided value + /// is null, then will be reported. + /// + /// A list of tags that can be used to filter health checks. + /// The . + public static IHealthChecksBuilder AddCheck( + this IHealthChecksBuilder builder, + string name, + IHealthCheck instance, + HealthStatus? failureStatus = null, + IEnumerable tags = null) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + + if (instance == null) + { + throw new ArgumentNullException(nameof(instance)); + } + + return builder.Add(new HealthCheckRegistration(name, instance, failureStatus, tags)); + } + + /// + /// Adds a new health check with the specified name and implementation. + /// + /// The health check implementation type. + /// The . + /// The name of the health check. + /// + /// The that should be reported when the health check reports a failure. If the provided value + /// is null, then will be reported. + /// + /// A list of tags that can be used to filter health checks. + /// The . + /// + /// This method will use to create the health check + /// instance when needed. If a service of type is registred in the dependency injection container + /// with any liftime it will be used. Otherwise an instance of type will be constructed with + /// access to services from the dependency injection container. + /// + public static IHealthChecksBuilder AddCheck( + this IHealthChecksBuilder builder, + string name, + HealthStatus? failureStatus = null, + IEnumerable tags = null) where T : class, IHealthCheck + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + + return builder.Add(new HealthCheckRegistration(name, s => ActivatorUtilities.GetServiceOrCreateInstance(s), failureStatus, tags)); + } + + // NOTE: AddTypeActivatedCheck has overloads rather than default parameters values, because default parameter values don't + // play super well with params. + + /// + /// Adds a new type activated health check with the specified name and implementation. + /// + /// The health check implementation type. + /// The . + /// The name of the health check. + /// Additional arguments to provide to the constructor. + /// The . + /// + /// This method will use to create the health check + /// instance when needed. Additional arguments can be provided to the constructor via . + /// + public static IHealthChecksBuilder AddTypeActivatedCheck(this IHealthChecksBuilder builder, string name, params object[] args) where T : class, IHealthCheck + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + + return AddTypeActivatedCheck(builder, name, failureStatus: null, tags: null); + } + + /// + /// Adds a new type activated health check with the specified name and implementation. + /// + /// The health check implementation type. + /// The . + /// The name of the health check. + /// + /// The that should be reported when the health check reports a failure. If the provided value + /// is null, then will be reported. + /// + /// Additional arguments to provide to the constructor. + /// The . + /// + /// This method will use to create the health check + /// instance when needed. Additional arguments can be provided to the constructor via . + /// + public static IHealthChecksBuilder AddTypeActivatedCheck( + this IHealthChecksBuilder builder, + string name, + HealthStatus? failureStatus, + params object[] args) where T : class, IHealthCheck + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + + return AddTypeActivatedCheck(builder, name, failureStatus, tags: null); + } + + /// + /// Adds a new type activated health check with the specified name and implementation. + /// + /// The health check implementation type. + /// The . + /// The name of the health check. + /// + /// The that should be reported when the health check reports a failure. If the provided value + /// is null, then will be reported. + /// + /// A list of tags that can be used to filter health checks. + /// Additional arguments to provide to the constructor. + /// The . + /// + /// This method will use to create the health check + /// instance when needed. Additional arguments can be provided to the constructor via . + /// + public static IHealthChecksBuilder AddTypeActivatedCheck( + this IHealthChecksBuilder builder, + string name, + HealthStatus? failureStatus, + IEnumerable tags, + params object[] args) where T : class, IHealthCheck + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + + return builder.Add(new HealthCheckRegistration(name, s => ActivatorUtilities.CreateInstance(s, args), failureStatus, tags)); + } + } +} diff --git a/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthChecksBuilderDelegateExtensions.cs b/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthChecksBuilderDelegateExtensions.cs new file mode 100644 index 000000000000..d7dfdd90ae10 --- /dev/null +++ b/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthChecksBuilderDelegateExtensions.cs @@ -0,0 +1,149 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Diagnostics.HealthChecks; + +namespace Microsoft.Extensions.DependencyInjection +{ + /// + /// Provides extension methods for registering delegates with the . + /// + public static class HealthChecksBuilderDelegateExtensions + { + /// + /// Adds a new health check with the specified name and implementation. + /// + /// The . + /// The name of the health check. + /// A list of tags that can be used to filter health checks. + /// A delegate that provides the health check implementation. + /// The . + public static IHealthChecksBuilder AddCheck( + this IHealthChecksBuilder builder, + string name, + Func check, + IEnumerable tags = null) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + + if (check == null) + { + throw new ArgumentNullException(nameof(check)); + } + + var instance = new DelegateHealthCheck((ct) => Task.FromResult(check())); + return builder.Add(new HealthCheckRegistration(name, instance, failureStatus: null, tags)); + } + + /// + /// Adds a new health check with the specified name and implementation. + /// + /// The . + /// The name of the health check. + /// A list of tags that can be used to filter health checks. + /// A delegate that provides the health check implementation. + /// The . + public static IHealthChecksBuilder AddCheck( + this IHealthChecksBuilder builder, + string name, + Func check, + IEnumerable tags = null) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + + if (check == null) + { + throw new ArgumentNullException(nameof(check)); + } + + var instance = new DelegateHealthCheck((ct) => Task.FromResult(check(ct))); + return builder.Add(new HealthCheckRegistration(name, instance, failureStatus: null, tags)); + } + + /// + /// Adds a new health check with the specified name and implementation. + /// + /// The . + /// The name of the health check. + /// A list of tags that can be used to filter health checks. + /// A delegate that provides the health check implementation. + /// The . + public static IHealthChecksBuilder AddAsyncCheck( + this IHealthChecksBuilder builder, + string name, + Func> check, + IEnumerable tags = null) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + + if (check == null) + { + throw new ArgumentNullException(nameof(check)); + } + + var instance = new DelegateHealthCheck((ct) => check()); + return builder.Add(new HealthCheckRegistration(name, instance, failureStatus: null, tags)); + } + + /// + /// Adds a new health check with the specified name and implementation. + /// + /// The . + /// The name of the health check. + /// A list of tags that can be used to filter health checks. + /// A delegate that provides the health check implementation. + /// The . + public static IHealthChecksBuilder AddAsyncCheck( + this IHealthChecksBuilder builder, + string name, + Func> check, + IEnumerable tags = null) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + + if (check == null) + { + throw new ArgumentNullException(nameof(check)); + } + + var instance = new DelegateHealthCheck((ct) => check(ct)); + return builder.Add(new HealthCheckRegistration(name, instance, failureStatus: null, tags)); + } + } +} diff --git a/src/HealthChecks/HealthChecks/src/DependencyInjection/IHealthChecksBuilder.cs b/src/HealthChecks/HealthChecks/src/DependencyInjection/IHealthChecksBuilder.cs new file mode 100644 index 000000000000..eb78293f87c4 --- /dev/null +++ b/src/HealthChecks/HealthChecks/src/DependencyInjection/IHealthChecksBuilder.cs @@ -0,0 +1,24 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.Extensions.Diagnostics.HealthChecks; + +namespace Microsoft.Extensions.DependencyInjection +{ + /// + /// A builder used to register health checks. + /// + public interface IHealthChecksBuilder + { + /// + /// Adds a for a health check. + /// + /// The . + IHealthChecksBuilder Add(HealthCheckRegistration registration); + + /// + /// Gets the into which instances should be registered. + /// + IServiceCollection Services { get; } + } +} diff --git a/src/HealthChecks/HealthChecks/src/HealthCheckLogScope.cs b/src/HealthChecks/HealthChecks/src/HealthCheckLogScope.cs new file mode 100644 index 000000000000..c7ef3ff5bd2d --- /dev/null +++ b/src/HealthChecks/HealthChecks/src/HealthCheckLogScope.cs @@ -0,0 +1,48 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Microsoft.Extensions.Diagnostics.HealthChecks +{ + internal class HealthCheckLogScope : IReadOnlyList> + { + public string HealthCheckName { get; } + + int IReadOnlyCollection>.Count { get; } = 1; + + KeyValuePair IReadOnlyList>.this[int index] + { + get + { + if (index == 0) + { + return new KeyValuePair(nameof(HealthCheckName), HealthCheckName); + } + + throw new ArgumentOutOfRangeException(nameof(index)); + } + } + + /// + /// Creates a new instance of with the provided name. + /// + /// The name of the health check being executed. + public HealthCheckLogScope(string healthCheckName) + { + HealthCheckName = healthCheckName; + } + + IEnumerator> IEnumerable>.GetEnumerator() + { + yield return new KeyValuePair(nameof(HealthCheckName), HealthCheckName); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable>)this).GetEnumerator(); + } + } +} diff --git a/src/HealthChecks/HealthChecks/src/HealthCheckPublisherHostedService.cs b/src/HealthChecks/HealthChecks/src/HealthCheckPublisherHostedService.cs new file mode 100644 index 000000000000..d124ffa2e3ea --- /dev/null +++ b/src/HealthChecks/HealthChecks/src/HealthCheckPublisherHostedService.cs @@ -0,0 +1,262 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Internal; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace Microsoft.Extensions.Diagnostics.HealthChecks +{ + internal sealed class HealthCheckPublisherHostedService : IHostedService + { + private readonly HealthCheckService _healthCheckService; + private readonly IOptions _options; + private readonly ILogger _logger; + private readonly IHealthCheckPublisher[] _publishers; + + private CancellationTokenSource _stopping; + private Timer _timer; + + public HealthCheckPublisherHostedService( + HealthCheckService healthCheckService, + IOptions options, + ILogger logger, + IEnumerable publishers) + { + if (healthCheckService == null) + { + throw new ArgumentNullException(nameof(healthCheckService)); + } + + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } + + if (logger == null) + { + throw new ArgumentNullException(nameof(logger)); + } + + if (publishers == null) + { + throw new ArgumentNullException(nameof(publishers)); + } + + _healthCheckService = healthCheckService; + _options = options; + _logger = logger; + _publishers = publishers.ToArray(); + + _stopping = new CancellationTokenSource(); + } + + internal bool IsStopping => _stopping.IsCancellationRequested; + + internal bool IsTimerRunning => _timer != null; + + public Task StartAsync(CancellationToken cancellationToken = default) + { + if (_publishers.Length == 0) + { + return Task.CompletedTask; + } + + // IMPORTANT - make sure this is the last thing that happens in this method. The timer can + // fire before other code runs. + _timer = NonCapturingTimer.Create(Timer_Tick, null, dueTime: _options.Value.Delay, period: _options.Value.Period); + + return Task.CompletedTask; + } + + public Task StopAsync(CancellationToken cancellationToken = default) + { + try + { + _stopping.Cancel(); + } + catch + { + // Ignore exceptions thrown as a result of a cancellation. + } + + if (_publishers.Length == 0) + { + return Task.CompletedTask; + } + + _timer?.Dispose(); + _timer = null; + + + return Task.CompletedTask; + } + + // Yes, async void. We need to be async. We need to be void. We handle the exceptions in RunAsync + private async void Timer_Tick(object state) + { + await RunAsync(); + } + + // Internal for testing + internal async Task RunAsync() + { + var duration = ValueStopwatch.StartNew(); + Logger.HealthCheckPublisherProcessingBegin(_logger); + + CancellationTokenSource cancellation = null; + try + { + var timeout = _options.Value.Timeout; + + cancellation = CancellationTokenSource.CreateLinkedTokenSource(_stopping.Token); + cancellation.CancelAfter(timeout); + + await RunAsyncCore(cancellation.Token); + + Logger.HealthCheckPublisherProcessingEnd(_logger, duration.GetElapsedTime()); + } + catch (OperationCanceledException) when (IsStopping) + { + // This is a cancellation - if the app is shutting down we want to ignore it. Otherwise, it's + // a timeout and we want to log it. + } + catch (Exception ex) + { + // This is an error, publishing failed. + Logger.HealthCheckPublisherProcessingEnd(_logger, duration.GetElapsedTime(), ex); + } + finally + { + cancellation.Dispose(); + } + } + + private async Task RunAsyncCore(CancellationToken cancellationToken) + { + // Forcibly yield - we want to unblock the timer thread. + await Task.Yield(); + + // The health checks service does it's own logging, and doesn't throw exceptions. + var report = await _healthCheckService.CheckHealthAsync(_options.Value.Predicate, cancellationToken); + + var publishers = _publishers; + var tasks = new Task[publishers.Length]; + for (var i = 0; i < publishers.Length; i++) + { + tasks[i] = RunPublisherAsync(publishers[i], report, cancellationToken); + } + + await Task.WhenAll(tasks); + } + + private async Task RunPublisherAsync(IHealthCheckPublisher publisher, HealthReport report, CancellationToken cancellationToken) + { + var duration = ValueStopwatch.StartNew(); + + try + { + Logger.HealthCheckPublisherBegin(_logger, publisher); + + await publisher.PublishAsync(report, cancellationToken); + Logger.HealthCheckPublisherEnd(_logger, publisher, duration.GetElapsedTime()); + } + catch (OperationCanceledException) when (IsStopping) + { + // This is a cancellation - if the app is shutting down we want to ignore it. Otherwise, it's + // a timeout and we want to log it. + } + catch (OperationCanceledException ocex) + { + Logger.HealthCheckPublisherTimeout(_logger, publisher, duration.GetElapsedTime()); + throw ocex; + } + catch (Exception ex) + { + Logger.HealthCheckPublisherError(_logger, publisher, duration.GetElapsedTime(), ex); + throw ex; + } + } + + internal static class EventIds + { + public static readonly EventId HealthCheckPublisherProcessingBegin = new EventId(100, "HealthCheckPublisherProcessingBegin"); + public static readonly EventId HealthCheckPublisherProcessingEnd = new EventId(101, "HealthCheckPublisherProcessingEnd"); + public static readonly EventId HealthCheckPublisherProcessingError = new EventId(101, "HealthCheckPublisherProcessingError"); + + public static readonly EventId HealthCheckPublisherBegin = new EventId(102, "HealthCheckPublisherBegin"); + public static readonly EventId HealthCheckPublisherEnd = new EventId(103, "HealthCheckPublisherEnd"); + public static readonly EventId HealthCheckPublisherError = new EventId(104, "HealthCheckPublisherError"); + public static readonly EventId HealthCheckPublisherTimeout = new EventId(104, "HealthCheckPublisherTimeout"); + } + + private static class Logger + { + private static readonly Action _healthCheckPublisherProcessingBegin = LoggerMessage.Define( + LogLevel.Debug, + EventIds.HealthCheckPublisherProcessingBegin, + "Running health check publishers"); + + private static readonly Action _healthCheckPublisherProcessingEnd = LoggerMessage.Define( + LogLevel.Debug, + EventIds.HealthCheckPublisherProcessingEnd, + "Health check publisher processing completed after {ElapsedMilliseconds}ms"); + + private static readonly Action _healthCheckPublisherBegin = LoggerMessage.Define( + LogLevel.Debug, + EventIds.HealthCheckPublisherBegin, + "Running health check publisher '{HealthCheckPublisher}'"); + + private static readonly Action _healthCheckPublisherEnd = LoggerMessage.Define( + LogLevel.Debug, + EventIds.HealthCheckPublisherEnd, + "Health check '{HealthCheckPublisher}' completed after {ElapsedMilliseconds}ms"); + + private static readonly Action _healthCheckPublisherError = LoggerMessage.Define( + LogLevel.Error, + EventIds.HealthCheckPublisherError, + "Health check {HealthCheckPublisher} threw an unhandled exception after {ElapsedMilliseconds}ms"); + + private static readonly Action _healthCheckPublisherTimeout = LoggerMessage.Define( + LogLevel.Error, + EventIds.HealthCheckPublisherTimeout, + "Health check {HealthCheckPublisher} was canceled after {ElapsedMilliseconds}ms"); + + public static void HealthCheckPublisherProcessingBegin(ILogger logger) + { + _healthCheckPublisherProcessingBegin(logger, null); + } + + public static void HealthCheckPublisherProcessingEnd(ILogger logger, TimeSpan duration, Exception exception = null) + { + _healthCheckPublisherProcessingEnd(logger, duration.TotalMilliseconds, exception); + } + + public static void HealthCheckPublisherBegin(ILogger logger, IHealthCheckPublisher publisher) + { + _healthCheckPublisherBegin(logger, publisher, null); + } + + public static void HealthCheckPublisherEnd(ILogger logger, IHealthCheckPublisher publisher, TimeSpan duration) + { + _healthCheckPublisherEnd(logger, publisher, duration.TotalMilliseconds, null); + } + + public static void HealthCheckPublisherError(ILogger logger, IHealthCheckPublisher publisher, TimeSpan duration, Exception exception) + { + _healthCheckPublisherError(logger, publisher, duration.TotalMilliseconds, exception); + } + + public static void HealthCheckPublisherTimeout(ILogger logger, IHealthCheckPublisher publisher, TimeSpan duration) + { + _healthCheckPublisherTimeout(logger, publisher, duration.TotalMilliseconds, null); + } + } + } +} diff --git a/src/HealthChecks/HealthChecks/src/HealthCheckPublisherOptions.cs b/src/HealthChecks/HealthChecks/src/HealthCheckPublisherOptions.cs new file mode 100644 index 000000000000..1313718af874 --- /dev/null +++ b/src/HealthChecks/HealthChecks/src/HealthCheckPublisherOptions.cs @@ -0,0 +1,84 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.Extensions.Diagnostics.HealthChecks +{ + /// + /// Options for the default service that executes instances. + /// + public sealed class HealthCheckPublisherOptions + { + private TimeSpan _delay; + private TimeSpan _period; + + public HealthCheckPublisherOptions() + { + _delay = TimeSpan.FromSeconds(5); + _period = TimeSpan.FromSeconds(30); + } + + /// + /// Gets or sets the initial delay applied after the application starts before executing + /// instances. The delay is applied once at startup, and does + /// not apply to subsequent iterations. The default value is 5 seconds. + /// + public TimeSpan Delay + { + get => _delay; + set + { + if (value == System.Threading.Timeout.InfiniteTimeSpan) + { + throw new ArgumentException($"The {nameof(Delay)} must not be infinite.", nameof(value)); + } + + _delay = value; + } + } + + /// + /// Gets or sets the period of execution. The default value is + /// 30 seconds. + /// + /// + /// The cannot be set to a value lower than 1 second. + /// + public TimeSpan Period + { + get => _period; + set + { + if (value < TimeSpan.FromSeconds(1)) + { + throw new ArgumentException($"The {nameof(Period)} must be greater than or equal to one second.", nameof(value)); + } + + if (value == System.Threading.Timeout.InfiniteTimeSpan) + { + throw new ArgumentException($"The {nameof(Period)} must not be infinite.", nameof(value)); + } + + _delay = value; + } + } + + /// + /// Gets or sets a predicate that is used to filter the set of health checks executed. + /// + /// + /// If is null, the health check publisher service will run all + /// registered health checks - this is the default behavior. To run a subset of health checks, + /// provide a function that filters the set of checks. The predicate will be evaluated each period. + /// + public Func Predicate { get; set; } + + /// + /// Gets or sets the timeout for executing the health checks an all + /// instances. Use to execute with no timeout. + /// The default value is 30 seconds. + /// + public TimeSpan Timeout { get; set; } = TimeSpan.FromSeconds(30); + } +} diff --git a/src/HealthChecks/HealthChecks/src/HealthCheckService.cs b/src/HealthChecks/HealthChecks/src/HealthCheckService.cs new file mode 100644 index 000000000000..e4a128148d48 --- /dev/null +++ b/src/HealthChecks/HealthChecks/src/HealthCheckService.cs @@ -0,0 +1,61 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.Extensions.Diagnostics.HealthChecks +{ + /// + /// A service which can be used to check the status of instances + /// registered in the application. + /// + /// + /// + /// The default implementation of is registered in the dependency + /// injection container as a singleton service by calling + /// . + /// + /// + /// The returned by + /// + /// provides a convenience API for registering health checks. + /// + /// + /// implementations can be registered through extension methods provided by + /// . + /// + /// + public abstract class HealthCheckService + { + /// + /// Runs all the health checks in the application and returns the aggregated status. + /// + /// A which can be used to cancel the health checks. + /// + /// A which will complete when all the health checks have been run, + /// yielding a containing the results. + /// + public Task CheckHealthAsync(CancellationToken cancellationToken = default) + { + return CheckHealthAsync(predicate: null, cancellationToken); + } + + /// + /// Runs the provided health checks and returns the aggregated status + /// + /// + /// A predicate that can be used to include health checks based on user-defined criteria. + /// + /// A which can be used to cancel the health checks. + /// + /// A which will complete when all the health checks have been run, + /// yielding a containing the results. + /// + public abstract Task CheckHealthAsync( + Func predicate, + CancellationToken cancellationToken = default); + } +} diff --git a/src/HealthChecks/HealthChecks/src/HealthCheckServiceOptions.cs b/src/HealthChecks/HealthChecks/src/HealthCheckServiceOptions.cs new file mode 100644 index 000000000000..b8dfdb9b40e0 --- /dev/null +++ b/src/HealthChecks/HealthChecks/src/HealthCheckServiceOptions.cs @@ -0,0 +1,18 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; + +namespace Microsoft.Extensions.Diagnostics.HealthChecks +{ + /// + /// Options for the default implementation of + /// + public sealed class HealthCheckServiceOptions + { + /// + /// Gets the health check registrations. + /// + public ICollection Registrations { get; } = new List(); + } +} diff --git a/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj b/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj new file mode 100644 index 000000000000..d0b1c97ef05a --- /dev/null +++ b/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj @@ -0,0 +1,26 @@ + + + Components for performing health checks in .NET applications + +Commonly Used Types: +Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckService +Microsoft.Extensions.Diagnostics.HealthChecks.IHealthChecksBuilder + + netstandard2.0 + $(NoWarn);CS1591 + true + diagnostics;healthchecks + + + + + + + + + + + + + + diff --git a/src/HealthChecks/HealthChecks/src/Properties/AssemblyInfo.cs b/src/HealthChecks/HealthChecks/src/Properties/AssemblyInfo.cs new file mode 100644 index 000000000000..13e969bfad29 --- /dev/null +++ b/src/HealthChecks/HealthChecks/src/Properties/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Microsoft.Extensions.Diagnostics.HealthChecks.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] \ No newline at end of file diff --git a/src/HealthChecks/HealthChecks/src/baseline.netcore.json b/src/HealthChecks/HealthChecks/src/baseline.netcore.json new file mode 100644 index 000000000000..cb2fe053f133 --- /dev/null +++ b/src/HealthChecks/HealthChecks/src/baseline.netcore.json @@ -0,0 +1,5 @@ +{ + "AssemblyIdentity": "Microsoft.Extensions.Diagnostics.HealthChecks, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "Types": [ + ] +} \ No newline at end of file diff --git a/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs b/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs new file mode 100644 index 000000000000..9ab991204ee1 --- /dev/null +++ b/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs @@ -0,0 +1,419 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Testing; +using Microsoft.Extensions.Options; +using Xunit; + +namespace Microsoft.Extensions.Diagnostics.HealthChecks +{ + public class DefaultHealthCheckServiceTest + { + [Fact] + public void Constructor_ThrowsUsefulExceptionForDuplicateNames() + { + // Arrange + // + // Doing this the old fashioned way so we can verify that the exception comes + // from the constructor. + var serviceCollection = new ServiceCollection(); + serviceCollection.AddLogging(); + serviceCollection.AddOptions(); + serviceCollection.AddHealthChecks() + .AddCheck("Foo", new DelegateHealthCheck(_ => Task.FromResult(HealthCheckResult.Healthy()))) + .AddCheck("Foo", new DelegateHealthCheck(_ => Task.FromResult(HealthCheckResult.Healthy()))) + .AddCheck("Bar", new DelegateHealthCheck(_ => Task.FromResult(HealthCheckResult.Healthy()))) + .AddCheck("Baz", new DelegateHealthCheck(_ => Task.FromResult(HealthCheckResult.Healthy()))) + .AddCheck("Baz", new DelegateHealthCheck(_ => Task.FromResult(HealthCheckResult.Healthy()))); + + var services = serviceCollection.BuildServiceProvider(); + + var scopeFactory = services.GetRequiredService(); + var options = services.GetRequiredService>(); + var logger = services.GetRequiredService>(); + + // Act + var exception = Assert.Throws(() => new DefaultHealthCheckService(scopeFactory, options, logger)); + + // Assert + Assert.StartsWith($"Duplicate health checks were registered with the name(s): Foo, Baz", exception.Message); + } + + [Fact] + public async Task CheckAsync_RunsAllChecksAndAggregatesResultsAsync() + { + const string DataKey = "Foo"; + const string DataValue = "Bar"; + const string DegradedMessage = "I'm not feeling so good"; + const string UnhealthyMessage = "Halp!"; + const string HealthyMessage = "Everything is A-OK"; + var exception = new Exception("Things are pretty bad!"); + + // Arrange + var data = new Dictionary() + { + { DataKey, DataValue } + }; + + var service = CreateHealthChecksService(b => + { + b.AddAsyncCheck("HealthyCheck", _ => Task.FromResult(HealthCheckResult.Healthy(HealthyMessage, data))); + b.AddAsyncCheck("DegradedCheck", _ => Task.FromResult(HealthCheckResult.Degraded(DegradedMessage))); + b.AddAsyncCheck("UnhealthyCheck", _ => Task.FromResult(HealthCheckResult.Unhealthy(UnhealthyMessage, exception))); + }); + + // Act + var results = await service.CheckHealthAsync(); + + // Assert + Assert.Collection( + results.Entries.OrderBy(kvp => kvp.Key), + actual => + { + Assert.Equal("DegradedCheck", actual.Key); + Assert.Equal(DegradedMessage, actual.Value.Description); + Assert.Equal(HealthStatus.Degraded, actual.Value.Status); + Assert.Null(actual.Value.Exception); + Assert.Empty(actual.Value.Data); + }, + actual => + { + Assert.Equal("HealthyCheck", actual.Key); + Assert.Equal(HealthyMessage, actual.Value.Description); + Assert.Equal(HealthStatus.Healthy, actual.Value.Status); + Assert.Null(actual.Value.Exception); + Assert.Collection(actual.Value.Data, item => + { + Assert.Equal(DataKey, item.Key); + Assert.Equal(DataValue, item.Value); + }); + }, + actual => + { + Assert.Equal("UnhealthyCheck", actual.Key); + Assert.Equal(UnhealthyMessage, actual.Value.Description); + Assert.Equal(HealthStatus.Unhealthy, actual.Value.Status); + Assert.Same(exception, actual.Value.Exception); + Assert.Empty(actual.Value.Data); + }); + } + + [Fact] + public async Task CheckAsync_RunsFilteredChecksAndAggregatesResultsAsync() + { + const string DataKey = "Foo"; + const string DataValue = "Bar"; + const string DegradedMessage = "I'm not feeling so good"; + const string UnhealthyMessage = "Halp!"; + const string HealthyMessage = "Everything is A-OK"; + var exception = new Exception("Things are pretty bad!"); + + // Arrange + var data = new Dictionary + { + { DataKey, DataValue } + }; + + var service = CreateHealthChecksService(b => + { + b.AddAsyncCheck("HealthyCheck", _ => Task.FromResult(HealthCheckResult.Healthy(HealthyMessage, data))); + b.AddAsyncCheck("DegradedCheck", _ => Task.FromResult(HealthCheckResult.Degraded(DegradedMessage))); + b.AddAsyncCheck("UnhealthyCheck", _ => Task.FromResult(HealthCheckResult.Unhealthy(UnhealthyMessage, exception))); + }); + + // Act + var results = await service.CheckHealthAsync(c => c.Name == "HealthyCheck"); + + // Assert + Assert.Collection(results.Entries, + actual => + { + Assert.Equal("HealthyCheck", actual.Key); + Assert.Equal(HealthyMessage, actual.Value.Description); + Assert.Equal(HealthStatus.Healthy, actual.Value.Status); + Assert.Null(actual.Value.Exception); + Assert.Collection(actual.Value.Data, item => + { + Assert.Equal(DataKey, item.Key); + Assert.Equal(DataValue, item.Value); + }); + }); + } + + [Fact] + public async Task CheckHealthAsync_SetsRegistrationForEachCheck() + { + // Arrange + var thrownException = new InvalidOperationException("Whoops!"); + var faultedException = new InvalidOperationException("Ohnoes!"); + + var service = CreateHealthChecksService(b => + { + b.AddCheck("A"); + b.AddCheck("B"); + b.AddCheck("C"); + }); + + // Act + var results = await service.CheckHealthAsync(); + + // Assert + Assert.Collection( + results.Entries, + actual => + { + Assert.Equal("A", actual.Key); + Assert.Collection( + actual.Value.Data, + kvp => Assert.Equal(kvp, new KeyValuePair("name", "A"))); + }, + actual => + { + Assert.Equal("B", actual.Key); + Assert.Collection( + actual.Value.Data, + kvp => Assert.Equal(kvp, new KeyValuePair("name", "B"))); + }, + actual => + { + Assert.Equal("C", actual.Key); + Assert.Collection( + actual.Value.Data, + kvp => Assert.Equal(kvp, new KeyValuePair("name", "C"))); + }); + } + + [Fact] + public async Task CheckHealthAsync_Cancellation_CanPropagate() + { + // Arrange + var insideCheck = new TaskCompletionSource(); + + var service = CreateHealthChecksService(b => + { + b.AddAsyncCheck("cancels", async ct => + { + insideCheck.SetResult(null); + + await Task.Delay(10000, ct); + return HealthCheckResult.Unhealthy(); + }); + }); + + var cancel = new CancellationTokenSource(); + var task = service.CheckHealthAsync(cancel.Token); + + // After this returns we know the check has started + await insideCheck.Task; + + cancel.Cancel(); + + // Act & Assert + await Assert.ThrowsAsync(async () => await task); + } + + [Fact] + public async Task CheckHealthAsync_ConvertsExceptionInHealthCheckToUnhealthyResultAsync() + { + // Arrange + var thrownException = new InvalidOperationException("Whoops!"); + var faultedException = new InvalidOperationException("Ohnoes!"); + + var service = CreateHealthChecksService(b => + { + b.AddAsyncCheck("Throws", ct => throw thrownException); + b.AddAsyncCheck("Faults", ct => Task.FromException(faultedException)); + b.AddAsyncCheck("Succeeds", ct => Task.FromResult(HealthCheckResult.Healthy())); + }); + + // Act + var results = await service.CheckHealthAsync(); + + // Assert + Assert.Collection( + results.Entries, + actual => + { + Assert.Equal("Throws", actual.Key); + Assert.Equal(thrownException.Message, actual.Value.Description); + Assert.Equal(HealthStatus.Unhealthy, actual.Value.Status); + Assert.Same(thrownException, actual.Value.Exception); + }, + actual => + { + Assert.Equal("Faults", actual.Key); + Assert.Equal(faultedException.Message, actual.Value.Description); + Assert.Equal(HealthStatus.Unhealthy, actual.Value.Status); + Assert.Same(faultedException, actual.Value.Exception); + }, + actual => + { + Assert.Equal("Succeeds", actual.Key); + Assert.Null(actual.Value.Description); + Assert.Equal(HealthStatus.Healthy, actual.Value.Status); + Assert.Null(actual.Value.Exception); + }); + } + + [Fact] + public async Task CheckHealthAsync_SetsUpALoggerScopeForEachCheck() + { + // Arrange + var sink = new TestSink(); + var check = new DelegateHealthCheck(cancellationToken => + { + Assert.Collection(sink.Scopes, + actual => + { + Assert.Equal(actual.LoggerName, typeof(DefaultHealthCheckService).FullName); + Assert.Collection((IEnumerable>)actual.Scope, + item => + { + Assert.Equal("HealthCheckName", item.Key); + Assert.Equal("TestScope", item.Value); + }); + }); + return Task.FromResult(HealthCheckResult.Healthy()); + }); + + var loggerFactory = new TestLoggerFactory(sink, enabled: true); + var service = CreateHealthChecksService(b => + { + // Override the logger factory for testing + b.Services.AddSingleton(loggerFactory); + + b.AddCheck("TestScope", check); + }); + + // Act + var results = await service.CheckHealthAsync(); + + // Assert + Assert.Collection(results.Entries, actual => + { + Assert.Equal("TestScope", actual.Key); + Assert.Equal(HealthStatus.Healthy, actual.Value.Status); + }); + } + + [Fact] + public async Task CheckHealthAsync_CheckCanDependOnTransientService() + { + // Arrange + var service = CreateHealthChecksService(b => + { + b.Services.AddTransient(); + + b.AddCheck("Test"); + }); + + // Act + var results = await service.CheckHealthAsync(); + + // Assert + Assert.Collection( + results.Entries, + actual => + { + Assert.Equal("Test", actual.Key); + Assert.Equal(HealthStatus.Healthy, actual.Value.Status); + }); + } + + [Fact] + public async Task CheckHealthAsync_CheckCanDependOnScopedService() + { + // Arrange + var service = CreateHealthChecksService(b => + { + b.Services.AddScoped(); + + b.AddCheck("Test"); + }); + + // Act + var results = await service.CheckHealthAsync(); + + // Assert + Assert.Collection( + results.Entries, + actual => + { + Assert.Equal("Test", actual.Key); + Assert.Equal(HealthStatus.Healthy, actual.Value.Status); + }); + } + + [Fact] + public async Task CheckHealthAsync_CheckCanDependOnSingletonService() + { + // Arrange + var service = CreateHealthChecksService(b => + { + b.Services.AddSingleton(); + + b.AddCheck("Test"); + }); + + // Act + var results = await service.CheckHealthAsync(); + + // Assert + Assert.Collection( + results.Entries, + actual => + { + Assert.Equal("Test", actual.Key); + Assert.Equal(HealthStatus.Healthy, actual.Value.Status); + }); + } + + private static DefaultHealthCheckService CreateHealthChecksService(Action configure) + { + var services = new ServiceCollection(); + services.AddLogging(); + services.AddOptions(); + + var builder = services.AddHealthChecks(); + if (configure != null) + { + configure(builder); + } + + return (DefaultHealthCheckService)services.BuildServiceProvider(validateScopes: true).GetRequiredService(); + } + + private class AnotherService { } + + private class CheckWithServiceDependency : IHealthCheck + { + public CheckWithServiceDependency(AnotherService _) + { + } + + public Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) + { + return Task.FromResult(HealthCheckResult.Healthy()); + } + } + + private class NameCapturingCheck : IHealthCheck + { + public Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) + { + var data = new Dictionary() + { + { "name", context.Registration.Name }, + }; + return Task.FromResult(HealthCheckResult.Healthy(data: data)); + } + } + } +} diff --git a/src/HealthChecks/HealthChecks/test/DependencyInjection/HealthChecksBuilderTest.cs b/src/HealthChecks/HealthChecks/test/DependencyInjection/HealthChecksBuilderTest.cs new file mode 100644 index 000000000000..4235f152a289 --- /dev/null +++ b/src/HealthChecks/HealthChecks/test/DependencyInjection/HealthChecksBuilderTest.cs @@ -0,0 +1,257 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using Microsoft.Extensions.Options; +using Xunit; + +namespace Microsoft.Extensions.DependencyInjection +{ + // Integration tests for extension methods on IHealthCheckBuilder + // + // We test the longest overload of each 'family' of Add...Check methods, since they chain to each other. + public class HealthChecksBuilderTest + { + [Fact] + public void AddCheck_Instance() + { + // Arrange + var instance = new DelegateHealthCheck((_) => + { + return Task.FromResult(HealthCheckResult.Healthy()); + }); + + var services = CreateServices(); + services.AddHealthChecks().AddCheck("test", failureStatus: HealthStatus.Degraded,tags: new[] { "tag", }, instance: instance); + + var serviceProvider = services.BuildServiceProvider(); + + // Act + var options = serviceProvider.GetRequiredService>().Value; + + // Assert + var registration = Assert.Single(options.Registrations); + Assert.Equal("test", registration.Name); + Assert.Equal(HealthStatus.Degraded, registration.FailureStatus); + Assert.Equal(new[] { "tag", }, registration.Tags); + Assert.Same(instance, registration.Factory(serviceProvider)); + } + + [Fact] + public void AddCheck_T_TypeActivated() + { + // Arrange + var services = CreateServices(); + services.AddHealthChecks().AddCheck("test", failureStatus: HealthStatus.Degraded, tags: new[] { "tag", }); + + var serviceProvider = services.BuildServiceProvider(); + + // Act + var options = serviceProvider.GetRequiredService>().Value; + + // Assert + var registration = Assert.Single(options.Registrations); + Assert.Equal("test", registration.Name); + Assert.Equal(HealthStatus.Degraded, registration.FailureStatus); + Assert.Equal(new[] { "tag", }, registration.Tags); + Assert.IsType(registration.Factory(serviceProvider)); + } + + [Fact] + public void AddCheck_T_Service() + { + // Arrange + var instance = new TestHealthCheck(); + + var services = CreateServices(); + services.AddSingleton(instance); + services.AddHealthChecks().AddCheck("test", failureStatus: HealthStatus.Degraded, tags: new[] { "tag", }); + + var serviceProvider = services.BuildServiceProvider(); + + // Act + var options = serviceProvider.GetRequiredService>().Value; + + // Assert + var registration = Assert.Single(options.Registrations); + Assert.Equal("test", registration.Name); + Assert.Equal(HealthStatus.Degraded, registration.FailureStatus); + Assert.Equal(new[] { "tag", }, registration.Tags); + Assert.Same(instance, registration.Factory(serviceProvider)); + } + + [Fact] + public void AddTypeActivatedCheck() + { + // Arrange + var services = CreateServices(); + services + .AddHealthChecks() + .AddTypeActivatedCheck("test", failureStatus: HealthStatus.Degraded, tags: new[] { "tag", }, args: new object[] { 5, "hi", }); + + var serviceProvider = services.BuildServiceProvider(); + + // Act + var options = serviceProvider.GetRequiredService>().Value; + + // Assert + var registration = Assert.Single(options.Registrations); + Assert.Equal("test", registration.Name); + Assert.Equal(HealthStatus.Degraded, registration.FailureStatus); + Assert.Equal(new[] { "tag", }, registration.Tags); + + var check = Assert.IsType(registration.Factory(serviceProvider)); + Assert.Equal(5, check.I); + Assert.Equal("hi", check.S); + } + + [Fact] + public void AddDelegateCheck_NoArg() + { + // Arrange + var services = CreateServices(); + services.AddHealthChecks().AddCheck("test", tags: new[] { "tag", }, check: () => + { + return HealthCheckResult.Healthy(); + }); + + var serviceProvider = services.BuildServiceProvider(); + + // Act + var options = serviceProvider.GetRequiredService>().Value; + + // Assert + var registration = Assert.Single(options.Registrations); + Assert.Equal("test", registration.Name); + Assert.Equal(HealthStatus.Unhealthy, registration.FailureStatus); + Assert.Equal(new[] { "tag", }, registration.Tags); + Assert.IsType(registration.Factory(serviceProvider)); + } + + [Fact] + public void AddDelegateCheck_CancellationToken() + { + // Arrange + var services = CreateServices(); + services.AddHealthChecks().AddCheck("test", (_) => + { + return HealthCheckResult.Degraded(); + }, tags: new[] { "tag", }); + + var serviceProvider = services.BuildServiceProvider(); + + // Act + var options = serviceProvider.GetRequiredService>().Value; + + // Assert + var registration = Assert.Single(options.Registrations); + Assert.Equal("test", registration.Name); + Assert.Equal(HealthStatus.Unhealthy, registration.FailureStatus); + Assert.Equal(new[] { "tag", }, registration.Tags); + Assert.IsType(registration.Factory(serviceProvider)); + } + + [Fact] + public void AddAsyncDelegateCheck_NoArg() + { + // Arrange + var services = CreateServices(); + services.AddHealthChecks().AddAsyncCheck("test", () => + { + return Task.FromResult(HealthCheckResult.Healthy()); + }, tags: new[] { "tag", }); + + var serviceProvider = services.BuildServiceProvider(); + + // Act + var options = serviceProvider.GetRequiredService>().Value; + + // Assert + var registration = Assert.Single(options.Registrations); + Assert.Equal("test", registration.Name); + Assert.Equal(HealthStatus.Unhealthy, registration.FailureStatus); + Assert.Equal(new[] { "tag", }, registration.Tags); + Assert.IsType(registration.Factory(serviceProvider)); + } + + [Fact] + public void AddAsyncDelegateCheck_CancellationToken() + { + // Arrange + var services = CreateServices(); + services.AddHealthChecks().AddAsyncCheck("test", (_) => + { + return Task.FromResult(HealthCheckResult.Unhealthy()); + }, tags: new[] { "tag", }); + + var serviceProvider = services.BuildServiceProvider(); + + // Act + var options = serviceProvider.GetRequiredService>().Value; + + // Assert + var registration = Assert.Single(options.Registrations); + Assert.Equal("test", registration.Name); + Assert.Equal(HealthStatus.Unhealthy, registration.FailureStatus); + Assert.Equal(new[] { "tag", }, registration.Tags); + Assert.IsType(registration.Factory(serviceProvider)); + } + + [Fact] + public void ChecksCanBeRegisteredInMultipleCallsToAddHealthChecks() + { + var services = new ServiceCollection(); + services + .AddHealthChecks() + .AddAsyncCheck("Foo", () => Task.FromResult(HealthCheckResult.Healthy())); + services + .AddHealthChecks() + .AddAsyncCheck("Bar", () => Task.FromResult(HealthCheckResult.Healthy())); + + // Act + var options = services.BuildServiceProvider().GetRequiredService>(); + + // Assert + Assert.Collection( + options.Value.Registrations, + actual => Assert.Equal("Foo", actual.Name), + actual => Assert.Equal("Bar", actual.Name)); + } + + private IServiceCollection CreateServices() + { + var services = new ServiceCollection(); + services.AddLogging(); + services.AddOptions(); + return services; + } + + private class TestHealthCheck : IHealthCheck + { + public Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) + { + throw new System.NotImplementedException(); + } + } + + private class TestHealthCheckWithArgs : IHealthCheck + { + public TestHealthCheckWithArgs(int i, string s) + { + I = i; + S = s; + } + + public int I { get; set; } + + public string S { get; set; } + + public Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) + { + throw new System.NotImplementedException(); + } + } + } +} diff --git a/src/HealthChecks/HealthChecks/test/DependencyInjection/ServiceCollectionExtensionsTest.cs b/src/HealthChecks/HealthChecks/test/DependencyInjection/ServiceCollectionExtensionsTest.cs new file mode 100644 index 000000000000..694a97628d67 --- /dev/null +++ b/src/HealthChecks/HealthChecks/test/DependencyInjection/ServiceCollectionExtensionsTest.cs @@ -0,0 +1,43 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Linq; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using Microsoft.Extensions.Hosting; +using Xunit; + +namespace Microsoft.Extensions.DependencyInjection +{ + public class ServiceCollectionExtensionsTest + { + [Fact] + public void AddHealthChecks_RegistersSingletonHealthCheckServiceIdempotently() + { + // Arrange + var services = new ServiceCollection(); + + // Act + services.AddHealthChecks(); + services.AddHealthChecks(); + + // Assert + Assert.Collection(services.OrderBy(s => s.ServiceType.FullName), + actual => + { + Assert.Equal(ServiceLifetime.Singleton, actual.Lifetime); + Assert.Equal(typeof(HealthCheckService), actual.ServiceType); + Assert.Equal(typeof(DefaultHealthCheckService), actual.ImplementationType); + Assert.Null(actual.ImplementationInstance); + Assert.Null(actual.ImplementationFactory); + }, + actual => + { + Assert.Equal(ServiceLifetime.Singleton, actual.Lifetime); + Assert.Equal(typeof(IHostedService), actual.ServiceType); + Assert.Equal(typeof(HealthCheckPublisherHostedService), actual.ImplementationType); + Assert.Null(actual.ImplementationInstance); + Assert.Null(actual.ImplementationFactory); + }); + } + } +} diff --git a/src/HealthChecks/HealthChecks/test/HealthCheckPublisherHostedServiceTest.cs b/src/HealthChecks/HealthChecks/test/HealthCheckPublisherHostedServiceTest.cs new file mode 100644 index 000000000000..94687efcb878 --- /dev/null +++ b/src/HealthChecks/HealthChecks/test/HealthCheckPublisherHostedServiceTest.cs @@ -0,0 +1,528 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Testing; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Testing; +using Xunit; + +namespace Microsoft.Extensions.Diagnostics.HealthChecks +{ + public class HealthCheckPublisherHostedServiceTest + { + [Fact] + public async Task StartAsync_WithoutPublishers_DoesNotStartTimer() + { + // Arrange + var publishers = new IHealthCheckPublisher[] + { + }; + + var service = CreateService(publishers); + + try + { + // Act + await service.StartAsync(); + + // Assert + Assert.False(service.IsTimerRunning); + Assert.False(service.IsStopping); + } + finally + { + await service.StopAsync(); + Assert.False(service.IsTimerRunning); + Assert.True(service.IsStopping); + } + } + + [Fact] + public async Task StartAsync_WithPublishers_StartsTimer() + { + // Arrange + var publishers = new IHealthCheckPublisher[] + { + new TestPublisher(), + }; + + var service = CreateService(publishers); + + try + { + // Act + await service.StartAsync(); + + // Assert + Assert.True(service.IsTimerRunning); + Assert.False(service.IsStopping); + } + finally + { + await service.StopAsync(); + Assert.False(service.IsTimerRunning); + Assert.True(service.IsStopping); + } + } + + [Fact] + public async Task StartAsync_WithPublishers_StartsTimer_RunsPublishers() + { + // Arrange + var unblock0 = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var unblock1 = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var unblock2 = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + var publishers = new TestPublisher[] + { + new TestPublisher() { Wait = unblock0.Task, }, + new TestPublisher() { Wait = unblock1.Task, }, + new TestPublisher() { Wait = unblock2.Task, }, + }; + + var service = CreateService(publishers, configure: (options) => + { + options.Delay = TimeSpan.FromMilliseconds(0); + }); + + try + { + // Act + await service.StartAsync(); + + await publishers[0].Started.TimeoutAfter(TimeSpan.FromSeconds(10)); + await publishers[1].Started.TimeoutAfter(TimeSpan.FromSeconds(10)); + await publishers[2].Started.TimeoutAfter(TimeSpan.FromSeconds(10)); + + unblock0.SetResult(null); + unblock1.SetResult(null); + unblock2.SetResult(null); + + // Assert + Assert.True(service.IsTimerRunning); + Assert.False(service.IsStopping); + } + finally + { + await service.StopAsync(); + Assert.False(service.IsTimerRunning); + Assert.True(service.IsStopping); + } + } + + [Fact] + public async Task StopAsync_CancelsExecution() + { + // Arrange + var unblock = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + var publishers = new TestPublisher[] + { + new TestPublisher() { Wait = unblock.Task, } + }; + + var service = CreateService(publishers); + + try + { + await service.StartAsync(); + + // Start execution + var running = service.RunAsync(); + + // Wait for the publisher to see the cancellation token + await publishers[0].Started.TimeoutAfter(TimeSpan.FromSeconds(10)); + Assert.Single(publishers[0].Entries); + + // Act + await service.StopAsync(); // Trigger cancellation + + // Assert + await AssertCancelledAsync(publishers[0].Entries[0].cancellationToken); + Assert.False(service.IsTimerRunning); + Assert.True(service.IsStopping); + + unblock.SetResult(null); + + await running.TimeoutAfter(TimeSpan.FromSeconds(10)); + } + finally + { + await service.StopAsync(); + Assert.False(service.IsTimerRunning); + Assert.True(service.IsStopping); + } + } + + [Fact] + public async Task RunAsync_WaitsForCompletion_Single() + { + // Arrange + var sink = new TestSink(); + + var unblock = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + var publishers = new TestPublisher[] + { + new TestPublisher() { Wait = unblock.Task, }, + }; + + var service = CreateService(publishers, sink: sink); + + try + { + await service.StartAsync(); + + // Act + var running = service.RunAsync(); + + await publishers[0].Started.TimeoutAfter(TimeSpan.FromSeconds(10)); + + unblock.SetResult(null); + + await running.TimeoutAfter(TimeSpan.FromSeconds(10)); + + // Assert + Assert.True(service.IsTimerRunning); + Assert.False(service.IsStopping); + + for (var i = 0; i < publishers.Length; i++) + { + var report = Assert.Single(publishers[i].Entries).report; + Assert.Equal(new[] { "one", "two", }, report.Entries.Keys.OrderBy(k => k)); + } + } + finally + { + await service.StopAsync(); + Assert.False(service.IsTimerRunning); + Assert.True(service.IsStopping); + } + + Assert.Collection( + sink.Writes, + entry => { Assert.Equal(HealthCheckPublisherHostedService.EventIds.HealthCheckPublisherProcessingBegin, entry.EventId); }, + entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckProcessingBegin, entry.EventId); }, + entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckBegin, entry.EventId); }, + entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckEnd, entry.EventId); }, + entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckBegin, entry.EventId); }, + entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckEnd, entry.EventId); }, + entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckProcessingEnd, entry.EventId); }, + entry => { Assert.Equal(HealthCheckPublisherHostedService.EventIds.HealthCheckPublisherBegin, entry.EventId); }, + entry => { Assert.Equal(HealthCheckPublisherHostedService.EventIds.HealthCheckPublisherEnd, entry.EventId); }, + entry => { Assert.Equal(HealthCheckPublisherHostedService.EventIds.HealthCheckPublisherProcessingEnd, entry.EventId); }); + } + + // Not testing logs here to avoid differences in logging order + [Fact] + public async Task RunAsync_WaitsForCompletion_Multiple() + { + // Arrange + var unblock0 = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var unblock1 = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var unblock2 = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + var publishers = new TestPublisher[] + { + new TestPublisher() { Wait = unblock0.Task, }, + new TestPublisher() { Wait = unblock1.Task, }, + new TestPublisher() { Wait = unblock2.Task, }, + }; + + var service = CreateService(publishers); + + try + { + await service.StartAsync(); + + // Act + var running = service.RunAsync(); + + await publishers[0].Started.TimeoutAfter(TimeSpan.FromSeconds(10)); + await publishers[1].Started.TimeoutAfter(TimeSpan.FromSeconds(10)); + await publishers[2].Started.TimeoutAfter(TimeSpan.FromSeconds(10)); + + unblock0.SetResult(null); + unblock1.SetResult(null); + unblock2.SetResult(null); + + await running.TimeoutAfter(TimeSpan.FromSeconds(10)); + + // Assert + Assert.True(service.IsTimerRunning); + Assert.False(service.IsStopping); + + for (var i = 0; i < publishers.Length; i++) + { + var report = Assert.Single(publishers[i].Entries).report; + Assert.Equal(new[] { "one", "two", }, report.Entries.Keys.OrderBy(k => k)); + } + } + finally + { + await service.StopAsync(); + Assert.False(service.IsTimerRunning); + Assert.True(service.IsStopping); + } + } + + [Fact] + public async Task RunAsync_PublishersCanTimeout() + { + // Arrange + var sink = new TestSink(); + var unblock = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + var publishers = new TestPublisher[] + { + new TestPublisher() { Wait = unblock.Task, }, + }; + + var service = CreateService(publishers, sink: sink, configure: (options) => + { + options.Timeout = TimeSpan.FromMilliseconds(50); + }); + + try + { + await service.StartAsync(); + + // Act + var running = service.RunAsync(); + + await publishers[0].Started.TimeoutAfter(TimeSpan.FromSeconds(10)); + + await AssertCancelledAsync(publishers[0].Entries[0].cancellationToken); + + unblock.SetResult(null); + + await running.TimeoutAfter(TimeSpan.FromSeconds(10)); + + // Assert + Assert.True(service.IsTimerRunning); + Assert.False(service.IsStopping); + } + finally + { + await service.StopAsync(); + Assert.False(service.IsTimerRunning); + Assert.True(service.IsStopping); + } + + Assert.Collection( + sink.Writes, + entry => { Assert.Equal(HealthCheckPublisherHostedService.EventIds.HealthCheckPublisherProcessingBegin, entry.EventId); }, + entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckProcessingBegin, entry.EventId); }, + entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckBegin, entry.EventId); }, + entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckEnd, entry.EventId); }, + entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckBegin, entry.EventId); }, + entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckEnd, entry.EventId); }, + entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckProcessingEnd, entry.EventId); }, + entry => { Assert.Equal(HealthCheckPublisherHostedService.EventIds.HealthCheckPublisherBegin, entry.EventId); }, + entry => { Assert.Equal(HealthCheckPublisherHostedService.EventIds.HealthCheckPublisherTimeout, entry.EventId); }, + entry => { Assert.Equal(HealthCheckPublisherHostedService.EventIds.HealthCheckPublisherProcessingEnd, entry.EventId); }); + } + + [Fact] + public async Task RunAsync_CanFilterHealthChecks() + { + // Arrange + var publishers = new TestPublisher[] + { + new TestPublisher(), + new TestPublisher(), + }; + + var service = CreateService(publishers, configure: (options) => + { + options.Predicate = (r) => r.Name == "one"; + }); + + try + { + await service.StartAsync(); + + // Act + await service.RunAsync().TimeoutAfter(TimeSpan.FromSeconds(10)); + + // Assert + for (var i = 0; i < publishers.Length; i++) + { + var report = Assert.Single(publishers[i].Entries).report; + Assert.Equal(new[] { "one", }, report.Entries.Keys.OrderBy(k => k)); + } + } + finally + { + await service.StopAsync(); + Assert.False(service.IsTimerRunning); + Assert.True(service.IsStopping); + } + } + + [Fact] + public async Task RunAsync_HandlesExceptions() + { + // Arrange + var sink = new TestSink(); + var publishers = new TestPublisher[] + { + new TestPublisher() { Exception = new InvalidTimeZoneException(), }, + }; + + var service = CreateService(publishers, sink: sink); + + try + { + await service.StartAsync(); + + // Act + await service.RunAsync().TimeoutAfter(TimeSpan.FromSeconds(10)); + + } + finally + { + await service.StopAsync(); + Assert.False(service.IsTimerRunning); + Assert.True(service.IsStopping); + } + + Assert.Collection( + sink.Writes, + entry => { Assert.Equal(HealthCheckPublisherHostedService.EventIds.HealthCheckPublisherProcessingBegin, entry.EventId); }, + entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckProcessingBegin, entry.EventId); }, + entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckBegin, entry.EventId); }, + entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckEnd, entry.EventId); }, + entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckBegin, entry.EventId); }, + entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckEnd, entry.EventId); }, + entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckProcessingEnd, entry.EventId); }, + entry => { Assert.Equal(HealthCheckPublisherHostedService.EventIds.HealthCheckPublisherBegin, entry.EventId); }, + entry => { Assert.Equal(HealthCheckPublisherHostedService.EventIds.HealthCheckPublisherError, entry.EventId); }, + entry => { Assert.Equal(HealthCheckPublisherHostedService.EventIds.HealthCheckPublisherProcessingEnd, entry.EventId); }); + } + + // Not testing logging here to avoid flaky ordering issues + [Fact] + public async Task RunAsync_HandlesExceptions_Multiple() + { + // Arrange + var sink = new TestSink(); + var publishers = new TestPublisher[] + { + new TestPublisher() { Exception = new InvalidTimeZoneException(), }, + new TestPublisher(), + new TestPublisher() { Exception = new InvalidTimeZoneException(), }, + }; + + var service = CreateService(publishers, sink: sink); + + try + { + await service.StartAsync(); + + // Act + await service.RunAsync().TimeoutAfter(TimeSpan.FromSeconds(10)); + + } + finally + { + await service.StopAsync(); + Assert.False(service.IsTimerRunning); + Assert.True(service.IsStopping); + } + } + + private HealthCheckPublisherHostedService CreateService( + IHealthCheckPublisher[] publishers, + Action configure = null, + TestSink sink = null) + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddOptions(); + serviceCollection.AddLogging(); + serviceCollection.AddHealthChecks() + .AddCheck("one", () => { return HealthCheckResult.Healthy(); }) + .AddCheck("two", () => { return HealthCheckResult.Healthy(); }); + + // Choosing big values for tests to make sure that we're not dependent on the defaults. + // All of the tests that rely on the timer will set their own values for speed. + serviceCollection.Configure(options => + { + options.Delay = TimeSpan.FromMinutes(5); + options.Period = TimeSpan.FromMinutes(5); + options.Timeout = TimeSpan.FromMinutes(5); + }); + + if (publishers != null) + { + for (var i = 0; i < publishers.Length; i++) + { + serviceCollection.AddSingleton(publishers[i]); + } + } + + if (configure != null) + { + serviceCollection.Configure(configure); + } + + if (sink != null) + { + serviceCollection.AddSingleton(new TestLoggerFactory(sink, enabled: true)); + } + + var services = serviceCollection.BuildServiceProvider(); + return services.GetServices().OfType< HealthCheckPublisherHostedService>().Single(); + } + + private static async Task AssertCancelledAsync(CancellationToken cancellationToken) + { + await Assert.ThrowsAsync(() => Task.Delay(TimeSpan.FromSeconds(10), cancellationToken)); + } + + private class TestPublisher : IHealthCheckPublisher + { + private TaskCompletionSource _started; + + public TestPublisher() + { + _started = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + } + + public List<(HealthReport report, CancellationToken cancellationToken)> Entries { get; } = new List<(HealthReport report, CancellationToken cancellationToken)>(); + + public Exception Exception { get; set; } + + public Task Started => _started.Task; + + public Task Wait { get; set; } + + public async Task PublishAsync(HealthReport report, CancellationToken cancellationToken) + { + Entries.Add((report, cancellationToken)); + + // Signal that we've started + _started.SetResult(null); + + if (Wait != null) + { + await Wait; + } + + if (Exception != null) + { + throw Exception; + } + + cancellationToken.ThrowIfCancellationRequested(); + } + } + } +} diff --git a/src/HealthChecks/HealthChecks/test/HealthReportTest.cs b/src/HealthChecks/HealthChecks/test/HealthReportTest.cs new file mode 100644 index 000000000000..07f8e5a8e372 --- /dev/null +++ b/src/HealthChecks/HealthChecks/test/HealthReportTest.cs @@ -0,0 +1,45 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using Xunit; + +namespace Microsoft.Extensions.Diagnostics.HealthChecks +{ + public class HealthReportTest + { + [Theory] + [InlineData(HealthStatus.Healthy)] + [InlineData(HealthStatus.Degraded)] + [InlineData(HealthStatus.Unhealthy)] + public void Status_MatchesWorstStatusInResults(HealthStatus status) + { + var result = new HealthReport(new Dictionary() + { + {"Foo", new HealthReportEntry(HealthStatus.Healthy, null,TimeSpan.MinValue, null, null) }, + {"Bar", new HealthReportEntry(HealthStatus.Healthy, null, TimeSpan.MinValue,null, null) }, + {"Baz", new HealthReportEntry(status, exception: null, description: null,duration:TimeSpan.MinValue, data: null) }, + {"Quick", new HealthReportEntry(HealthStatus.Healthy, null, TimeSpan.MinValue, null, null) }, + {"Quack", new HealthReportEntry(HealthStatus.Healthy, null, TimeSpan.MinValue, null, null) }, + {"Quock", new HealthReportEntry(HealthStatus.Healthy, null, TimeSpan.MinValue, null, null) }, + }, totalDuration: TimeSpan.MinValue); + + Assert.Equal(status, result.Status); + } + + [Theory] + [InlineData(200)] + [InlineData(300)] + [InlineData(400)] + public void TotalDuration_MatchesTotalDurationParameter(int milliseconds) + { + var result = new HealthReport(new Dictionary() + { + {"Foo", new HealthReportEntry(HealthStatus.Healthy, null,TimeSpan.MinValue, null, null) } + }, totalDuration: TimeSpan.FromMilliseconds(milliseconds)); + + Assert.Equal(TimeSpan.FromMilliseconds(milliseconds), result.TotalDuration); + } + } +} diff --git a/src/HealthChecks/HealthChecks/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests.csproj b/src/HealthChecks/HealthChecks/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests.csproj new file mode 100644 index 000000000000..e822f6a7a0ae --- /dev/null +++ b/src/HealthChecks/HealthChecks/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests.csproj @@ -0,0 +1,12 @@ + + + + $(StandardTestTfms) + Microsoft.Extensions.Diagnostics.HealthChecks + + + + + + + From e5fee282abf55ea72729256f7e532d0aa7b226ba Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Mon, 3 Dec 2018 16:17:41 -0800 Subject: [PATCH 019/183] Reorganize source code in preparation to move into aspnet/Extensions Prior to reorganization, this source code was found in https://github.com/aspnet/Localization/tree/dotnet/extensions@0bcac31dd705fb9db60723f5d7eaeffb728358f5 \n\nCommit migrated from https://github.com/dotnet/extensions/commit/c8e0450d7a0b8310f671c0c39bc19638b7b04c47 --- .../Abstractions/src/IStringLocalizer.cs | 45 ++ .../src/IStringLocalizerFactory.cs | 29 + .../Abstractions/src/IStringLocalizerOfT.cs | 14 + .../Abstractions/src/LocalizedString.cs | 90 +++ ...xtensions.Localization.Abstractions.csproj | 15 + .../src/StringLocalizerExtensions.cs | 74 ++ .../Abstractions/src/StringLocalizerOfT.cs | 67 ++ .../Abstractions/src/baseline.netcore.json | 413 +++++++++++ .../Localization/src/IResourceNamesCache.cs | 22 + .../src/Internal/AssemblyWrapper.cs | 28 + .../src/Internal/IResourceStringProvider.cs | 13 + ...eManagerStringLocalizerLoggerExtensions.cs | 27 + .../Internal/ResourceManagerStringProvider.cs | 80 ++ .../Localization/src/LocalizationOptions.cs | 16 + ...LocalizationServiceCollectionExtensions.cs | 76 ++ .../Microsoft.Extensions.Localization.csproj | 19 + .../src/Properties/AssemblyInfo.cs | 6 + .../src/Properties/Resources.Designer.cs | 62 ++ .../src/ResourceLocationAttribute.cs | 33 + .../src/ResourceManagerStringLocalizer.cs | 274 +++++++ .../ResourceManagerStringLocalizerFactory.cs | 270 +++++++ ...sourceManagerWithCultureStringLocalizer.cs | 164 +++++ .../Localization/src/ResourceNamesCache.cs | 23 + .../Localization/src/Resources.resx | 126 ++++ .../src/RootNamespaceAttribute.cs | 35 + .../Localization/src/baseline.netcore.json | 687 ++++++++++++++++++ ...lizationServiceCollectionExtensionsTest.cs | 68 ++ ...osoft.Extensions.Localization.Tests.csproj | 14 + ...sourceManagerStringLocalizerFactoryTest.cs | 296 ++++++++ .../ResourceManagerStringLocalizerTest.cs | 299 ++++++++ 30 files changed, 3385 insertions(+) create mode 100644 src/Localization/Abstractions/src/IStringLocalizer.cs create mode 100644 src/Localization/Abstractions/src/IStringLocalizerFactory.cs create mode 100644 src/Localization/Abstractions/src/IStringLocalizerOfT.cs create mode 100644 src/Localization/Abstractions/src/LocalizedString.cs create mode 100644 src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj create mode 100644 src/Localization/Abstractions/src/StringLocalizerExtensions.cs create mode 100644 src/Localization/Abstractions/src/StringLocalizerOfT.cs create mode 100644 src/Localization/Abstractions/src/baseline.netcore.json create mode 100644 src/Localization/Localization/src/IResourceNamesCache.cs create mode 100644 src/Localization/Localization/src/Internal/AssemblyWrapper.cs create mode 100644 src/Localization/Localization/src/Internal/IResourceStringProvider.cs create mode 100644 src/Localization/Localization/src/Internal/ResourceManagerStringLocalizerLoggerExtensions.cs create mode 100644 src/Localization/Localization/src/Internal/ResourceManagerStringProvider.cs create mode 100644 src/Localization/Localization/src/LocalizationOptions.cs create mode 100644 src/Localization/Localization/src/LocalizationServiceCollectionExtensions.cs create mode 100644 src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj create mode 100644 src/Localization/Localization/src/Properties/AssemblyInfo.cs create mode 100644 src/Localization/Localization/src/Properties/Resources.Designer.cs create mode 100644 src/Localization/Localization/src/ResourceLocationAttribute.cs create mode 100644 src/Localization/Localization/src/ResourceManagerStringLocalizer.cs create mode 100644 src/Localization/Localization/src/ResourceManagerStringLocalizerFactory.cs create mode 100644 src/Localization/Localization/src/ResourceManagerWithCultureStringLocalizer.cs create mode 100644 src/Localization/Localization/src/ResourceNamesCache.cs create mode 100644 src/Localization/Localization/src/Resources.resx create mode 100644 src/Localization/Localization/src/RootNamespaceAttribute.cs create mode 100644 src/Localization/Localization/src/baseline.netcore.json create mode 100644 src/Localization/Localization/test/LocalizationServiceCollectionExtensionsTest.cs create mode 100644 src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests.csproj create mode 100644 src/Localization/Localization/test/ResourceManagerStringLocalizerFactoryTest.cs create mode 100644 src/Localization/Localization/test/ResourceManagerStringLocalizerTest.cs diff --git a/src/Localization/Abstractions/src/IStringLocalizer.cs b/src/Localization/Abstractions/src/IStringLocalizer.cs new file mode 100644 index 000000000000..0e1145bbcab0 --- /dev/null +++ b/src/Localization/Abstractions/src/IStringLocalizer.cs @@ -0,0 +1,45 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Globalization; + +namespace Microsoft.Extensions.Localization +{ + /// + /// Represents a service that provides localized strings. + /// + public interface IStringLocalizer + { + /// + /// Gets the string resource with the given name. + /// + /// The name of the string resource. + /// The string resource as a . + LocalizedString this[string name] { get; } + + /// + /// Gets the string resource with the given name and formatted with the supplied arguments. + /// + /// The name of the string resource. + /// The values to format the string with. + /// The formatted string resource as a . + LocalizedString this[string name, params object[] arguments] { get; } + + /// + /// Gets all string resources. + /// + /// + /// A indicating whether to include strings from parent cultures. + /// + /// The strings. + IEnumerable GetAllStrings(bool includeParentCultures); + + /// + /// Creates a new for a specific . + /// + /// The to use. + /// A culture-specific . + IStringLocalizer WithCulture(CultureInfo culture); + } +} \ No newline at end of file diff --git a/src/Localization/Abstractions/src/IStringLocalizerFactory.cs b/src/Localization/Abstractions/src/IStringLocalizerFactory.cs new file mode 100644 index 000000000000..559fa69c3033 --- /dev/null +++ b/src/Localization/Abstractions/src/IStringLocalizerFactory.cs @@ -0,0 +1,29 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.Extensions.Localization +{ + /// + /// Represents a factory that creates instances. + /// + public interface IStringLocalizerFactory + { + /// + /// Creates an using the and + /// of the specified . + /// + /// The . + /// The . + IStringLocalizer Create(Type resourceSource); + + /// + /// Creates an . + /// + /// The base name of the resource to load strings from. + /// The location to load resources from. + /// The . + IStringLocalizer Create(string baseName, string location); + } +} \ No newline at end of file diff --git a/src/Localization/Abstractions/src/IStringLocalizerOfT.cs b/src/Localization/Abstractions/src/IStringLocalizerOfT.cs new file mode 100644 index 000000000000..695678a9008a --- /dev/null +++ b/src/Localization/Abstractions/src/IStringLocalizerOfT.cs @@ -0,0 +1,14 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.Extensions.Localization +{ + /// + /// Represents an that provides strings for . + /// + /// The to provide strings for. + public interface IStringLocalizer : IStringLocalizer + { + + } +} \ No newline at end of file diff --git a/src/Localization/Abstractions/src/LocalizedString.cs b/src/Localization/Abstractions/src/LocalizedString.cs new file mode 100644 index 000000000000..6556da40a0a5 --- /dev/null +++ b/src/Localization/Abstractions/src/LocalizedString.cs @@ -0,0 +1,90 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.Extensions.Localization +{ + /// + /// A locale specific string. + /// + public class LocalizedString + { + /// + /// Creates a new . + /// + /// The name of the string in the resource it was loaded from. + /// The actual string. + public LocalizedString(string name, string value) + : this(name, value, resourceNotFound: false) + { + } + + /// + /// Creates a new . + /// + /// The name of the string in the resource it was loaded from. + /// The actual string. + /// Whether the string was not found in a resource. Set this to true to indicate an alternate string value was used. + public LocalizedString(string name, string value, bool resourceNotFound) + : this(name, value, resourceNotFound, searchedLocation: null) + { + } + + /// + /// Creates a new . + /// + /// The name of the string in the resource it was loaded from. + /// The actual string. + /// Whether the string was not found in a resource. Set this to true to indicate an alternate string value was used. + /// The location which was searched for a localization value. + public LocalizedString(string name, string value, bool resourceNotFound, string searchedLocation) + { + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + Name = name; + Value = value; + ResourceNotFound = resourceNotFound; + SearchedLocation = searchedLocation; + } + + public static implicit operator string(LocalizedString localizedString) + { + return localizedString?.Value; + } + + /// + /// The name of the string in the resource it was loaded from. + /// + public string Name { get; } + + /// + /// The actual string. + /// + public string Value { get; } + + /// + /// Whether the string was not found in a resource. If true, an alternate string value was used. + /// + public bool ResourceNotFound { get; } + + /// + /// The location which was searched for a localization value. + /// + public string SearchedLocation { get; } + + /// + /// Returns the actual string. + /// + /// The actual string. + public override string ToString() => Value; + } +} \ No newline at end of file diff --git a/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj b/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj new file mode 100644 index 000000000000..8508eb071aad --- /dev/null +++ b/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj @@ -0,0 +1,15 @@ + + + + Microsoft .NET Extensions + Abstractions of application localization services. +Commonly used types: +Microsoft.Extensions.Localization.IStringLocalizer +Microsoft.Extensions.Localization.IStringLocalizer<T> + netstandard2.0 + $(NoWarn);CS1591 + true + localization + + + diff --git a/src/Localization/Abstractions/src/StringLocalizerExtensions.cs b/src/Localization/Abstractions/src/StringLocalizerExtensions.cs new file mode 100644 index 000000000000..bde47f74f33a --- /dev/null +++ b/src/Localization/Abstractions/src/StringLocalizerExtensions.cs @@ -0,0 +1,74 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; + +namespace Microsoft.Extensions.Localization +{ + public static class StringLocalizerExtensions + { + /// + /// Gets the string resource with the given name. + /// + /// The . + /// The name of the string resource. + /// The string resource as a . + public static LocalizedString GetString( + this IStringLocalizer stringLocalizer, + string name) + { + if (stringLocalizer == null) + { + throw new ArgumentNullException(nameof(stringLocalizer)); + } + + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + + return stringLocalizer[name]; + } + + /// + /// Gets the string resource with the given name and formatted with the supplied arguments. + /// + /// The . + /// The name of the string resource. + /// The values to format the string with. + /// The formatted string resource as a . + public static LocalizedString GetString( + this IStringLocalizer stringLocalizer, + string name, + params object[] arguments) + { + if (stringLocalizer == null) + { + throw new ArgumentNullException(nameof(stringLocalizer)); + } + + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + + return stringLocalizer[name, arguments]; + } + + /// + /// Gets all string resources including those for parent cultures. + /// + /// The . + /// The string resources. + public static IEnumerable GetAllStrings(this IStringLocalizer stringLocalizer) + { + if (stringLocalizer == null) + { + throw new ArgumentNullException(nameof(stringLocalizer)); + } + + return stringLocalizer.GetAllStrings(includeParentCultures: true); + } + } +} diff --git a/src/Localization/Abstractions/src/StringLocalizerOfT.cs b/src/Localization/Abstractions/src/StringLocalizerOfT.cs new file mode 100644 index 000000000000..131c1126ec3d --- /dev/null +++ b/src/Localization/Abstractions/src/StringLocalizerOfT.cs @@ -0,0 +1,67 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Globalization; + +namespace Microsoft.Extensions.Localization +{ + /// + /// Provides strings for . + /// + /// The to provide strings for. + public class StringLocalizer : IStringLocalizer + { + private IStringLocalizer _localizer; + + /// + /// Creates a new . + /// + /// The to use. + public StringLocalizer(IStringLocalizerFactory factory) + { + if (factory == null) + { + throw new ArgumentNullException(nameof(factory)); + } + + _localizer = factory.Create(typeof(TResourceSource)); + } + + /// + public virtual IStringLocalizer WithCulture(CultureInfo culture) => _localizer.WithCulture(culture); + + /// + public virtual LocalizedString this[string name] + { + get + { + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + + return _localizer[name]; + } + } + + /// + public virtual LocalizedString this[string name, params object[] arguments] + { + get + { + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + + return _localizer[name, arguments]; + } + } + + /// + public IEnumerable GetAllStrings(bool includeParentCultures) => + _localizer.GetAllStrings(includeParentCultures); + } +} \ No newline at end of file diff --git a/src/Localization/Abstractions/src/baseline.netcore.json b/src/Localization/Abstractions/src/baseline.netcore.json new file mode 100644 index 000000000000..02ba71db8e72 --- /dev/null +++ b/src/Localization/Abstractions/src/baseline.netcore.json @@ -0,0 +1,413 @@ +{ + "AssemblyIdentity": "Microsoft.Extensions.Localization.Abstractions, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "Types": [ + { + "Name": "Microsoft.Extensions.Localization.IStringLocalizer", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_Item", + "Parameters": [ + { + "Name": "name", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.Extensions.Localization.LocalizedString", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Item", + "Parameters": [ + { + "Name": "name", + "Type": "System.String" + }, + { + "Name": "arguments", + "Type": "System.Object[]", + "IsParams": true + } + ], + "ReturnType": "Microsoft.Extensions.Localization.LocalizedString", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetAllStrings", + "Parameters": [ + { + "Name": "includeParentCultures", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Collections.Generic.IEnumerable", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "WithCulture", + "Parameters": [ + { + "Name": "culture", + "Type": "System.Globalization.CultureInfo" + } + ], + "ReturnType": "Microsoft.Extensions.Localization.IStringLocalizer", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.Extensions.Localization.IStringLocalizerFactory", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "Create", + "Parameters": [ + { + "Name": "resourceSource", + "Type": "System.Type" + } + ], + "ReturnType": "Microsoft.Extensions.Localization.IStringLocalizer", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Create", + "Parameters": [ + { + "Name": "baseName", + "Type": "System.String" + }, + { + "Name": "location", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.Extensions.Localization.IStringLocalizer", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.Extensions.Localization.IStringLocalizer", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [ + "Microsoft.Extensions.Localization.IStringLocalizer" + ], + "Members": [], + "GenericParameters": [ + { + "ParameterName": "T", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.Extensions.Localization.LocalizedString", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "op_Implicit", + "Parameters": [ + { + "Name": "localizedString", + "Type": "Microsoft.Extensions.Localization.LocalizedString" + } + ], + "ReturnType": "System.String", + "Static": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Name", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Value", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ResourceNotFound", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SearchedLocation", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ToString", + "Parameters": [], + "ReturnType": "System.String", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "name", + "Type": "System.String" + }, + { + "Name": "value", + "Type": "System.String" + } + ], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "name", + "Type": "System.String" + }, + { + "Name": "value", + "Type": "System.String" + }, + { + "Name": "resourceNotFound", + "Type": "System.Boolean" + } + ], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "name", + "Type": "System.String" + }, + { + "Name": "value", + "Type": "System.String" + }, + { + "Name": "resourceNotFound", + "Type": "System.Boolean" + }, + { + "Name": "searchedLocation", + "Type": "System.String" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.Extensions.Localization.StringLocalizerExtensions", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "GetString", + "Parameters": [ + { + "Name": "stringLocalizer", + "Type": "Microsoft.Extensions.Localization.IStringLocalizer" + }, + { + "Name": "name", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.Extensions.Localization.LocalizedString", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetString", + "Parameters": [ + { + "Name": "stringLocalizer", + "Type": "Microsoft.Extensions.Localization.IStringLocalizer" + }, + { + "Name": "name", + "Type": "System.String" + }, + { + "Name": "arguments", + "Type": "System.Object[]", + "IsParams": true + } + ], + "ReturnType": "Microsoft.Extensions.Localization.LocalizedString", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetAllStrings", + "Parameters": [ + { + "Name": "stringLocalizer", + "Type": "Microsoft.Extensions.Localization.IStringLocalizer" + } + ], + "ReturnType": "System.Collections.Generic.IEnumerable", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.Extensions.Localization.StringLocalizer", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.Extensions.Localization.IStringLocalizer" + ], + "Members": [ + { + "Kind": "Method", + "Name": "get_Item", + "Parameters": [ + { + "Name": "name", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.Extensions.Localization.LocalizedString", + "Virtual": true, + "ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Item", + "Parameters": [ + { + "Name": "name", + "Type": "System.String" + }, + { + "Name": "arguments", + "Type": "System.Object[]", + "IsParams": true + } + ], + "ReturnType": "Microsoft.Extensions.Localization.LocalizedString", + "Virtual": true, + "ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetAllStrings", + "Parameters": [ + { + "Name": "includeParentCultures", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Collections.Generic.IEnumerable", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "WithCulture", + "Parameters": [ + { + "Name": "culture", + "Type": "System.Globalization.CultureInfo" + } + ], + "ReturnType": "Microsoft.Extensions.Localization.IStringLocalizer", + "Virtual": true, + "ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "factory", + "Type": "Microsoft.Extensions.Localization.IStringLocalizerFactory" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TResourceSource", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + } + ] + } + ] +} \ No newline at end of file diff --git a/src/Localization/Localization/src/IResourceNamesCache.cs b/src/Localization/Localization/src/IResourceNamesCache.cs new file mode 100644 index 000000000000..90d104aa68ff --- /dev/null +++ b/src/Localization/Localization/src/IResourceNamesCache.cs @@ -0,0 +1,22 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; + +namespace Microsoft.Extensions.Localization +{ + /// + /// Represents a cache of string names in resources. + /// + public interface IResourceNamesCache + { + /// + /// Adds a set of resource names to the cache by using the specified function, if the name does not already exist. + /// + /// The resource name to add string names for. + /// The function used to generate the string names for the resource. + /// The string names for the resource. + IList GetOrAdd(string name, Func> valueFactory); + } +} diff --git a/src/Localization/Localization/src/Internal/AssemblyWrapper.cs b/src/Localization/Localization/src/Internal/AssemblyWrapper.cs new file mode 100644 index 000000000000..b0c3c2bce1e4 --- /dev/null +++ b/src/Localization/Localization/src/Internal/AssemblyWrapper.cs @@ -0,0 +1,28 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; +using System.Reflection; + +namespace Microsoft.Extensions.Localization.Internal +{ + public class AssemblyWrapper + { + public AssemblyWrapper(Assembly assembly) + { + if (assembly == null) + { + throw new ArgumentNullException(nameof(assembly)); + } + + Assembly = assembly; + } + + public Assembly Assembly { get; } + + public virtual string FullName => Assembly.FullName; + + public virtual Stream GetManifestResourceStream(string name) => Assembly.GetManifestResourceStream(name); + } +} diff --git a/src/Localization/Localization/src/Internal/IResourceStringProvider.cs b/src/Localization/Localization/src/Internal/IResourceStringProvider.cs new file mode 100644 index 000000000000..b74bd80edaa3 --- /dev/null +++ b/src/Localization/Localization/src/Internal/IResourceStringProvider.cs @@ -0,0 +1,13 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Globalization; + +namespace Microsoft.Extensions.Localization.Internal +{ + public interface IResourceStringProvider + { + IList GetAllResourceStrings(CultureInfo culture, bool throwOnMissing); + } +} diff --git a/src/Localization/Localization/src/Internal/ResourceManagerStringLocalizerLoggerExtensions.cs b/src/Localization/Localization/src/Internal/ResourceManagerStringLocalizerLoggerExtensions.cs new file mode 100644 index 000000000000..456e07009e38 --- /dev/null +++ b/src/Localization/Localization/src/Internal/ResourceManagerStringLocalizerLoggerExtensions.cs @@ -0,0 +1,27 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Globalization; +using Microsoft.Extensions.Logging; + +namespace Microsoft.Extensions.Localization.Internal +{ + internal static class ResourceManagerStringLocalizerLoggerExtensions + { + private static readonly Action _searchedLocation; + + static ResourceManagerStringLocalizerLoggerExtensions() + { + _searchedLocation = LoggerMessage.Define( + LogLevel.Debug, + 1, + $"{nameof(ResourceManagerStringLocalizer)} searched for '{{Key}}' in '{{LocationSearched}}' with culture '{{Culture}}'."); + } + + public static void SearchedLocation(this ILogger logger, string key, string searchedLocation, CultureInfo culture) + { + _searchedLocation(logger, key, searchedLocation, culture, null); + } + } +} diff --git a/src/Localization/Localization/src/Internal/ResourceManagerStringProvider.cs b/src/Localization/Localization/src/Internal/ResourceManagerStringProvider.cs new file mode 100644 index 000000000000..9eef8c84a835 --- /dev/null +++ b/src/Localization/Localization/src/Internal/ResourceManagerStringProvider.cs @@ -0,0 +1,80 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Reflection; +using System.Resources; + +namespace Microsoft.Extensions.Localization.Internal +{ + public class ResourceManagerStringProvider : IResourceStringProvider + { + private readonly IResourceNamesCache _resourceNamesCache; + private readonly ResourceManager _resourceManager; + private readonly Assembly _assembly; + private readonly string _resourceBaseName; + + public ResourceManagerStringProvider( + IResourceNamesCache resourceCache, + ResourceManager resourceManager, + Assembly assembly, + string baseName) + { + _resourceManager = resourceManager; + _resourceNamesCache = resourceCache; + _assembly = assembly; + _resourceBaseName = baseName; + } + + private string GetResourceCacheKey(CultureInfo culture) + { + var resourceName = _resourceManager.BaseName; + + return $"Culture={culture.Name};resourceName={resourceName};Assembly={_assembly.FullName}"; + } + + private string GetResourceName(CultureInfo culture) + { + var resourceStreamName = _resourceBaseName; + if (!string.IsNullOrEmpty(culture.Name)) + { + resourceStreamName += "." + culture.Name; + } + resourceStreamName += ".resources"; + + return resourceStreamName; + } + + public IList GetAllResourceStrings(CultureInfo culture, bool throwOnMissing) + { + var cacheKey = GetResourceCacheKey(culture); + + return _resourceNamesCache.GetOrAdd(cacheKey, _ => + { + // We purposly don't dispose the ResourceSet because it causes an ObjectDisposedException when you try to read the values later. + var resourceSet = _resourceManager.GetResourceSet(culture, createIfNotExists: true, tryParents: false); + if (resourceSet == null) + { + if (throwOnMissing) + { + throw new MissingManifestResourceException(Resources.FormatLocalization_MissingManifest(GetResourceName(culture))); + } + else + { + return null; + } + } + + var names = new List(); + foreach (DictionaryEntry entry in resourceSet) + { + names.Add((string)entry.Key); + } + + return names; + }); + } + } +} diff --git a/src/Localization/Localization/src/LocalizationOptions.cs b/src/Localization/Localization/src/LocalizationOptions.cs new file mode 100644 index 000000000000..1b7408fe6774 --- /dev/null +++ b/src/Localization/Localization/src/LocalizationOptions.cs @@ -0,0 +1,16 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.Extensions.Localization +{ + /// + /// Provides programmatic configuration for localization. + /// + public class LocalizationOptions + { + /// + /// The relative path under application root where resource files are located. + /// + public string ResourcesPath { get; set; } = string.Empty; + } +} diff --git a/src/Localization/Localization/src/LocalizationServiceCollectionExtensions.cs b/src/Localization/Localization/src/LocalizationServiceCollectionExtensions.cs new file mode 100644 index 000000000000..111c1c40d954 --- /dev/null +++ b/src/Localization/Localization/src/LocalizationServiceCollectionExtensions.cs @@ -0,0 +1,76 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Localization; + +namespace Microsoft.Extensions.DependencyInjection +{ + /// + /// Extension methods for setting up localization services in an . + /// + public static class LocalizationServiceCollectionExtensions + { + /// + /// Adds services required for application localization. + /// + /// The to add the services to. + /// The so that additional calls can be chained. + public static IServiceCollection AddLocalization(this IServiceCollection services) + { + if (services == null) + { + throw new ArgumentNullException(nameof(services)); + } + + services.AddOptions(); + + AddLocalizationServices(services); + + return services; + } + + /// + /// Adds services required for application localization. + /// + /// The to add the services to. + /// + /// An to configure the . + /// + /// The so that additional calls can be chained. + public static IServiceCollection AddLocalization( + this IServiceCollection services, + Action setupAction) + { + if (services == null) + { + throw new ArgumentNullException(nameof(services)); + } + + if (setupAction == null) + { + throw new ArgumentNullException(nameof(setupAction)); + } + + AddLocalizationServices(services, setupAction); + + return services; + } + + // To enable unit testing + internal static void AddLocalizationServices(IServiceCollection services) + { + services.TryAddSingleton(); + services.TryAddTransient(typeof(IStringLocalizer<>), typeof(StringLocalizer<>)); + } + + internal static void AddLocalizationServices( + IServiceCollection services, + Action setupAction) + { + AddLocalizationServices(services); + services.Configure(setupAction); + } + } +} \ No newline at end of file diff --git a/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj b/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj new file mode 100644 index 000000000000..73365a15eb47 --- /dev/null +++ b/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj @@ -0,0 +1,19 @@ + + + + Microsoft .NET Extensions + Application localization services and default implementation based on ResourceManager to load localized assembly resources. + netstandard2.0 + $(NoWarn);CS1591 + true + localization + + + + + + + + + + diff --git a/src/Localization/Localization/src/Properties/AssemblyInfo.cs b/src/Localization/Localization/src/Properties/AssemblyInfo.cs new file mode 100644 index 000000000000..3e297b801e9b --- /dev/null +++ b/src/Localization/Localization/src/Properties/AssemblyInfo.cs @@ -0,0 +1,6 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Microsoft.Extensions.Localization.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] diff --git a/src/Localization/Localization/src/Properties/Resources.Designer.cs b/src/Localization/Localization/src/Properties/Resources.Designer.cs new file mode 100644 index 000000000000..1123d648ad25 --- /dev/null +++ b/src/Localization/Localization/src/Properties/Resources.Designer.cs @@ -0,0 +1,62 @@ +// +namespace Microsoft.Extensions.Localization +{ + using System.Globalization; + using System.Reflection; + using System.Resources; + + internal static class Resources + { + private static readonly ResourceManager _resourceManager + = new ResourceManager("Microsoft.Extensions.Localization.Resources", typeof(Resources).GetTypeInfo().Assembly); + + /// + /// The manifest '{0}' was not found. + /// + internal static string Localization_MissingManifest + { + get { return GetString("Localization_MissingManifest"); } + } + + /// + /// The manifest '{0}' was not found. + /// + internal static string FormatLocalization_MissingManifest(object p0) + { + return string.Format(CultureInfo.CurrentCulture, GetString("Localization_MissingManifest"), p0); + } + + /// + /// No manifests exist for the current culture. + /// + internal static string Localization_MissingManifest_Parent + { + get { return GetString("Localization_MissingManifest_Parent"); } + } + + /// + /// No manifests exist for the current culture. + /// + internal static string FormatLocalization_MissingManifest_Parent() + { + return GetString("Localization_MissingManifest_Parent"); + } + + private static string GetString(string name, params string[] formatterNames) + { + var value = _resourceManager.GetString(name); + + System.Diagnostics.Debug.Assert(value != null); + + if (formatterNames != null) + { + for (var i = 0; i < formatterNames.Length; i++) + { + value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}"); + } + } + + return value; + } + } +} diff --git a/src/Localization/Localization/src/ResourceLocationAttribute.cs b/src/Localization/Localization/src/ResourceLocationAttribute.cs new file mode 100644 index 000000000000..5bf281d90e36 --- /dev/null +++ b/src/Localization/Localization/src/ResourceLocationAttribute.cs @@ -0,0 +1,33 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.Extensions.Localization +{ + /// + /// Provides the location of resources for an Assembly. + /// + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false, Inherited = false)] + public class ResourceLocationAttribute : Attribute + { + /// + /// Creates a new . + /// + /// The location of resources for this Assembly. + public ResourceLocationAttribute(string resourceLocation) + { + if (string.IsNullOrEmpty(resourceLocation)) + { + throw new ArgumentNullException(nameof(resourceLocation)); + } + + ResourceLocation = resourceLocation; + } + + /// + /// The location of resources for this Assembly. + /// + public string ResourceLocation { get; } + } +} diff --git a/src/Localization/Localization/src/ResourceManagerStringLocalizer.cs b/src/Localization/Localization/src/ResourceManagerStringLocalizer.cs new file mode 100644 index 000000000000..e2e1a3f234fc --- /dev/null +++ b/src/Localization/Localization/src/ResourceManagerStringLocalizer.cs @@ -0,0 +1,274 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Globalization; +using System.Reflection; +using System.Resources; +using Microsoft.Extensions.Localization.Internal; +using Microsoft.Extensions.Logging; + +namespace Microsoft.Extensions.Localization +{ + /// + /// An that uses the and + /// to provide localized strings. + /// + /// This type is thread-safe. + public class ResourceManagerStringLocalizer : IStringLocalizer + { + private readonly ConcurrentDictionary _missingManifestCache = new ConcurrentDictionary(); + private readonly IResourceNamesCache _resourceNamesCache; + private readonly ResourceManager _resourceManager; + private readonly IResourceStringProvider _resourceStringProvider; + private readonly string _resourceBaseName; + private readonly ILogger _logger; + + /// + /// Creates a new . + /// + /// The to read strings from. + /// The that contains the strings as embedded resources. + /// The base name of the embedded resource that contains the strings. + /// Cache of the list of strings for a given resource assembly name. + /// The . + public ResourceManagerStringLocalizer( + ResourceManager resourceManager, + Assembly resourceAssembly, + string baseName, + IResourceNamesCache resourceNamesCache, + ILogger logger) + : this( + resourceManager, + new AssemblyWrapper(resourceAssembly), + baseName, + resourceNamesCache, + logger) + { + } + + /// + /// Intended for testing purposes only. + /// + public ResourceManagerStringLocalizer( + ResourceManager resourceManager, + AssemblyWrapper resourceAssemblyWrapper, + string baseName, + IResourceNamesCache resourceNamesCache, + ILogger logger) + : this( + resourceManager, + new ResourceManagerStringProvider( + resourceNamesCache, + resourceManager, + resourceAssemblyWrapper.Assembly, + baseName), + baseName, + resourceNamesCache, + logger) + { + } + + /// + /// Intended for testing purposes only. + /// + public ResourceManagerStringLocalizer( + ResourceManager resourceManager, + IResourceStringProvider resourceStringProvider, + string baseName, + IResourceNamesCache resourceNamesCache, + ILogger logger) + { + if (resourceManager == null) + { + throw new ArgumentNullException(nameof(resourceManager)); + } + + if (resourceStringProvider == null) + { + throw new ArgumentNullException(nameof(resourceStringProvider)); + } + + if (baseName == null) + { + throw new ArgumentNullException(nameof(baseName)); + } + + if (resourceNamesCache == null) + { + throw new ArgumentNullException(nameof(resourceNamesCache)); + } + + if (logger == null) + { + throw new ArgumentNullException(nameof(logger)); + } + + _resourceStringProvider = resourceStringProvider; + _resourceManager = resourceManager; + _resourceBaseName = baseName; + _resourceNamesCache = resourceNamesCache; + _logger = logger; + } + + /// + public virtual LocalizedString this[string name] + { + get + { + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + + var value = GetStringSafely(name, null); + + return new LocalizedString(name, value ?? name, resourceNotFound: value == null, searchedLocation: _resourceBaseName); + } + } + + /// + public virtual LocalizedString this[string name, params object[] arguments] + { + get + { + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + + var format = GetStringSafely(name, null); + var value = string.Format(format ?? name, arguments); + + return new LocalizedString(name, value, resourceNotFound: format == null, searchedLocation: _resourceBaseName); + } + } + + /// + /// Creates a new for a specific . + /// + /// The to use. + /// A culture-specific . + public IStringLocalizer WithCulture(CultureInfo culture) + { + return culture == null + ? new ResourceManagerStringLocalizer( + _resourceManager, + _resourceStringProvider, + _resourceBaseName, + _resourceNamesCache, + _logger) + : new ResourceManagerWithCultureStringLocalizer( + _resourceManager, + _resourceStringProvider, + _resourceBaseName, + _resourceNamesCache, + culture, + _logger); + } + + /// + public virtual IEnumerable GetAllStrings(bool includeParentCultures) => + GetAllStrings(includeParentCultures, CultureInfo.CurrentUICulture); + + /// + /// Returns all strings in the specified culture. + /// + /// + /// The to get strings for. + /// The strings. + protected IEnumerable GetAllStrings(bool includeParentCultures, CultureInfo culture) + { + if (culture == null) + { + throw new ArgumentNullException(nameof(culture)); + } + + var resourceNames = includeParentCultures + ? GetResourceNamesFromCultureHierarchy(culture) + : _resourceStringProvider.GetAllResourceStrings(culture, true); + + foreach (var name in resourceNames) + { + var value = GetStringSafely(name, culture); + yield return new LocalizedString(name, value ?? name, resourceNotFound: value == null, searchedLocation: _resourceBaseName); + } + } + + /// + /// Gets a resource string from the and returns null instead of + /// throwing exceptions if a match isn't found. + /// + /// The name of the string resource. + /// The to get the string for. + /// The resource string, or null if none was found. + protected string GetStringSafely(string name, CultureInfo culture) + { + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + + var keyCulture = culture ?? CultureInfo.CurrentUICulture; + + var cacheKey = $"name={name}&culture={keyCulture.Name}"; + + _logger.SearchedLocation(name, _resourceBaseName, keyCulture); + + if (_missingManifestCache.ContainsKey(cacheKey)) + { + return null; + } + + try + { + return culture == null ? _resourceManager.GetString(name) : _resourceManager.GetString(name, culture); + } + catch (MissingManifestResourceException) + { + _missingManifestCache.TryAdd(cacheKey, null); + return null; + } + } + + private IEnumerable GetResourceNamesFromCultureHierarchy(CultureInfo startingCulture) + { + var currentCulture = startingCulture; + var resourceNames = new HashSet(); + + var hasAnyCultures = false; + + while (true) + { + + var cultureResourceNames = _resourceStringProvider.GetAllResourceStrings(currentCulture, false); + + if (cultureResourceNames != null) + { + foreach (var resourceName in cultureResourceNames) + { + resourceNames.Add(resourceName); + } + hasAnyCultures = true; + } + + if (currentCulture == currentCulture.Parent) + { + // currentCulture begat currentCulture, probably time to leave + break; + } + + currentCulture = currentCulture.Parent; + } + + if (!hasAnyCultures) + { + throw new MissingManifestResourceException(Resources.Localization_MissingManifest_Parent); + } + + return resourceNames; + } + } +} \ No newline at end of file diff --git a/src/Localization/Localization/src/ResourceManagerStringLocalizerFactory.cs b/src/Localization/Localization/src/ResourceManagerStringLocalizerFactory.cs new file mode 100644 index 000000000000..2eb737eaa74c --- /dev/null +++ b/src/Localization/Localization/src/ResourceManagerStringLocalizerFactory.cs @@ -0,0 +1,270 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Concurrent; +using System.IO; +using System.Reflection; +using System.Resources; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace Microsoft.Extensions.Localization +{ + /// + /// An that creates instances of . + /// + /// + /// offers multiple ways to set the relative path of + /// resources to be used. They are, in order of precedence: + /// -> -> the project root. + /// + public class ResourceManagerStringLocalizerFactory : IStringLocalizerFactory + { + private readonly IResourceNamesCache _resourceNamesCache = new ResourceNamesCache(); + private readonly ConcurrentDictionary _localizerCache = + new ConcurrentDictionary(); + private readonly string _resourcesRelativePath; + private readonly ILoggerFactory _loggerFactory; + + /// + /// Creates a new . + /// + /// The . + /// The . + public ResourceManagerStringLocalizerFactory( + IOptions localizationOptions, + ILoggerFactory loggerFactory) + { + if (localizationOptions == null) + { + throw new ArgumentNullException(nameof(localizationOptions)); + } + + if (loggerFactory == null) + { + throw new ArgumentNullException(nameof(loggerFactory)); + } + + _resourcesRelativePath = localizationOptions.Value.ResourcesPath ?? string.Empty; + _loggerFactory = loggerFactory; + + if (!string.IsNullOrEmpty(_resourcesRelativePath)) + { + _resourcesRelativePath = _resourcesRelativePath.Replace(Path.AltDirectorySeparatorChar, '.') + .Replace(Path.DirectorySeparatorChar, '.') + "."; + } + } + + /// + /// Gets the resource prefix used to look up the resource. + /// + /// The type of the resource to be looked up. + /// The prefix for resource lookup. + protected virtual string GetResourcePrefix(TypeInfo typeInfo) + { + if (typeInfo == null) + { + throw new ArgumentNullException(nameof(typeInfo)); + } + + return GetResourcePrefix(typeInfo, GetRootNamespace(typeInfo.Assembly), GetResourcePath(typeInfo.Assembly)); + } + + /// + /// Gets the resource prefix used to look up the resource. + /// + /// The type of the resource to be looked up. + /// The base namespace of the application. + /// The folder containing all resources. + /// The prefix for resource lookup. + /// + /// For the type "Sample.Controllers.Home" if there's a resourceRelativePath return + /// "Sample.Resourcepath.Controllers.Home" if there isn't one then it would return "Sample.Controllers.Home". + /// + protected virtual string GetResourcePrefix(TypeInfo typeInfo, string baseNamespace, string resourcesRelativePath) + { + if (typeInfo == null) + { + throw new ArgumentNullException(nameof(typeInfo)); + } + + if (string.IsNullOrEmpty(baseNamespace)) + { + throw new ArgumentNullException(nameof(baseNamespace)); + } + + if (string.IsNullOrEmpty(resourcesRelativePath)) + { + return typeInfo.FullName; + } + else + { + // This expectation is defined by dotnet's automatic resource storage. + // We have to conform to "{RootNamespace}.{ResourceLocation}.{FullTypeName - AssemblyName}". + var assemblyName = new AssemblyName(typeInfo.Assembly.FullName).Name; + return baseNamespace + "." + resourcesRelativePath + TrimPrefix(typeInfo.FullName, assemblyName + "."); + } + } + + /// + /// Gets the resource prefix used to look up the resource. + /// + /// The name of the resource to be looked up + /// The base namespace of the application. + /// The prefix for resource lookup. + protected virtual string GetResourcePrefix(string baseResourceName, string baseNamespace) + { + if (string.IsNullOrEmpty(baseResourceName)) + { + throw new ArgumentNullException(nameof(baseResourceName)); + } + + if (string.IsNullOrEmpty(baseNamespace)) + { + throw new ArgumentNullException(nameof(baseNamespace)); + } + + var assemblyName = new AssemblyName(baseNamespace); + var assembly = Assembly.Load(assemblyName); + var rootNamespace = GetRootNamespace(assembly); + var resourceLocation = GetResourcePath(assembly); + var locationPath = rootNamespace + "." + resourceLocation; + + baseResourceName = locationPath + TrimPrefix(baseResourceName, baseNamespace + "."); + + return baseResourceName; + } + + /// + /// Creates a using the and + /// of the specified . + /// + /// The . + /// The . + public IStringLocalizer Create(Type resourceSource) + { + if (resourceSource == null) + { + throw new ArgumentNullException(nameof(resourceSource)); + } + + var typeInfo = resourceSource.GetTypeInfo(); + + var baseName = GetResourcePrefix(typeInfo); + + var assembly = typeInfo.Assembly; + + return _localizerCache.GetOrAdd(baseName, _ => CreateResourceManagerStringLocalizer(assembly, baseName)); + } + + /// + /// Creates a . + /// + /// The base name of the resource to load strings from. + /// The location to load resources from. + /// The . + public IStringLocalizer Create(string baseName, string location) + { + if (baseName == null) + { + throw new ArgumentNullException(nameof(baseName)); + } + + if (location == null) + { + throw new ArgumentNullException(nameof(location)); + } + + return _localizerCache.GetOrAdd($"B={baseName},L={location}", _ => + { + var assemblyName = new AssemblyName(location); + var assembly = Assembly.Load(assemblyName); + baseName = GetResourcePrefix(baseName, location); + + return CreateResourceManagerStringLocalizer(assembly, baseName); + }); + } + + /// Creates a for the given input. + /// The assembly to create a for. + /// The base name of the resource to search for. + /// A for the given and . + /// This method is virtual for testing purposes only. + protected virtual ResourceManagerStringLocalizer CreateResourceManagerStringLocalizer( + Assembly assembly, + string baseName) + { + return new ResourceManagerStringLocalizer( + new ResourceManager(baseName, assembly), + assembly, + baseName, + _resourceNamesCache, + _loggerFactory.CreateLogger()); + } + + /// + /// Gets the resource prefix used to look up the resource. + /// + /// The general location of the resource. + /// The base name of the resource. + /// The location of the resource within . + /// The resource prefix used to look up the resource. + protected virtual string GetResourcePrefix(string location, string baseName, string resourceLocation) + { + // Re-root the base name if a resources path is set + return location + "." + resourceLocation + TrimPrefix(baseName, location + "."); + } + + /// Gets a from the provided . + /// The assembly to get a from. + /// The associated with the given . + /// This method is protected and virtual for testing purposes only. + protected virtual ResourceLocationAttribute GetResourceLocationAttribute(Assembly assembly) + { + return assembly.GetCustomAttribute(); + } + + /// Gets a from the provided . + /// The assembly to get a from. + /// The associated with the given . + /// This method is protected and virtual for testing purposes only. + protected virtual RootNamespaceAttribute GetRootNamespaceAttribute(Assembly assembly) + { + return assembly.GetCustomAttribute(); + } + + private string GetRootNamespace(Assembly assembly) + { + var rootNamespaceAttribute = GetRootNamespaceAttribute(assembly); + + return rootNamespaceAttribute?.RootNamespace ?? + new AssemblyName(assembly.FullName).Name; + } + + private string GetResourcePath(Assembly assembly) + { + var resourceLocationAttribute = GetResourceLocationAttribute(assembly); + + // If we don't have an attribute assume all assemblies use the same resource location. + var resourceLocation = resourceLocationAttribute == null + ? _resourcesRelativePath + : resourceLocationAttribute.ResourceLocation + "."; + resourceLocation = resourceLocation + .Replace(Path.DirectorySeparatorChar, '.') + .Replace(Path.AltDirectorySeparatorChar, '.'); + + return resourceLocation; + } + + private static string TrimPrefix(string name, string prefix) + { + if (name.StartsWith(prefix, StringComparison.Ordinal)) + { + return name.Substring(prefix.Length); + } + + return name; + } + } +} \ No newline at end of file diff --git a/src/Localization/Localization/src/ResourceManagerWithCultureStringLocalizer.cs b/src/Localization/Localization/src/ResourceManagerWithCultureStringLocalizer.cs new file mode 100644 index 000000000000..65b6ae242c62 --- /dev/null +++ b/src/Localization/Localization/src/ResourceManagerWithCultureStringLocalizer.cs @@ -0,0 +1,164 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Reflection; +using System.Resources; +using Microsoft.Extensions.Localization.Internal; +using Microsoft.Extensions.Logging; + +namespace Microsoft.Extensions.Localization +{ + /// + /// An that uses the and + /// to provide localized strings for a specific . + /// + public class ResourceManagerWithCultureStringLocalizer : ResourceManagerStringLocalizer + { + private readonly string _resourceBaseName; + private readonly CultureInfo _culture; + + /// + /// Creates a new . + /// + /// The to read strings from. + /// The that can find the resources. + /// The base name of the embedded resource that contains the strings. + /// Cache of the list of strings for a given resource assembly name. + /// The specific to use. + /// The . + internal ResourceManagerWithCultureStringLocalizer( + ResourceManager resourceManager, + IResourceStringProvider resourceStringProvider, + string baseName, + IResourceNamesCache resourceNamesCache, + CultureInfo culture, + ILogger logger) + : base(resourceManager, resourceStringProvider, baseName, resourceNamesCache, logger) + { + if (resourceManager == null) + { + throw new ArgumentNullException(nameof(resourceManager)); + } + + if (resourceStringProvider == null) + { + throw new ArgumentNullException(nameof(resourceStringProvider)); + } + + if (baseName == null) + { + throw new ArgumentNullException(nameof(baseName)); + } + + if (resourceNamesCache == null) + { + throw new ArgumentNullException(nameof(resourceNamesCache)); + } + + if (culture == null) + { + throw new ArgumentNullException(nameof(culture)); + } + + if (logger == null) + { + throw new ArgumentNullException(nameof(logger)); + } + + _resourceBaseName = baseName; + _culture = culture; + } + + /// + /// Creates a new . + /// + /// The to read strings from. + /// The that contains the strings as embedded resources. + /// The base name of the embedded resource that contains the strings. + /// Cache of the list of strings for a given resource assembly name. + /// The specific to use. + /// The . + public ResourceManagerWithCultureStringLocalizer( + ResourceManager resourceManager, + Assembly resourceAssembly, + string baseName, + IResourceNamesCache resourceNamesCache, + CultureInfo culture, + ILogger logger) + : base(resourceManager, resourceAssembly, baseName, resourceNamesCache, logger) + { + if (resourceManager == null) + { + throw new ArgumentNullException(nameof(resourceManager)); + } + + if (resourceAssembly == null) + { + throw new ArgumentNullException(nameof(resourceAssembly)); + } + + if (baseName == null) + { + throw new ArgumentNullException(nameof(baseName)); + } + + if (resourceNamesCache == null) + { + throw new ArgumentNullException(nameof(resourceNamesCache)); + } + + if (culture == null) + { + throw new ArgumentNullException(nameof(culture)); + } + + if (logger == null) + { + throw new ArgumentNullException(nameof(logger)); + } + + _resourceBaseName = baseName; + _culture = culture; + } + + /// + public override LocalizedString this[string name] + { + get + { + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + + var value = GetStringSafely(name, _culture); + + return new LocalizedString(name, value ?? name, resourceNotFound: value == null, searchedLocation: _resourceBaseName); + } + } + + /// + public override LocalizedString this[string name, params object[] arguments] + { + get + { + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + + var format = GetStringSafely(name, _culture); + var value = string.Format(_culture, format ?? name, arguments); + + return new LocalizedString(name, value, resourceNotFound: format == null, searchedLocation: _resourceBaseName); + } + } + + /// + public override IEnumerable GetAllStrings(bool includeParentCultures) => + GetAllStrings(includeParentCultures, _culture); + } +} \ No newline at end of file diff --git a/src/Localization/Localization/src/ResourceNamesCache.cs b/src/Localization/Localization/src/ResourceNamesCache.cs new file mode 100644 index 000000000000..86e94c102a8c --- /dev/null +++ b/src/Localization/Localization/src/ResourceNamesCache.cs @@ -0,0 +1,23 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; + +namespace Microsoft.Extensions.Localization +{ + /// + /// An implementation of backed by a . + /// + public class ResourceNamesCache : IResourceNamesCache + { + private readonly ConcurrentDictionary> _cache = new ConcurrentDictionary>(); + + /// + public IList GetOrAdd(string name, Func> valueFactory) + { + return _cache.GetOrAdd(name, valueFactory); + } + } +} diff --git a/src/Localization/Localization/src/Resources.resx b/src/Localization/Localization/src/Resources.resx new file mode 100644 index 000000000000..b679f04664c0 --- /dev/null +++ b/src/Localization/Localization/src/Resources.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + The manifest '{0}' was not found. + + + No manifests exist for the current culture. + + \ No newline at end of file diff --git a/src/Localization/Localization/src/RootNamespaceAttribute.cs b/src/Localization/Localization/src/RootNamespaceAttribute.cs new file mode 100644 index 000000000000..f28b4ea1fd70 --- /dev/null +++ b/src/Localization/Localization/src/RootNamespaceAttribute.cs @@ -0,0 +1,35 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.Extensions.Localization +{ + /// + /// Provides the RootNamespace of an Assembly. The RootNamespace of the assembly is used by Localization to + /// determine the resource name to look for when RootNamespace differs from the AssemblyName. + /// + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false, Inherited = false)] + public class RootNamespaceAttribute : Attribute + { + /// + /// Creates a new . + /// + /// The RootNamespace for this Assembly. + public RootNamespaceAttribute(string rootNamespace) + { + if (string.IsNullOrEmpty(rootNamespace)) + { + throw new ArgumentNullException(nameof(rootNamespace)); + } + + RootNamespace = rootNamespace; + } + + /// + /// The RootNamespace of this Assembly. The RootNamespace of the assembly is used by Localization to + /// determine the resource name to look for when RootNamespace differs from the AssemblyName. + /// + public string RootNamespace { get; } + } +} diff --git a/src/Localization/Localization/src/baseline.netcore.json b/src/Localization/Localization/src/baseline.netcore.json new file mode 100644 index 000000000000..860db76899cb --- /dev/null +++ b/src/Localization/Localization/src/baseline.netcore.json @@ -0,0 +1,687 @@ +{ + "AssemblyIdentity": "Microsoft.Extensions.Localization, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "Types": [ + { + "Name": "Microsoft.Extensions.DependencyInjection.LocalizationServiceCollectionExtensions", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "AddLocalization", + "Parameters": [ + { + "Name": "services", + "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" + } + ], + "ReturnType": "Microsoft.Extensions.DependencyInjection.IServiceCollection", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddLocalization", + "Parameters": [ + { + "Name": "services", + "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" + }, + { + "Name": "setupAction", + "Type": "System.Action" + } + ], + "ReturnType": "Microsoft.Extensions.DependencyInjection.IServiceCollection", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.Extensions.Localization.IResourceNamesCache", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "GetOrAdd", + "Parameters": [ + { + "Name": "name", + "Type": "System.String" + }, + { + "Name": "valueFactory", + "Type": "System.Func>" + } + ], + "ReturnType": "System.Collections.Generic.IList", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.Extensions.Localization.LocalizationOptions", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_ResourcesPath", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ResourcesPath", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.Extensions.Localization.ResourceLocationAttribute", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "System.Attribute", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_ResourceLocation", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "resourceLocation", + "Type": "System.String" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.Extensions.Localization.ResourceManagerStringLocalizer", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.Extensions.Localization.IStringLocalizer" + ], + "Members": [ + { + "Kind": "Method", + "Name": "get_Item", + "Parameters": [ + { + "Name": "name", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.Extensions.Localization.LocalizedString", + "Virtual": true, + "ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Item", + "Parameters": [ + { + "Name": "name", + "Type": "System.String" + }, + { + "Name": "arguments", + "Type": "System.Object[]", + "IsParams": true + } + ], + "ReturnType": "Microsoft.Extensions.Localization.LocalizedString", + "Virtual": true, + "ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "WithCulture", + "Parameters": [ + { + "Name": "culture", + "Type": "System.Globalization.CultureInfo" + } + ], + "ReturnType": "Microsoft.Extensions.Localization.IStringLocalizer", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetAllStrings", + "Parameters": [ + { + "Name": "includeParentCultures", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Collections.Generic.IEnumerable", + "Virtual": true, + "ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetAllStrings", + "Parameters": [ + { + "Name": "includeParentCultures", + "Type": "System.Boolean" + }, + { + "Name": "culture", + "Type": "System.Globalization.CultureInfo" + } + ], + "ReturnType": "System.Collections.Generic.IEnumerable", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetStringSafely", + "Parameters": [ + { + "Name": "name", + "Type": "System.String" + }, + { + "Name": "culture", + "Type": "System.Globalization.CultureInfo" + } + ], + "ReturnType": "System.String", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "resourceManager", + "Type": "System.Resources.ResourceManager" + }, + { + "Name": "resourceAssembly", + "Type": "System.Reflection.Assembly" + }, + { + "Name": "baseName", + "Type": "System.String" + }, + { + "Name": "resourceNamesCache", + "Type": "Microsoft.Extensions.Localization.IResourceNamesCache" + }, + { + "Name": "logger", + "Type": "Microsoft.Extensions.Logging.ILogger" + } + ], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "resourceManager", + "Type": "System.Resources.ResourceManager" + }, + { + "Name": "resourceAssemblyWrapper", + "Type": "Microsoft.Extensions.Localization.Internal.AssemblyWrapper" + }, + { + "Name": "baseName", + "Type": "System.String" + }, + { + "Name": "resourceNamesCache", + "Type": "Microsoft.Extensions.Localization.IResourceNamesCache" + }, + { + "Name": "logger", + "Type": "Microsoft.Extensions.Logging.ILogger" + } + ], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "resourceManager", + "Type": "System.Resources.ResourceManager" + }, + { + "Name": "resourceStringProvider", + "Type": "Microsoft.Extensions.Localization.Internal.IResourceStringProvider" + }, + { + "Name": "baseName", + "Type": "System.String" + }, + { + "Name": "resourceNamesCache", + "Type": "Microsoft.Extensions.Localization.IResourceNamesCache" + }, + { + "Name": "logger", + "Type": "Microsoft.Extensions.Logging.ILogger" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.Extensions.Localization.ResourceManagerStringLocalizerFactory", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.Extensions.Localization.IStringLocalizerFactory" + ], + "Members": [ + { + "Kind": "Method", + "Name": "GetResourcePrefix", + "Parameters": [ + { + "Name": "typeInfo", + "Type": "System.Reflection.TypeInfo" + } + ], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetResourcePrefix", + "Parameters": [ + { + "Name": "typeInfo", + "Type": "System.Reflection.TypeInfo" + }, + { + "Name": "baseNamespace", + "Type": "System.String" + }, + { + "Name": "resourcesRelativePath", + "Type": "System.String" + } + ], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetResourcePrefix", + "Parameters": [ + { + "Name": "baseResourceName", + "Type": "System.String" + }, + { + "Name": "baseNamespace", + "Type": "System.String" + } + ], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Create", + "Parameters": [ + { + "Name": "resourceSource", + "Type": "System.Type" + } + ], + "ReturnType": "Microsoft.Extensions.Localization.IStringLocalizer", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizerFactory", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Create", + "Parameters": [ + { + "Name": "baseName", + "Type": "System.String" + }, + { + "Name": "location", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.Extensions.Localization.IStringLocalizer", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizerFactory", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CreateResourceManagerStringLocalizer", + "Parameters": [ + { + "Name": "assembly", + "Type": "System.Reflection.Assembly" + }, + { + "Name": "baseName", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.Extensions.Localization.ResourceManagerStringLocalizer", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetResourcePrefix", + "Parameters": [ + { + "Name": "location", + "Type": "System.String" + }, + { + "Name": "baseName", + "Type": "System.String" + }, + { + "Name": "resourceLocation", + "Type": "System.String" + } + ], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetResourceLocationAttribute", + "Parameters": [ + { + "Name": "assembly", + "Type": "System.Reflection.Assembly" + } + ], + "ReturnType": "Microsoft.Extensions.Localization.ResourceLocationAttribute", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetRootNamespaceAttribute", + "Parameters": [ + { + "Name": "assembly", + "Type": "System.Reflection.Assembly" + } + ], + "ReturnType": "Microsoft.Extensions.Localization.RootNamespaceAttribute", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "localizationOptions", + "Type": "Microsoft.Extensions.Options.IOptions" + }, + { + "Name": "loggerFactory", + "Type": "Microsoft.Extensions.Logging.ILoggerFactory" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.Extensions.Localization.ResourceManagerWithCultureStringLocalizer", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.Extensions.Localization.ResourceManagerStringLocalizer", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_Item", + "Parameters": [ + { + "Name": "name", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.Extensions.Localization.LocalizedString", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Item", + "Parameters": [ + { + "Name": "name", + "Type": "System.String" + }, + { + "Name": "arguments", + "Type": "System.Object[]", + "IsParams": true + } + ], + "ReturnType": "Microsoft.Extensions.Localization.LocalizedString", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetAllStrings", + "Parameters": [ + { + "Name": "includeParentCultures", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Collections.Generic.IEnumerable", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "resourceManager", + "Type": "System.Resources.ResourceManager" + }, + { + "Name": "resourceAssembly", + "Type": "System.Reflection.Assembly" + }, + { + "Name": "baseName", + "Type": "System.String" + }, + { + "Name": "resourceNamesCache", + "Type": "Microsoft.Extensions.Localization.IResourceNamesCache" + }, + { + "Name": "culture", + "Type": "System.Globalization.CultureInfo" + }, + { + "Name": "logger", + "Type": "Microsoft.Extensions.Logging.ILogger" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.Extensions.Localization.ResourceNamesCache", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.Extensions.Localization.IResourceNamesCache" + ], + "Members": [ + { + "Kind": "Method", + "Name": "GetOrAdd", + "Parameters": [ + { + "Name": "name", + "Type": "System.String" + }, + { + "Name": "valueFactory", + "Type": "System.Func>" + } + ], + "ReturnType": "System.Collections.Generic.IList", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.Extensions.Localization.IResourceNamesCache", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.Extensions.Localization.RootNamespaceAttribute", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "System.Attribute", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_RootNamespace", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "rootNamespace", + "Type": "System.String" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + } + ] +} \ No newline at end of file diff --git a/src/Localization/Localization/test/LocalizationServiceCollectionExtensionsTest.cs b/src/Localization/Localization/test/LocalizationServiceCollectionExtensionsTest.cs new file mode 100644 index 000000000000..d78581655cdd --- /dev/null +++ b/src/Localization/Localization/test/LocalizationServiceCollectionExtensionsTest.cs @@ -0,0 +1,68 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Linq; +using Microsoft.Extensions.Localization; +using Microsoft.Extensions.Options; +using Xunit; + +namespace Microsoft.Extensions.DependencyInjection +{ + public class LocalizationServiceCollectionExtensionsTest + { + [Fact] + public void AddLocalization_AddsNeededServices() + { + // Arrange + var collection = new ServiceCollection(); + + // Act + LocalizationServiceCollectionExtensions.AddLocalizationServices(collection); + + // Assert + AssertContainsSingle(collection, typeof(IStringLocalizerFactory), typeof(ResourceManagerStringLocalizerFactory)); + AssertContainsSingle(collection, typeof(IStringLocalizer<>), typeof(StringLocalizer<>)); + } + + [Fact] + public void AddLocalizationWithLocalizationOptions_AddsNeededServices() + { + // Arrange + var collection = new ServiceCollection(); + + // Act + LocalizationServiceCollectionExtensions.AddLocalizationServices( + collection, + options => options.ResourcesPath = "Resources"); + + AssertContainsSingle(collection, typeof(IStringLocalizerFactory), typeof(ResourceManagerStringLocalizerFactory)); + AssertContainsSingle(collection, typeof(IStringLocalizer<>), typeof(StringLocalizer<>)); + } + + private void AssertContainsSingle( + IServiceCollection services, + Type serviceType, + Type implementationType) + { + var matches = services + .Where(sd => + sd.ServiceType == serviceType && + sd.ImplementationType == implementationType) + .ToArray(); + + if (matches.Length == 0) + { + Assert.True( + false, + $"Could not find an instance of {implementationType} registered as {serviceType}"); + } + else if (matches.Length > 1) + { + Assert.True( + false, + $"Found multiple instances of {implementationType} registered as {serviceType}"); + } + } + } +} diff --git a/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests.csproj b/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests.csproj new file mode 100644 index 000000000000..4628ff7c0fcf --- /dev/null +++ b/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests.csproj @@ -0,0 +1,14 @@ + + + + $(StandardTestTfms) + + + + + + + + + + diff --git a/src/Localization/Localization/test/ResourceManagerStringLocalizerFactoryTest.cs b/src/Localization/Localization/test/ResourceManagerStringLocalizerFactoryTest.cs new file mode 100644 index 000000000000..7a18c0e4bdab --- /dev/null +++ b/src/Localization/Localization/test/ResourceManagerStringLocalizerFactoryTest.cs @@ -0,0 +1,296 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; +using System.Reflection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; +using Moq; +using Xunit; + +// This namespace intentionally matches the default assembly namespace. +namespace Microsoft.Extensions.Localization.Tests +{ + public class TestResourceManagerStringLocalizerFactory : ResourceManagerStringLocalizerFactory + { + private ResourceLocationAttribute _resourceLocationAttribute; + + private RootNamespaceAttribute _rootNamespaceAttribute; + + public Assembly Assembly { get; private set; } + public string BaseName { get; private set; } + + public TestResourceManagerStringLocalizerFactory( + IOptions localizationOptions, + ResourceLocationAttribute resourceLocationAttribute, + RootNamespaceAttribute rootNamespaceAttribute, + ILoggerFactory loggerFactory) + : base(localizationOptions, loggerFactory) + { + _resourceLocationAttribute = resourceLocationAttribute; + _rootNamespaceAttribute = rootNamespaceAttribute; + } + + protected override ResourceLocationAttribute GetResourceLocationAttribute(Assembly assembly) + { + return _resourceLocationAttribute; + } + + protected override RootNamespaceAttribute GetRootNamespaceAttribute(Assembly assembly) + { + return _rootNamespaceAttribute; + } + + protected override ResourceManagerStringLocalizer CreateResourceManagerStringLocalizer(Assembly assembly, string baseName) + { + BaseName = baseName; + Assembly = assembly; + + return base.CreateResourceManagerStringLocalizer(assembly, baseName); + } + } + + public class ResourceManagerStringLocalizerFactoryTest + { + [Fact] + public void Create_OverloadsProduceSameResult() + { + // Arrange + var locOptions = new LocalizationOptions(); + var options = new Mock>(); + options.Setup(o => o.Value).Returns(locOptions); + + var resourceLocationAttribute = new ResourceLocationAttribute(Path.Combine("My", "Resources")); + var loggerFactory = NullLoggerFactory.Instance; + var typeFactory = new TestResourceManagerStringLocalizerFactory( + options.Object, + resourceLocationAttribute, + rootNamespaceAttribute: null, + loggerFactory: loggerFactory); + var stringFactory = new TestResourceManagerStringLocalizerFactory( + options.Object, + resourceLocationAttribute, + rootNamespaceAttribute: null, + loggerFactory: loggerFactory); + var type = typeof(ResourceManagerStringLocalizerFactoryTest); + var assemblyName = new AssemblyName(type.GetTypeInfo().Assembly.FullName); + + // Act + typeFactory.Create(type); + stringFactory.Create(type.Name, assemblyName.Name); + + // Assert + Assert.Equal(typeFactory.BaseName, stringFactory.BaseName); + Assert.Equal(typeFactory.Assembly.FullName, stringFactory.Assembly.FullName); + } + + [Fact] + public void Create_FromType_ReturnsCachedResultForSameType() + { + // Arrange + var locOptions = new LocalizationOptions(); + var options = new Mock>(); + options.Setup(o => o.Value).Returns(locOptions); + var loggerFactory = NullLoggerFactory.Instance; + var factory = new ResourceManagerStringLocalizerFactory(localizationOptions: options.Object, loggerFactory: loggerFactory); + + // Act + var result1 = factory.Create(typeof(ResourceManagerStringLocalizerFactoryTest)); + var result2 = factory.Create(typeof(ResourceManagerStringLocalizerFactoryTest)); + + // Assert + Assert.Same(result1, result2); + } + + [Fact] + public void Create_FromType_ReturnsNewResultForDifferentType() + { + // Arrange + var locOptions = new LocalizationOptions(); + var options = new Mock>(); + options.Setup(o => o.Value).Returns(locOptions); + var loggerFactory = NullLoggerFactory.Instance; + var factory = new ResourceManagerStringLocalizerFactory(localizationOptions: options.Object, loggerFactory: loggerFactory); + + // Act + var result1 = factory.Create(typeof(ResourceManagerStringLocalizerFactoryTest)); + var result2 = factory.Create(typeof(LocalizationOptions)); + + // Assert + Assert.NotSame(result1, result2); + } + + [Fact] + public void Create_ResourceLocationAttribute_RootNamespaceIgnoredWhenNoLocation() + { + // Arrange + var locOptions = new LocalizationOptions(); + var options = new Mock>(); + options.Setup(o => o.Value).Returns(locOptions); + var loggerFactory = NullLoggerFactory.Instance; + + var resourcePath = Path.Combine("My", "Resources"); + var rootNamespace = "MyNamespace"; + var rootNamespaceAttribute = new RootNamespaceAttribute(rootNamespace); + + var typeFactory = new TestResourceManagerStringLocalizerFactory( + options.Object, + resourceLocationAttribute: null, + rootNamespaceAttribute: rootNamespaceAttribute, + loggerFactory: loggerFactory); + + var type = typeof(ResourceManagerStringLocalizerFactoryTest); + // Act + typeFactory.Create(type); + + // Assert + Assert.Equal($"Microsoft.Extensions.Localization.Tests.ResourceManagerStringLocalizerFactoryTest", typeFactory.BaseName); + } + + [Fact] + public void Create_ResourceLocationAttribute_UsesRootNamespace() + { + // Arrange + var locOptions = new LocalizationOptions(); + var options = new Mock>(); + options.Setup(o => o.Value).Returns(locOptions); + var loggerFactory = NullLoggerFactory.Instance; + + var resourcePath = Path.Combine("My", "Resources"); + var rootNamespace = "MyNamespace"; + var resourceLocationAttribute = new ResourceLocationAttribute(resourcePath); + var rootNamespaceAttribute = new RootNamespaceAttribute(rootNamespace); + + var typeFactory = new TestResourceManagerStringLocalizerFactory( + options.Object, + resourceLocationAttribute, + rootNamespaceAttribute, + loggerFactory); + + var type = typeof(ResourceManagerStringLocalizerFactoryTest); + // Act + typeFactory.Create(type); + + // Assert + Assert.Equal($"MyNamespace.My.Resources.ResourceManagerStringLocalizerFactoryTest", typeFactory.BaseName); + } + + [Fact] + public void Create_FromType_ResourcesPathDirectorySeperatorToDot() + { + // Arrange + var locOptions = new LocalizationOptions(); + locOptions.ResourcesPath = Path.Combine("My", "Resources"); + var options = new Mock>(); + options.Setup(o => o.Value).Returns(locOptions); + var loggerFactory = NullLoggerFactory.Instance; + var factory = new TestResourceManagerStringLocalizerFactory( + options.Object, + resourceLocationAttribute: null, + rootNamespaceAttribute: null, + loggerFactory: loggerFactory); + + // Act + factory.Create(typeof(ResourceManagerStringLocalizerFactoryTest)); + + // Assert + Assert.Equal("Microsoft.Extensions.Localization.Tests.My.Resources." + nameof(ResourceManagerStringLocalizerFactoryTest), factory.BaseName); + } + + [Fact] + public void Create_FromNameLocation_ReturnsCachedResultForSameNameLocation() + { + // Arrange + var locOptions = new LocalizationOptions(); + var options = new Mock>(); + options.Setup(o => o.Value).Returns(locOptions); + var loggerFactory = NullLoggerFactory.Instance; + var factory = new ResourceManagerStringLocalizerFactory(localizationOptions: options.Object, loggerFactory: loggerFactory); + var location = typeof(ResourceManagerStringLocalizer).GetTypeInfo().Assembly.FullName; + + // Act + var result1 = factory.Create("baseName", location); + var result2 = factory.Create("baseName", location); + + // Assert + Assert.Same(result1, result2); + } + + [Fact] + public void Create_FromNameLocation_ReturnsNewResultForDifferentName() + { + // Arrange + var locOptions = new LocalizationOptions(); + var options = new Mock>(); + options.Setup(o => o.Value).Returns(locOptions); + var loggerFactory = NullLoggerFactory.Instance; + var factory = new ResourceManagerStringLocalizerFactory(localizationOptions: options.Object, loggerFactory: loggerFactory); + var location = typeof(ResourceManagerStringLocalizer).GetTypeInfo().Assembly.FullName; + + // Act + var result1 = factory.Create("baseName1", location); + var result2 = factory.Create("baseName2", location); + + // Assert + Assert.NotSame(result1, result2); + } + + [Fact] + public void Create_FromNameLocation_ReturnsNewResultForDifferentLocation() + { + // Arrange + var locOptions = new LocalizationOptions(); + var options = new Mock>(); + options.Setup(o => o.Value).Returns(locOptions); + var loggerFactory = NullLoggerFactory.Instance; + var factory = new ResourceManagerStringLocalizerFactory(localizationOptions: options.Object, loggerFactory: loggerFactory); + var location1 = new AssemblyName(typeof(ResourceManagerStringLocalizer).GetTypeInfo().Assembly.FullName).Name; + var location2 = new AssemblyName(typeof(ResourceManagerStringLocalizerFactoryTest).GetTypeInfo().Assembly.FullName).Name; + + // Act + var result1 = factory.Create("baseName", location1); + var result2 = factory.Create("baseName", location2); + + // Assert + Assert.NotSame(result1, result2); + } + + [Fact] + public void Create_FromNameLocation_ResourcesPathDirectorySeparatorToDot() + { + // Arrange + var locOptions = new LocalizationOptions(); + locOptions.ResourcesPath = Path.Combine("My", "Resources"); + var options = new Mock>(); + options.Setup(o => o.Value).Returns(locOptions); + var loggerFactory = NullLoggerFactory.Instance; + var factory = new TestResourceManagerStringLocalizerFactory( + options.Object, + resourceLocationAttribute: null, + rootNamespaceAttribute: null, + loggerFactory: loggerFactory); + + // Act + var result1 = factory.Create("baseName", location: "Microsoft.Extensions.Localization.Tests"); + + // Assert + Assert.Equal("Microsoft.Extensions.Localization.Tests.My.Resources.baseName", factory.BaseName); + } + + [Fact] + public void Create_FromNameLocation_NullLocationThrows() + { + // Arrange + var locOptions = new LocalizationOptions(); + var options = new Mock>(); + options.Setup(o => o.Value).Returns(locOptions); + var loggerFactory = NullLoggerFactory.Instance; + var factory = new ResourceManagerStringLocalizerFactory(localizationOptions: options.Object, loggerFactory: loggerFactory); + + // Act & Assert + Assert.Throws(() => factory.Create("baseName", location: null)); + } + } +} diff --git a/src/Localization/Localization/test/ResourceManagerStringLocalizerTest.cs b/src/Localization/Localization/test/ResourceManagerStringLocalizerTest.cs new file mode 100644 index 000000000000..ff7bfa9933dd --- /dev/null +++ b/src/Localization/Localization/test/ResourceManagerStringLocalizerTest.cs @@ -0,0 +1,299 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Resources; +using Microsoft.AspNetCore.Testing; +using Microsoft.Extensions.Localization.Internal; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Testing; +using Xunit; + +namespace Microsoft.Extensions.Localization +{ + public class ResourceManagerStringLocalizerTest + { + [Fact] + public void EnumeratorCachesCultureWalkForSameAssembly() + { + // Arrange + var resourceNamesCache = new ResourceNamesCache(); + var baseName = "test"; + var resourceAssembly = new TestAssemblyWrapper(); + var resourceManager = new TestResourceManager(baseName, resourceAssembly); + var resourceStreamManager = new TestResourceStringProvider( + resourceNamesCache, + resourceManager, + resourceAssembly.Assembly, + baseName); + var logger = Logger; + var localizer1 = new ResourceManagerStringLocalizer(resourceManager, + resourceStreamManager, + baseName, + resourceNamesCache, + logger); + var localizer2 = new ResourceManagerStringLocalizer(resourceManager, + resourceStreamManager, + baseName, + resourceNamesCache, + logger); + + // Act + for (var i = 0; i < 5; i++) + { + localizer1.GetAllStrings().ToList(); + localizer2.GetAllStrings().ToList(); + } + + // Assert + var expectedCallCount = GetCultureInfoDepth(CultureInfo.CurrentUICulture); + Assert.Equal(expectedCallCount, resourceAssembly.ManifestResourceStreamCallCount); + } + + [Fact] + public void EnumeratorCacheIsScopedByAssembly() + { + // Arrange + var resourceNamesCache = new ResourceNamesCache(); + var baseName = "test"; + var resourceAssembly1 = new TestAssemblyWrapper(typeof(ResourceManagerStringLocalizerTest)); + var resourceAssembly2 = new TestAssemblyWrapper(typeof(ResourceManagerStringLocalizer)); + var resourceManager1 = new TestResourceManager(baseName, resourceAssembly1); + var resourceManager2 = new TestResourceManager(baseName, resourceAssembly2); + var resourceStreamManager1 = new TestResourceStringProvider(resourceNamesCache, resourceManager1, resourceAssembly1.Assembly, baseName); + var resourceStreamManager2 = new TestResourceStringProvider(resourceNamesCache, resourceManager2, resourceAssembly2.Assembly, baseName); + var logger = Logger; + var localizer1 = new ResourceManagerStringLocalizer( + resourceManager1, + resourceStreamManager1, + baseName, + resourceNamesCache, + logger); + var localizer2 = new ResourceManagerStringLocalizer( + resourceManager2, + resourceStreamManager2, + baseName, + resourceNamesCache, + logger); + + // Act + localizer1.GetAllStrings().ToList(); + localizer2.GetAllStrings().ToList(); + + // Assert + var expectedCallCount = GetCultureInfoDepth(CultureInfo.CurrentUICulture); + Assert.Equal(expectedCallCount, resourceAssembly1.ManifestResourceStreamCallCount); + Assert.Equal(expectedCallCount, resourceAssembly2.ManifestResourceStreamCallCount); + } + + [Fact] + public void GetString_PopulatesSearchedLocationOnLocalizedString() + { + // Arrange + var baseName = "Resources.TestResource"; + var resourceNamesCache = new ResourceNamesCache(); + var resourceAssembly = new TestAssemblyWrapper(); + var resourceManager = new TestResourceManager(baseName, resourceAssembly); + var resourceStreamManager = new TestResourceStringProvider(resourceNamesCache, resourceManager, resourceAssembly.Assembly, baseName); + var logger = Logger; + var localizer = new ResourceManagerStringLocalizer( + resourceManager, + resourceStreamManager, + baseName, + resourceNamesCache, + logger); + + // Act + var value = localizer["name"]; + + // Assert + Assert.Equal("Resources.TestResource", value.SearchedLocation); + } + + [Fact] + [ReplaceCulture("en-US", "en-US")] + public void GetString_LogsLocationSearched() + { + // Arrange + var baseName = "Resources.TestResource"; + var resourceNamesCache = new ResourceNamesCache(); + var resourceAssembly = new TestAssemblyWrapper(); + var resourceManager = new TestResourceManager(baseName, resourceAssembly); + var resourceStreamManager = new TestResourceStringProvider(resourceNamesCache, resourceManager, resourceAssembly.Assembly, baseName); + var logger = Logger; + + var localizer = new ResourceManagerStringLocalizer( + resourceManager, + resourceStreamManager, + baseName, + resourceNamesCache, + logger); + + // Act + var value = localizer["a key!"]; + + // Assert + var write = Assert.Single(Sink.Writes); + Assert.Equal("ResourceManagerStringLocalizer searched for 'a key!' in 'Resources.TestResource' with culture 'en-US'.", write.State.ToString()); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void ResourceManagerStringLocalizer_GetAllStrings_ReturnsExpectedValue(bool includeParentCultures) + { + // Arrange + var baseName = "test"; + var resourceNamesCache = new ResourceNamesCache(); + var resourceAssembly = new TestAssemblyWrapper(); + var resourceManager = new TestResourceManager(baseName, resourceAssembly); + var resourceStreamManager = new TestResourceStringProvider(resourceNamesCache, resourceManager, resourceAssembly.Assembly, baseName); + var logger = Logger; + var localizer = new ResourceManagerStringLocalizer( + resourceManager, + resourceStreamManager, + baseName, + resourceNamesCache, + logger); + + // Act + // We have to access the result so it evaluates. + var strings = localizer.GetAllStrings(includeParentCultures).ToList(); + + // Assert + var value = Assert.Single(strings); + Assert.Equal("TestName", value.Value); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void ResourceManagerStringLocalizer_GetAllStrings_MissingResourceThrows(bool includeParentCultures) + { + // Arrange + var resourceNamesCache = new ResourceNamesCache(); + var baseName = "testington"; + var resourceAssembly = new TestAssemblyWrapper(); + resourceAssembly.HasResources = false; + var resourceManager = new TestResourceManager(baseName, resourceAssembly); + var logger = Logger; + + var localizer = new ResourceManagerWithCultureStringLocalizer( + resourceManager, + resourceAssembly.Assembly, + baseName, + resourceNamesCache, + CultureInfo.CurrentCulture, + logger); + + // Act & Assert + var exception = Assert.Throws(() => + { + // We have to access the result so it evaluates. + localizer.GetAllStrings(includeParentCultures).ToArray(); + }); + + var expectedTries = includeParentCultures ? 3 : 1; + var expected = includeParentCultures + ? "No manifests exist for the current culture." + : $"The manifest 'testington.{CultureInfo.CurrentCulture}.resources' was not found."; + Assert.Equal(expected, exception.Message); + Assert.Equal(expectedTries, resourceAssembly.ManifestResourceStreamCallCount); + } + + private static Stream MakeResourceStream() + { + var stream = new MemoryStream(); + var resourceWriter = new ResourceWriter(stream); + resourceWriter.AddResource("TestName", "value"); + resourceWriter.Generate(); + stream.Position = 0; + return stream; + } + + private static int GetCultureInfoDepth(CultureInfo culture) + { + var result = 0; + var currentCulture = culture; + + while (true) + { + result++; + + if (currentCulture == currentCulture.Parent) + { + break; + } + + currentCulture = currentCulture.Parent; + } + + return result; + } + + + private TestSink Sink { get; } = new TestSink(); + + private ILogger Logger => new TestLoggerFactory(Sink, enabled: true).CreateLogger(); + + public class TestResourceManager : ResourceManager + { + private AssemblyWrapper _assemblyWrapper; + + public TestResourceManager(string baseName, AssemblyWrapper assemblyWrapper) + : base(baseName, assemblyWrapper.Assembly) + { + _assemblyWrapper = assemblyWrapper; + } + + public override string GetString(string name, CultureInfo culture) => null; + + public override ResourceSet GetResourceSet(CultureInfo culture, bool createIfNotExists, bool tryParents) + { + var resourceStream = _assemblyWrapper.GetManifestResourceStream(BaseName); + + return resourceStream != null ? new ResourceSet(resourceStream) : null; + } + } + + public class TestResourceStringProvider : ResourceManagerStringProvider + { + public TestResourceStringProvider( + IResourceNamesCache resourceCache, + TestResourceManager resourceManager, + Assembly assembly, + string resourceBaseName) + : base(resourceCache, resourceManager, assembly, resourceBaseName) + { + } + } + + public class TestAssemblyWrapper : AssemblyWrapper + { + public TestAssemblyWrapper() + : this(typeof(TestAssemblyWrapper)) + { + } + + public TestAssemblyWrapper(Type type) + : base(type.GetTypeInfo().Assembly) + { + } + + public bool HasResources { get; set; } = true; + + public int ManifestResourceStreamCallCount { get; private set; } + + public override Stream GetManifestResourceStream(string name) + { + ManifestResourceStreamCallCount++; + + return HasResources ? MakeResourceStream() : null; + } + } + } +} From 090119859b15a49b54b8f637842eebf0ff1a42a0 Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Mon, 3 Dec 2018 16:22:29 -0800 Subject: [PATCH 020/183] Update targets and tasks to build Localization projects \n\nCommit migrated from https://github.com/dotnet/extensions/commit/22f5402e13ad2c5cdab081a5480077a69c5cc2e2 --- .../test/Microsoft.Extensions.Localization.Tests.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests.csproj b/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests.csproj index 4628ff7c0fcf..e7a47a1a6e7e 100644 --- a/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests.csproj +++ b/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests.csproj @@ -5,7 +5,6 @@ - From e7c4b678f99ac20ee46c9fef0fb930d0d2810e81 Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Tue, 4 Dec 2018 10:40:35 -0800 Subject: [PATCH 021/183] Reorganize source code in preparation to move into aspnet/Extensions Prior to reorganization, this source code was found in https://github.com/aspnet/Localization/tree/dotnet/extensions@60a3d57a3e8b8b6d69232815a9596b5a495a7d0b \n\nCommit migrated from https://github.com/dotnet/extensions/commit/b885414e8a44e777a9dd1170416bb143c24ed524 --- .../test/Microsoft.Extensions.Localization.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests.csproj b/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests.csproj index 4628ff7c0fcf..30f071feaeed 100644 --- a/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests.csproj +++ b/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests.csproj @@ -1,7 +1,7 @@  - $(StandardTestTfms) + netcoreapp3.0;net461 From 57cd32129b52caeb329478c67c9c973f5d94efa1 Mon Sep 17 00:00:00 2001 From: Arthur Vickers Date: Fri, 30 Nov 2018 14:43:22 -0800 Subject: [PATCH 022/183] Added tests that can be implemented for any provider to ensure consistency Partly to allow me to get a better understanding of the code, partly so we can have a better idea that all the providers work in a consistent manner. Part of https://github.com/aspnet/Configuration/issues/559 I will file issues for things found. \n\nCommit migrated from https://github.com/dotnet/extensions/commit/1878d16ecd5165849cd6a78ce708e499b71477ca --- .../ConfigurationProviderCommandLineTest.cs | 43 ++ .../test/ConfigurationProviderTestBase.cs | 552 ++++++++++++++++++ .../test/KeyPerFileTests.cs | 4 +- ...ions.Configuration.KeyPerFile.Tests.csproj | 4 + 4 files changed, 602 insertions(+), 1 deletion(-) create mode 100644 src/Configuration.KeyPerFile/test/ConfigurationProviderCommandLineTest.cs create mode 100644 src/Configuration.KeyPerFile/test/ConfigurationProviderTestBase.cs diff --git a/src/Configuration.KeyPerFile/test/ConfigurationProviderCommandLineTest.cs b/src/Configuration.KeyPerFile/test/ConfigurationProviderCommandLineTest.cs new file mode 100644 index 000000000000..066aecf33756 --- /dev/null +++ b/src/Configuration.KeyPerFile/test/ConfigurationProviderCommandLineTest.cs @@ -0,0 +1,43 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Extensions.Configuration.Test; +using Microsoft.Extensions.FileProviders; + +namespace Microsoft.Extensions.Configuration.KeyPerFile.Test +{ + public class ConfigurationProviderCommandLineTest : ConfigurationProviderTestBase + { + protected override (IConfigurationProvider Provider, Action Initializer) LoadThroughProvider( + TestSection testConfig) + { + var testFiles = new List(); + SectionToTestFiles(testFiles, "", testConfig); + + var provider = new KeyPerFileConfigurationProvider( + new KeyPerFileConfigurationSource + { + Optional = true, + FileProvider = new TestFileProvider(testFiles.ToArray()) + }); + + return (provider, () => { }); + } + + private void SectionToTestFiles(List testFiles, string sectionName, TestSection section) + { + foreach (var tuple in section.Values.SelectMany(e => e.Value.Expand(e.Key))) + { + testFiles.Add(new TestFile(sectionName + tuple.Key, tuple.Value)); + } + + foreach (var tuple in section.Sections) + { + SectionToTestFiles(testFiles, sectionName + tuple.Key + "__", tuple.Section); + } + } + } +} diff --git a/src/Configuration.KeyPerFile/test/ConfigurationProviderTestBase.cs b/src/Configuration.KeyPerFile/test/ConfigurationProviderTestBase.cs new file mode 100644 index 000000000000..c18bea8d2851 --- /dev/null +++ b/src/Configuration.KeyPerFile/test/ConfigurationProviderTestBase.cs @@ -0,0 +1,552 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Extensions.Configuration.Memory; +using Xunit; + +namespace Microsoft.Extensions.Configuration.Test +{ + public abstract class ConfigurationProviderTestBase + { + [Fact] + public virtual void Load_from_single_provider() + { + AssertConfig(BuildConfigRoot(LoadThroughProvider(TestSection.TestConfig))); + } + + [Fact] + public virtual void Combine_after_other_provider() + { + AssertConfig( + BuildConfigRoot( + LoadUsingMemoryProvider(TestSection.MissingSection2Config), + LoadThroughProvider(TestSection.MissingSection4Config))); + } + + [Fact] + public virtual void Combine_before_other_provider() + { + AssertConfig( + BuildConfigRoot( + LoadThroughProvider(TestSection.MissingSection2Config), + LoadUsingMemoryProvider(TestSection.MissingSection4Config))); + } + + [Fact] + public virtual void Second_provider_overrides_values_from_first() + { + AssertConfig( + BuildConfigRoot( + LoadUsingMemoryProvider(TestSection.NoValuesTestConfig), + LoadThroughProvider(TestSection.TestConfig))); + } + + [Fact] + public virtual void Combining_from_multiple_providers_is_case_insensitive() + { + AssertConfig( + BuildConfigRoot( + LoadUsingMemoryProvider(TestSection.DifferentCasedTestConfig), + LoadThroughProvider(TestSection.TestConfig))); + } + + [Fact] + public virtual void Load_from_single_provider_with_duplicates_throws() + { + AssertFormatOrArgumentException( + () => BuildConfigRoot(LoadThroughProvider(TestSection.DuplicatesTestConfig))); + } + + [Fact] + public virtual void Load_from_single_provider_with_differing_case_duplicates_throws() + { + AssertFormatOrArgumentException( + () => BuildConfigRoot(LoadThroughProvider(TestSection.DuplicatesDifferentCaseTestConfig))); + } + + private void AssertFormatOrArgumentException(Action test) + { + Exception caught = null; + try + { + test(); + } + catch (Exception e) + { + caught = e; + } + + Assert.True(caught is ArgumentException + || caught is FormatException); + } + + [Fact] + public virtual void Bind_to_object() + { + var configuration = BuildConfigRoot(LoadThroughProvider(TestSection.TestConfig)); + + var options = configuration.Get(); + + Assert.Equal("Value1", options.Key1); + Assert.Equal("Value12", options.Section1.Key2); + Assert.Equal("Value123", options.Section1.Section2.Key3); + Assert.Equal("Value344", options.Section3.Section4.Key4); + Assert.Equal(new[] { "ArrayValue0", "ArrayValue1", "ArrayValue2" }, options.Section1.Section2.Key3a); + } + + public class AsOptions + { + public string Key1 { get; set; } + + public Section1AsOptions Section1 { get; set; } + public Section3AsOptions Section3 { get; set; } + } + + public class Section1AsOptions + { + public string Key2 { get; set; } + + public Section2AsOptions Section2 { get; set; } + } + + public class Section2AsOptions + { + public string Key3 { get; set; } + public string[] Key3a { get; set; } + } + + public class Section3AsOptions + { + public Section4AsOptions Section4 { get; set; } + } + + public class Section4AsOptions + { + public string Key4 { get; set; } + } + + protected virtual void AssertConfig(IConfigurationRoot config) + { + Assert.Equal("Value1", config["Key1"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal("Value12", config["Section1:Key2"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal("Value123", config["Section1:Section2:Key3"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal("ArrayValue0", config["Section1:Section2:Key3a:0"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal("ArrayValue1", config["Section1:Section2:Key3a:1"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal("ArrayValue2", config["Section1:Section2:Key3a:2"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal("Value344", config["Section3:Section4:Key4"], StringComparer.InvariantCultureIgnoreCase); + + var section1 = config.GetSection("Section1"); + Assert.Equal("Value12", section1["Key2"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal("Value123", section1["Section2:Key3"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal("ArrayValue0", section1["Section2:Key3a:0"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal("ArrayValue1", section1["Section2:Key3a:1"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal("ArrayValue2", section1["Section2:Key3a:2"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal("Section1", section1.Path, StringComparer.InvariantCultureIgnoreCase); + Assert.Null(section1.Value); + + var section2 = config.GetSection("Section1:Section2"); + Assert.Equal("Value123", section2["Key3"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal("ArrayValue0", section2["Key3a:0"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal("ArrayValue1", section2["Key3a:1"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal("ArrayValue2", section2["Key3a:2"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal("Section1:Section2", section2.Path, StringComparer.InvariantCultureIgnoreCase); + Assert.Null(section2.Value); + + section2 = section1.GetSection("Section2"); + Assert.Equal("Value123", section2["Key3"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal("ArrayValue0", section2["Key3a:0"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal("ArrayValue1", section2["Key3a:1"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal("ArrayValue2", section2["Key3a:2"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal("Section1:Section2", section2.Path, StringComparer.InvariantCultureIgnoreCase); + Assert.Null(section2.Value); + + var section4 = config.GetSection("Section3:Section4"); + Assert.Equal("Value344", section4["Key4"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal("Section3:Section4", section4.Path, StringComparer.InvariantCultureIgnoreCase); + Assert.Null(section4.Value); + + section4 = config.GetSection("Section3").GetSection("Section4"); + Assert.Equal("Value344", section4["Key4"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal("Section3:Section4", section4.Path, StringComparer.InvariantCultureIgnoreCase); + Assert.Null(section4.Value); + + var sections = config.GetChildren().ToList(); + + Assert.Equal(3, sections.Count); + + Assert.Equal("Key1", sections[0].Key, StringComparer.InvariantCultureIgnoreCase); + Assert.Equal("Key1", sections[0].Path, StringComparer.InvariantCultureIgnoreCase); + Assert.Equal("Value1", sections[0].Value, StringComparer.InvariantCultureIgnoreCase); + + Assert.Equal("Section1", sections[1].Key, StringComparer.InvariantCultureIgnoreCase); + Assert.Equal("Section1", sections[1].Path, StringComparer.InvariantCultureIgnoreCase); + Assert.Null(sections[1].Value); + + Assert.Equal("Section3", sections[2].Key, StringComparer.InvariantCultureIgnoreCase); + Assert.Equal("Section3", sections[2].Path, StringComparer.InvariantCultureIgnoreCase); + Assert.Null(sections[2].Value); + + sections = section1.GetChildren().ToList(); + + Assert.Equal(2, sections.Count); + + Assert.Equal("Key2", sections[0].Key, StringComparer.InvariantCultureIgnoreCase); + Assert.Equal("Section1:Key2", sections[0].Path, StringComparer.InvariantCultureIgnoreCase); + Assert.Equal("Value12", sections[0].Value, StringComparer.InvariantCultureIgnoreCase); + + Assert.Equal("Section2", sections[1].Key, StringComparer.InvariantCultureIgnoreCase); + Assert.Equal("Section1:Section2", sections[1].Path, StringComparer.InvariantCultureIgnoreCase); + Assert.Null(sections[1].Value); + } + + protected abstract (IConfigurationProvider Provider, Action Initializer) LoadThroughProvider(TestSection testConfig); + + protected virtual IConfigurationRoot BuildConfigRoot( + params (IConfigurationProvider Provider, Action Initializer)[] providers) + { + var root = new ConfigurationRoot(providers.Select(e => e.Provider).ToList()); + + foreach (var initializer in providers.Select(e => e.Initializer)) + { + initializer(); + } + + return root; + } + + protected static (IConfigurationProvider Provider, Action Initializer) LoadUsingMemoryProvider(TestSection testConfig) + { + var values = new List>(); + SectionToValues(testConfig, "", values); + + return (new MemoryConfigurationProvider( + new MemoryConfigurationSource + { + InitialData = values + }), + () => { }); + } + + protected static void SectionToValues( + TestSection section, + string sectionName, + IList> values) + { + foreach (var tuple in section.Values.SelectMany(e => e.Value.Expand(e.Key))) + { + values.Add(new KeyValuePair(sectionName + tuple.Key, tuple.Value)); + } + + foreach (var tuple in section.Sections) + { + SectionToValues( + tuple.Section, + sectionName + tuple.Key + ":", + values); + } + } + + protected class TestKeyValue + { + public object Value { get; } + + public TestKeyValue(string value) + { + Value = value; + } + + public TestKeyValue(string[] values) + { + Value = values; + } + + public static implicit operator TestKeyValue(string value) => new TestKeyValue(value); + public static implicit operator TestKeyValue(string[] values) => new TestKeyValue(values); + + public string[] AsArray => Value as string[]; + + public string AsString => Value as string; + + public IEnumerable<(string Key, string Value)> Expand(string key) + { + if (AsString != null) + { + yield return (key, AsString); + } + else + { + for (var i = 0; i < AsArray.Length; i++) + { + yield return ($"{key}:{i}", AsArray[i]); + } + } + } + } + + protected class TestSection + { + public IEnumerable<(string Key, TestKeyValue Value)> Values { get; set; } + = Enumerable.Empty<(string, TestKeyValue)>(); + + public IEnumerable<(string Key, TestSection Section)> Sections { get; set; } + = Enumerable.Empty<(string, TestSection)>(); + + public static TestSection TestConfig { get; } + = new TestSection + { + Values = new[] { ("Key1", (TestKeyValue)"Value1") }, + Sections = new[] + { + ("Section1", new TestSection + { + Values = new[] {("Key2", (TestKeyValue)"Value12")}, + Sections = new[] + { + ("Section2", new TestSection + { + Values = new[] + { + ("Key3", (TestKeyValue)"Value123"), + ("Key3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"}), + }, + }) + } + }), + ("Section3", new TestSection + { + Sections = new[] + { + ("Section4", new TestSection + { + Values = new[] {("Key4", (TestKeyValue)"Value344")}, + }) + } + }), + + } + }; + + public static TestSection NoValuesTestConfig { get; } + = new TestSection + { + Values = new[] { ("Key1", (TestKeyValue)"------") }, + Sections = new[] + { + ("Section1", new TestSection + { + Values = new[] {("Key2", (TestKeyValue)"-------")}, + Sections = new[] + { + ("Section2", new TestSection + { + Values = new[] + { + ("Key3", (TestKeyValue)"-----"), + ("Key3a", (TestKeyValue)new[] {"-----------", "-----------", "-----------"}), + }, + }) + } + }), + ("Section3", new TestSection + { + Sections = new[] + { + ("Section4", new TestSection + { + Values = new[] {("Key4", (TestKeyValue)"--------")}, + }) + } + }), + + } + }; + + public static TestSection MissingSection2Config { get; } + = new TestSection + { + Values = new[] { ("Key1", (TestKeyValue)"Value1") }, + Sections = new[] + { + ("Section1", new TestSection + { + Values = new[] {("Key2", (TestKeyValue)"Value12")}, + }), + ("Section3", new TestSection + { + Sections = new[] + { + ("Section4", new TestSection + { + Values = new[] {("Key4", (TestKeyValue)"Value344")}, + }) + } + }), + + } + }; + + + public static TestSection MissingSection4Config { get; } + = new TestSection + { + Values = new[] { ("Key1", (TestKeyValue)"Value1") }, + Sections = new[] + { + ("Section1", new TestSection + { + Values = new[] {("Key2", (TestKeyValue)"Value12")}, + Sections = new[] + { + ("Section2", new TestSection + { + Values = new[] + { + ("Key3", (TestKeyValue)"Value123"), + ("Key3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"}), + }, + }) + } + }), + ("Section3", new TestSection + { + }), + + } + }; + + public static TestSection DifferentCasedTestConfig { get; } + = new TestSection + { + Values = new[] { ("KeY1", (TestKeyValue)"Value1") }, + Sections = new[] + { + ("SectioN1", new TestSection + { + Values = new[] {("KeY2", (TestKeyValue)"Value12")}, + Sections = new[] + { + ("SectioN2", new TestSection + { + Values = new[] + { + ("KeY3", (TestKeyValue)"Value123"), + ("KeY3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"}), + }, + }) + } + }), + ("SectioN3", new TestSection + { + Sections = new[] + { + ("SectioN4", new TestSection + { + Values = new[] {("KeY4", (TestKeyValue)"Value344")}, + }) + } + }), + + } + }; + + public static TestSection DuplicatesTestConfig { get; } + = new TestSection + { + Values = new[] + { + ("Key1", (TestKeyValue)"Value1"), + ("Key1", (TestKeyValue)"Value1") + }, + Sections = new[] + { + ("Section1", new TestSection + { + Values = new[] {("Key2", (TestKeyValue)"Value12")}, + Sections = new[] + { + ("Section2", new TestSection + { + Values = new[] + { + ("Key3", (TestKeyValue)"Value123"), + ("Key3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"}), + }, + }), + ("Section2", new TestSection + { + Values = new[] + { + ("Key3", (TestKeyValue)"Value123"), + ("Key3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"}), + }, + }) + + } + }), + ("Section3", new TestSection + { + Sections = new[] + { + ("Section4", new TestSection + { + Values = new[] {("Key4", (TestKeyValue)"Value344")}, + }) + } + }), + + } + }; + + public static TestSection DuplicatesDifferentCaseTestConfig { get; } + = new TestSection + { + Values = new[] + { + ("Key1", (TestKeyValue)"Value1"), + ("KeY1", (TestKeyValue)"Value1") + }, + Sections = new[] + { + ("Section1", new TestSection + { + Values = new[] {("Key2", (TestKeyValue)"Value12")}, + Sections = new[] + { + ("Section2", new TestSection + { + Values = new[] + { + ("Key3", (TestKeyValue)"Value123"), + ("Key3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"}), + }, + }), + ("SectioN2", new TestSection + { + Values = new[] + { + ("KeY3", (TestKeyValue)"Value123"), + ("KeY3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"}), + }, + }) + + } + }), + ("Section3", new TestSection + { + Sections = new[] + { + ("Section4", new TestSection + { + Values = new[] {("Key4", (TestKeyValue)"Value344")}, + }) + } + }), + + } + }; + } + } +} diff --git a/src/Configuration.KeyPerFile/test/KeyPerFileTests.cs b/src/Configuration.KeyPerFile/test/KeyPerFileTests.cs index d409c0eab087..fac0e839e8b5 100644 --- a/src/Configuration.KeyPerFile/test/KeyPerFileTests.cs +++ b/src/Configuration.KeyPerFile/test/KeyPerFileTests.cs @@ -1,8 +1,10 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + using System; using System.Collections; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Text; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Primitives; diff --git a/src/Configuration.KeyPerFile/test/Microsoft.Extensions.Configuration.KeyPerFile.Tests.csproj b/src/Configuration.KeyPerFile/test/Microsoft.Extensions.Configuration.KeyPerFile.Tests.csproj index 634056a345bd..e78f4fd19032 100644 --- a/src/Configuration.KeyPerFile/test/Microsoft.Extensions.Configuration.KeyPerFile.Tests.csproj +++ b/src/Configuration.KeyPerFile/test/Microsoft.Extensions.Configuration.KeyPerFile.Tests.csproj @@ -4,6 +4,10 @@ $(StandardTestTfms) + + + + From 317b4bb4eb3284f9f7051a73bbb7f70997572195 Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Thu, 13 Dec 2018 09:49:07 -0800 Subject: [PATCH 023/183] Create README.md for the localization folder (dotnet/extensions#683) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/c841b4f9e0ad4b3724136dcfde35a8b1ad71b537 --- src/Localization/README.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 src/Localization/README.md diff --git a/src/Localization/README.md b/src/Localization/README.md new file mode 100644 index 000000000000..dc6894b6e09e --- /dev/null +++ b/src/Localization/README.md @@ -0,0 +1,6 @@ +Localization +============ + +These projects provide abstractions for localizing resources in .NET applications. + +The ASP.NET Core implementation of localization can be found in https://github.com/aspnet/AspNetCore/tree/master/src/Middleware/Localization. From a053c7e1a46352e0b97c1bf2f42b98145e901651 Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Thu, 13 Dec 2018 10:33:33 -0800 Subject: [PATCH 024/183] Stop producing Microsoft.Extensions.Process.Sources This was only used in the aspnet/AspNetCore repo. so the source for this project is moving to that repo \n\nCommit migrated from https://github.com/dotnet/extensions/commit/98522c18fbce3570c108618db7ced3f5ed74a7b1 --- src/Shared/Process/ProcessHelper.cs | 113 ---------------------------- 1 file changed, 113 deletions(-) delete mode 100644 src/Shared/Process/ProcessHelper.cs diff --git a/src/Shared/Process/ProcessHelper.cs b/src/Shared/Process/ProcessHelper.cs deleted file mode 100644 index cf42a7e3a77e..000000000000 --- a/src/Shared/Process/ProcessHelper.cs +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Runtime.InteropServices; - -namespace Microsoft.Extensions.Internal -{ - internal static class ProcessExtensions - { - private static readonly bool _isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); - private static readonly TimeSpan _defaultTimeout = TimeSpan.FromSeconds(30); - - public static void KillTree(this Process process) - { - process.KillTree(_defaultTimeout); - } - - public static void KillTree(this Process process, TimeSpan timeout) - { - string stdout; - if (_isWindows) - { - RunProcessAndWaitForExit( - "taskkill", - $"/T /F /PID {process.Id}", - timeout, - out stdout); - } - else - { - var children = new HashSet(); - GetAllChildIdsUnix(process.Id, children, timeout); - foreach (var childId in children) - { - KillProcessUnix(childId, timeout); - } - KillProcessUnix(process.Id, timeout); - } - } - - private static void GetAllChildIdsUnix(int parentId, ISet children, TimeSpan timeout) - { - string stdout; - var exitCode = RunProcessAndWaitForExit( - "pgrep", - $"-P {parentId}", - timeout, - out stdout); - - if (exitCode == 0 && !string.IsNullOrEmpty(stdout)) - { - using (var reader = new StringReader(stdout)) - { - while (true) - { - var text = reader.ReadLine(); - if (text == null) - { - return; - } - - int id; - if (int.TryParse(text, out id)) - { - children.Add(id); - // Recursively get the children - GetAllChildIdsUnix(id, children, timeout); - } - } - } - } - } - - private static void KillProcessUnix(int processId, TimeSpan timeout) - { - string stdout; - RunProcessAndWaitForExit( - "kill", - $"-TERM {processId}", - timeout, - out stdout); - } - - private static int RunProcessAndWaitForExit(string fileName, string arguments, TimeSpan timeout, out string stdout) - { - var startInfo = new ProcessStartInfo - { - FileName = fileName, - Arguments = arguments, - RedirectStandardOutput = true, - UseShellExecute = false - }; - - var process = Process.Start(startInfo); - - stdout = null; - if (process.WaitForExit((int)timeout.TotalMilliseconds)) - { - stdout = process.StandardOutput.ReadToEnd(); - } - else - { - process.Kill(); - } - - return process.ExitCode; - } - } -} From 2d001bb58319e4aac567caae36d08d2606f91b1e Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Thu, 13 Dec 2018 10:33:47 -0800 Subject: [PATCH 025/183] Stop producing Microsoft.AspNetCore.Certificates.Generation.Sources This was only used in the aspnet/AspNetCore repo. so the source for this project is moving to that repo \n\nCommit migrated from https://github.com/dotnet/extensions/commit/9290dc1dbccdb6c6063970a0c1ac5f384cc95785 --- .../CertificateManager.cs | 720 ------------------ .../CertificatePurpose.cs | 12 - .../Directory.Build.props | 7 - .../EnsureCertificateResult.cs | 20 - .../Shared.Tests/CertificateManagerTests.cs | 304 -------- 5 files changed, 1063 deletions(-) delete mode 100644 src/Shared/CertificateGeneration/CertificateManager.cs delete mode 100644 src/Shared/CertificateGeneration/CertificatePurpose.cs delete mode 100644 src/Shared/CertificateGeneration/Directory.Build.props delete mode 100644 src/Shared/CertificateGeneration/EnsureCertificateResult.cs delete mode 100644 src/Shared/test/Shared.Tests/CertificateManagerTests.cs diff --git a/src/Shared/CertificateGeneration/CertificateManager.cs b/src/Shared/CertificateGeneration/CertificateManager.cs deleted file mode 100644 index 4e2a0a99643f..000000000000 --- a/src/Shared/CertificateGeneration/CertificateManager.cs +++ /dev/null @@ -1,720 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.X509Certificates; -using System.Text; -using System.Text.RegularExpressions; - -namespace Microsoft.AspNetCore.Certificates.Generation -{ - internal class CertificateManager - { - public const string AspNetHttpsOid = "1.3.6.1.4.1.311.84.1.1"; - public const string AspNetHttpsOidFriendlyName = "ASP.NET Core HTTPS development certificate"; - - public const string AspNetIdentityOid = "1.3.6.1.4.1.311.84.1.2"; - public const string AspNetIdentityOidFriendlyName = "ASP.NET Core Identity Json Web Token signing development certificate"; - - private const string ServerAuthenticationEnhancedKeyUsageOid = "1.3.6.1.5.5.7.3.1"; - private const string ServerAuthenticationEnhancedKeyUsageOidFriendlyName = "Server Authentication"; - - private const string LocalhostHttpsDnsName = "localhost"; - private const string LocalhostHttpsDistinguishedName = "CN=" + LocalhostHttpsDnsName; - - private const string IdentityDistinguishedName = "CN=Microsoft.AspNetCore.Identity.Signing"; - - public const int RSAMinimumKeySizeInBits = 2048; - - private static readonly TimeSpan MaxRegexTimeout = TimeSpan.FromMinutes(1); - private const string CertificateSubjectRegex = "CN=(.*[^,]+).*"; - private const string MacOSSystemKeyChain = "/Library/Keychains/System.keychain"; - private static readonly string MacOSUserKeyChain = Environment.GetEnvironmentVariable("HOME") + "/Library/Keychains/login.keychain-db"; - private const string MacOSFindCertificateCommandLine = "security"; -#if NETCOREAPP2_0 || NETCOREAPP2_1 - private static readonly string MacOSFindCertificateCommandLineArgumentsFormat = "find-certificate -c {0} -a -Z -p " + MacOSSystemKeyChain; -#endif - private const string MacOSFindCertificateOutputRegex = "SHA-1 hash: ([0-9A-Z]+)"; - private const string MacOSRemoveCertificateTrustCommandLine = "sudo"; - private const string MacOSRemoveCertificateTrustCommandLineArgumentsFormat = "security remove-trusted-cert -d {0}"; - private const string MacOSDeleteCertificateCommandLine = "sudo"; - private const string MacOSDeleteCertificateCommandLineArgumentsFormat = "security delete-certificate -Z {0} {1}"; - private const string MacOSTrustCertificateCommandLine = "sudo"; -#if NETCOREAPP2_0 || NETCOREAPP2_1 - private static readonly string MacOSTrustCertificateCommandLineArguments = "security add-trusted-cert -d -r trustRoot -k " + MacOSSystemKeyChain + " "; -#endif - private const int UserCancelledErrorCode = 1223; - - public IList ListCertificates( - CertificatePurpose purpose, - StoreName storeName, - StoreLocation location, - bool isValid, - bool requireExportable = true) - { - var certificates = new List(); - try - { - using (var store = new X509Store(storeName, location)) - { - store.Open(OpenFlags.ReadOnly); - certificates.AddRange(store.Certificates.OfType()); - IEnumerable matchingCertificates = certificates; - switch (purpose) - { - case CertificatePurpose.All: - matchingCertificates = matchingCertificates - .Where(c => HasOid(c, AspNetHttpsOid) || HasOid(c, AspNetIdentityOid)); - break; - case CertificatePurpose.HTTPS: - matchingCertificates = matchingCertificates - .Where(c => HasOid(c, AspNetHttpsOid)); - break; - case CertificatePurpose.Signing: - matchingCertificates = matchingCertificates - .Where(c => HasOid(c, AspNetIdentityOid)); - break; - default: - break; - } - if (isValid) - { - // Ensure the certificate hasn't expired, has a private key and its exportable - // (for container/unix scenarios). - var now = DateTimeOffset.Now; - matchingCertificates = matchingCertificates - .Where(c => c.NotBefore <= now && - now <= c.NotAfter && - (!requireExportable || !RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || IsExportable(c))); - } - - // We need to enumerate the certificates early to prevent dispoisng issues. - matchingCertificates = matchingCertificates.ToList(); - - var certificatesToDispose = certificates.Except(matchingCertificates); - DisposeCertificates(certificatesToDispose); - - store.Close(); - - return (IList)matchingCertificates; - } - } - catch - { - DisposeCertificates(certificates); - certificates.Clear(); - return certificates; - } - - bool HasOid(X509Certificate2 certificate, string oid) => - certificate.Extensions.OfType() - .Any(e => string.Equals(oid, e.Oid.Value, StringComparison.Ordinal)); -#if !XPLAT - bool IsExportable(X509Certificate2 c) => - ((c.GetRSAPrivateKey() is RSACryptoServiceProvider rsaPrivateKey && - rsaPrivateKey.CspKeyContainerInfo.Exportable) || - (c.GetRSAPrivateKey() is RSACng cngPrivateKey && - cngPrivateKey.Key.ExportPolicy == CngExportPolicies.AllowExport)); -#else - // Only check for RSA CryptoServiceProvider and do not fail in XPlat tooling as - // System.Security.Cryptography.Cng is not pat of the shared framework and we don't - // want to bring the dependency in on CLI scenarios. This functionality will be used - // on CLI scenarios as part of the first run experience, so checking the exportability - // of the certificate is not important. - bool IsExportable(X509Certificate2 c) => - ((c.GetRSAPrivateKey() is RSACryptoServiceProvider rsaPrivateKey && - rsaPrivateKey.CspKeyContainerInfo.Exportable) || !(c.GetRSAPrivateKey() is RSACryptoServiceProvider)); -#endif - } - - private void DisposeCertificates(IEnumerable disposables) - { - foreach (var disposable in disposables) - { - try - { - disposable.Dispose(); - } - catch - { - } - } - } - -#if NETCOREAPP2_0 || NETCOREAPP2_1 - - public X509Certificate2 CreateAspNetCoreHttpsDevelopmentCertificate(DateTimeOffset notBefore, DateTimeOffset notAfter, string subjectOverride) - { - var subject = new X500DistinguishedName(subjectOverride ?? LocalhostHttpsDistinguishedName); - var extensions = new List(); - var sanBuilder = new SubjectAlternativeNameBuilder(); - sanBuilder.AddDnsName(LocalhostHttpsDnsName); - - var keyUsage = new X509KeyUsageExtension(X509KeyUsageFlags.KeyEncipherment, critical: true); - var enhancedKeyUsage = new X509EnhancedKeyUsageExtension( - new OidCollection() { - new Oid( - ServerAuthenticationEnhancedKeyUsageOid, - ServerAuthenticationEnhancedKeyUsageOidFriendlyName) - }, - critical: true); - - var basicConstraints = new X509BasicConstraintsExtension( - certificateAuthority: false, - hasPathLengthConstraint: false, - pathLengthConstraint: 0, - critical: true); - - var aspNetHttpsExtension = new X509Extension( - new AsnEncodedData( - new Oid(AspNetHttpsOid, AspNetHttpsOidFriendlyName), - Encoding.ASCII.GetBytes(AspNetHttpsOidFriendlyName)), - critical: false); - - extensions.Add(basicConstraints); - extensions.Add(keyUsage); - extensions.Add(enhancedKeyUsage); - extensions.Add(sanBuilder.Build(critical: true)); - extensions.Add(aspNetHttpsExtension); - - var certificate = CreateSelfSignedCertificate(subject, extensions, notBefore, notAfter); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - certificate.FriendlyName = AspNetHttpsOidFriendlyName; - } - - return certificate; - } - - public X509Certificate2 CreateApplicationTokenSigningDevelopmentCertificate(DateTimeOffset notBefore, DateTimeOffset notAfter, string subjectOverride) - { - var subject = new X500DistinguishedName(subjectOverride ?? IdentityDistinguishedName); - var extensions = new List(); - - var keyUsage = new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature, critical: true); - var enhancedKeyUsage = new X509EnhancedKeyUsageExtension( - new OidCollection() { - new Oid( - ServerAuthenticationEnhancedKeyUsageOid, - ServerAuthenticationEnhancedKeyUsageOidFriendlyName) - }, - critical: true); - - var basicConstraints = new X509BasicConstraintsExtension( - certificateAuthority: false, - hasPathLengthConstraint: false, - pathLengthConstraint: 0, - critical: true); - - var aspNetIdentityExtension = new X509Extension( - new AsnEncodedData( - new Oid(AspNetIdentityOid, AspNetIdentityOidFriendlyName), - Encoding.ASCII.GetBytes(AspNetIdentityOidFriendlyName)), - critical: false); - - extensions.Add(basicConstraints); - extensions.Add(keyUsage); - extensions.Add(enhancedKeyUsage); - extensions.Add(aspNetIdentityExtension); - - var certificate = CreateSelfSignedCertificate(subject, extensions, notBefore, notAfter); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - certificate.FriendlyName = AspNetIdentityOidFriendlyName; - } - - return certificate; - } - - public X509Certificate2 CreateSelfSignedCertificate( - X500DistinguishedName subject, - IEnumerable extensions, - DateTimeOffset notBefore, - DateTimeOffset notAfter) - { - var key = CreateKeyMaterial(RSAMinimumKeySizeInBits); - - var request = new CertificateRequest(subject, key, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); - foreach (var extension in extensions) - { - request.CertificateExtensions.Add(extension); - } - - return request.CreateSelfSigned(notBefore, notAfter); - - RSA CreateKeyMaterial(int minimumKeySize) - { - var rsa = RSA.Create(minimumKeySize); - if (rsa.KeySize < minimumKeySize) - { - throw new InvalidOperationException($"Failed to create a key with a size of {minimumKeySize} bits"); - } - - return rsa; - } - } - - public X509Certificate2 SaveCertificateInStore(X509Certificate2 certificate, StoreName name, StoreLocation location) - { - var imported = certificate; - if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - // On non OSX systems we need to export the certificate and import it so that the transient - // key that we generated gets persisted. - var export = certificate.Export(X509ContentType.Pkcs12, ""); - imported = new X509Certificate2(export, "", X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable); - Array.Clear(export, 0, export.Length); - } - - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - imported.FriendlyName = certificate.FriendlyName; - } - - using (var store = new X509Store(name, location)) - { - store.Open(OpenFlags.ReadWrite); - store.Add(imported); - store.Close(); - }; - - return imported; - } - - public void ExportCertificate(X509Certificate2 certificate, string path, bool includePrivateKey, string password) - { - if (Path.GetDirectoryName(path) != "") - { - Directory.CreateDirectory(Path.GetDirectoryName(path)); - } - - if (includePrivateKey) - { - var bytes = certificate.Export(X509ContentType.Pkcs12, password); - try - { - File.WriteAllBytes(path, bytes); - } - finally - { - Array.Clear(bytes, 0, bytes.Length); - } - } - else - { - var bytes = certificate.Export(X509ContentType.Cert); - File.WriteAllBytes(path, bytes); - } - } - - public void TrustCertificate(X509Certificate2 certificate) - { - // Strip certificate of the private key if any. - var publicCertificate = new X509Certificate2(certificate.Export(X509ContentType.Cert)); - - if (!IsTrusted(publicCertificate)) - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - TrustCertificateOnWindows(certificate, publicCertificate); - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - TrustCertificateOnMac(publicCertificate); - } - } - } - - private void TrustCertificateOnMac(X509Certificate2 publicCertificate) - { - var tmpFile = Path.GetTempFileName(); - try - { - ExportCertificate(publicCertificate, tmpFile, includePrivateKey: false, password: null); - using (var process = Process.Start(MacOSTrustCertificateCommandLine, MacOSTrustCertificateCommandLineArguments + tmpFile)) - { - process.WaitForExit(); - if (process.ExitCode != 0) - { - throw new InvalidOperationException("There was an error trusting the certificate."); - } - } - } - finally - { - try - { - if (File.Exists(tmpFile)) - { - File.Delete(tmpFile); - } - } - catch - { - // We don't care if we can't delete the temp file. - } - } - } - - private static void TrustCertificateOnWindows(X509Certificate2 certificate, X509Certificate2 publicCertificate) - { - publicCertificate.FriendlyName = certificate.FriendlyName; - - using (var store = new X509Store(StoreName.Root, StoreLocation.CurrentUser)) - { - store.Open(OpenFlags.ReadWrite); - try - { - store.Add(publicCertificate); - } - catch (CryptographicException exception) when (exception.HResult == UserCancelledErrorCode) - { - throw new UserCancelledTrustException(); - } - store.Close(); - }; - } - - public bool IsTrusted(X509Certificate2 certificate) - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - return ListCertificates(CertificatePurpose.HTTPS, StoreName.Root, StoreLocation.CurrentUser, isValid: true, requireExportable: false) - .Any(c => c.Thumbprint == certificate.Thumbprint); - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - var subjectMatch = Regex.Match(certificate.Subject, CertificateSubjectRegex, RegexOptions.Singleline, MaxRegexTimeout); - if (!subjectMatch.Success) - { - throw new InvalidOperationException($"Can't determine the subject for the certificate with subject '{certificate.Subject}'."); - } - var subject = subjectMatch.Groups[1].Value; - using (var checkTrustProcess = Process.Start(new ProcessStartInfo( - MacOSFindCertificateCommandLine, - string.Format(MacOSFindCertificateCommandLineArgumentsFormat, subject)) - { - RedirectStandardOutput = true - })) - { - var output = checkTrustProcess.StandardOutput.ReadToEnd(); - checkTrustProcess.WaitForExit(); - var matches = Regex.Matches(output, MacOSFindCertificateOutputRegex, RegexOptions.Multiline, MaxRegexTimeout); - var hashes = matches.OfType().Select(m => m.Groups[1].Value).ToList(); - return hashes.Any(h => string.Equals(h, certificate.Thumbprint, StringComparison.Ordinal)); - } - } - else - { - return false; - } - } - - public void CleanupHttpsCertificates(string subject = LocalhostHttpsDistinguishedName) - { - CleanupCertificates(CertificatePurpose.HTTPS, subject); - } - - public void CleanupCertificates(CertificatePurpose purpose, string subject) - { - // On OS X we don't have a good way to manage trusted certificates in the system keychain - // so we do everything by invoking the native toolchain. - // This has some limitations, like for example not being able to identify our custom OID extension. For that - // matter, when we are cleaning up certificates on the machine, we start by removing the trusted certificates. - // To do this, we list the certificates that we can identify on the current user personal store and we invoke - // the native toolchain to remove them from the sytem keychain. Once we have removed the trusted certificates, - // we remove the certificates from the local user store to finish up the cleanup. - var certificates = ListCertificates(purpose, StoreName.My, StoreLocation.CurrentUser, isValid: false); - foreach (var certificate in certificates) - { - RemoveCertificate(certificate, RemoveLocations.All); - } - } - - public void RemoveAllCertificates(CertificatePurpose purpose, StoreName storeName, StoreLocation storeLocation, string subject = null) - { - var certificates = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? - ListCertificates(purpose, StoreName.My, StoreLocation.CurrentUser, isValid: false) : - ListCertificates(purpose, storeName, storeLocation, isValid: false); - var certificatesWithName = subject == null ? certificates : certificates.Where(c => c.Subject == subject); - - var removeLocation = storeName == StoreName.My ? RemoveLocations.Local : RemoveLocations.Trusted; - - foreach (var certificate in certificates) - { - RemoveCertificate(certificate, removeLocation); - } - - DisposeCertificates(certificates); - } - - private void RemoveCertificate(X509Certificate2 certificate, RemoveLocations locations) - { - switch (locations) - { - case RemoveLocations.Undefined: - throw new InvalidOperationException($"'{nameof(RemoveLocations.Undefined)}' is not a valid location."); - case RemoveLocations.Local: - RemoveCertificateFromUserStore(certificate); - break; - case RemoveLocations.Trusted when !RuntimeInformation.IsOSPlatform(OSPlatform.Linux): - RemoveCertificateFromTrustedRoots(certificate); - break; - case RemoveLocations.All: - if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - RemoveCertificateFromTrustedRoots(certificate); - } - RemoveCertificateFromUserStore(certificate); - break; - default: - throw new InvalidOperationException("Invalid location."); - } - } - - private static void RemoveCertificateFromUserStore(X509Certificate2 certificate) - { - using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser)) - { - store.Open(OpenFlags.ReadWrite); - var matching = store.Certificates - .OfType() - .Single(c => c.SerialNumber == certificate.SerialNumber); - - store.Remove(matching); - store.Close(); - } - } - - private void RemoveCertificateFromTrustedRoots(X509Certificate2 certificate) - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - using (var store = new X509Store(StoreName.Root, StoreLocation.CurrentUser)) - { - store.Open(OpenFlags.ReadWrite); - var matching = store.Certificates - .OfType() - .Single(c => c.SerialNumber == certificate.SerialNumber); - - store.Remove(matching); - store.Close(); - } - } - else - { - if (IsTrusted(certificate)) // On OSX this check just ensures its on the system keychain - { - try - { - RemoveCertificateTrustRule(certificate); - } - catch - { - // We don't care if we fail to remove the trust rule if - // for some reason the certificate became untrusted. - // The delete command will fail if the certificate is - // trusted. - } - RemoveCertificateFromKeyChain(MacOSSystemKeyChain, certificate); - } - } - } - - private static void RemoveCertificateTrustRule(X509Certificate2 certificate) - { - var certificatePath = Path.GetTempFileName(); - try - { - var certBytes = certificate.Export(X509ContentType.Cert); - File.WriteAllBytes(certificatePath, certBytes); - var processInfo = new ProcessStartInfo( - MacOSRemoveCertificateTrustCommandLine, - string.Format( - MacOSRemoveCertificateTrustCommandLineArgumentsFormat, - certificatePath - )); - using (var process = Process.Start(processInfo)) - { - process.WaitForExit(); - } - } - finally - { - try - { - if (File.Exists(certificatePath)) - { - File.Delete(certificatePath); - } - } - catch - { - // We don't care about failing to do clean-up on a temp file. - } - } - } - - private static void RemoveCertificateFromKeyChain(string keyChain, X509Certificate2 certificate) - { - var processInfo = new ProcessStartInfo( - MacOSDeleteCertificateCommandLine, - string.Format( - MacOSDeleteCertificateCommandLineArgumentsFormat, - certificate.Thumbprint.ToUpperInvariant(), - keyChain - )) - { - RedirectStandardOutput = true, - RedirectStandardError = true - }; - - using (var process = Process.Start(processInfo)) - { - var output = process.StandardOutput.ReadToEnd() + process.StandardError.ReadToEnd(); - process.WaitForExit(); - - if (process.ExitCode != 0) - { - throw new InvalidOperationException($@"There was an error removing the certificate with thumbprint '{certificate.Thumbprint}'. - -{output}"); - } - } - } - - public EnsureCertificateResult EnsureAspNetCoreHttpsDevelopmentCertificate( - DateTimeOffset notBefore, - DateTimeOffset notAfter, - string path = null, - bool trust = false, - bool includePrivateKey = false, - string password = null, - string subject = LocalhostHttpsDistinguishedName) - { - return EnsureValidCertificateExists(notBefore, notAfter, CertificatePurpose.HTTPS, path, trust, includePrivateKey, password, subject); - } - - public EnsureCertificateResult EnsureAspNetCoreApplicationTokensDevelopmentCertificate( - DateTimeOffset notBefore, - DateTimeOffset notAfter, - string path = null, - bool trust = false, - bool includePrivateKey = false, - string password = null, - string subject = IdentityDistinguishedName) - { - return EnsureValidCertificateExists(notBefore, notAfter, CertificatePurpose.Signing, path, trust, includePrivateKey, password, subject); - } - - public EnsureCertificateResult EnsureValidCertificateExists( - DateTimeOffset notBefore, - DateTimeOffset notAfter, - CertificatePurpose purpose, - string path = null, - bool trust = false, - bool includePrivateKey = false, - string password = null, - string subjectOverride = null) - { - if (purpose == CertificatePurpose.All) - { - throw new ArgumentException("The certificate must have a specific purpose."); - } - - var certificates = ListCertificates(purpose, StoreName.My, StoreLocation.CurrentUser, isValid: true).Concat( - ListCertificates(purpose, StoreName.My, StoreLocation.LocalMachine, isValid: true)); - - certificates = subjectOverride == null ? certificates : certificates.Where(c => c.Subject == subjectOverride); - - var result = EnsureCertificateResult.Succeeded; - - X509Certificate2 certificate = null; - if (certificates.Count() > 0) - { - certificate = certificates.FirstOrDefault(); - result = EnsureCertificateResult.ValidCertificatePresent; - } - else - { - try - { - switch (purpose) - { - case CertificatePurpose.All: - throw new InvalidOperationException("The certificate must have a specific purpose."); - case CertificatePurpose.HTTPS: - certificate = CreateAspNetCoreHttpsDevelopmentCertificate(notBefore, notAfter, subjectOverride); - break; - case CertificatePurpose.Signing: - certificate = CreateApplicationTokenSigningDevelopmentCertificate(notBefore, notAfter, subjectOverride); - break; - default: - throw new InvalidOperationException("The certificate must have a purpose."); - } - } - catch - { - return EnsureCertificateResult.ErrorCreatingTheCertificate; - } - - try - { - certificate = SaveCertificateInStore(certificate, StoreName.My, StoreLocation.CurrentUser); - } - catch - { - return EnsureCertificateResult.ErrorSavingTheCertificateIntoTheCurrentUserPersonalStore; - } - } - if (path != null) - { - try - { - ExportCertificate(certificate, path, includePrivateKey, password); - } - catch - { - return EnsureCertificateResult.ErrorExportingTheCertificate; - } - } - - if ((RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) && trust) - { - try - { - TrustCertificate(certificate); - } - catch (UserCancelledTrustException) - { - return EnsureCertificateResult.UserCancelledTrustStep; - } - catch - { - return EnsureCertificateResult.FailedToTrustTheCertificate; - } - } - - return result; - } - - private class UserCancelledTrustException : Exception - { - } - - private enum RemoveLocations - { - Undefined, - Local, - Trusted, - All - } -#endif - } -} \ No newline at end of file diff --git a/src/Shared/CertificateGeneration/CertificatePurpose.cs b/src/Shared/CertificateGeneration/CertificatePurpose.cs deleted file mode 100644 index 1ad1a6d79b65..000000000000 --- a/src/Shared/CertificateGeneration/CertificatePurpose.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -namespace Microsoft.AspNetCore.Certificates.Generation -{ - internal enum CertificatePurpose - { - All, - HTTPS, - Signing - } -} \ No newline at end of file diff --git a/src/Shared/CertificateGeneration/Directory.Build.props b/src/Shared/CertificateGeneration/Directory.Build.props deleted file mode 100644 index f3f6fc4b350c..000000000000 --- a/src/Shared/CertificateGeneration/Directory.Build.props +++ /dev/null @@ -1,7 +0,0 @@ - - - - - Microsoft.AspNetCore.Certificates.Generation.Sources - - diff --git a/src/Shared/CertificateGeneration/EnsureCertificateResult.cs b/src/Shared/CertificateGeneration/EnsureCertificateResult.cs deleted file mode 100644 index d3c86ce05d08..000000000000 --- a/src/Shared/CertificateGeneration/EnsureCertificateResult.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -#if NETCOREAPP2_0 || NETCOREAPP2_1 - -namespace Microsoft.AspNetCore.Certificates.Generation -{ - internal enum EnsureCertificateResult - { - Succeeded = 1, - ValidCertificatePresent, - ErrorCreatingTheCertificate, - ErrorSavingTheCertificateIntoTheCurrentUserPersonalStore, - ErrorExportingTheCertificate, - FailedToTrustTheCertificate, - UserCancelledTrustStep - } -} - -#endif \ No newline at end of file diff --git a/src/Shared/test/Shared.Tests/CertificateManagerTests.cs b/src/Shared/test/Shared.Tests/CertificateManagerTests.cs deleted file mode 100644 index 613f5c966f15..000000000000 --- a/src/Shared/test/Shared.Tests/CertificateManagerTests.cs +++ /dev/null @@ -1,304 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -#if NETCOREAPP2_0 || NETCOREAPP2_1 - -using System; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.X509Certificates; -using System.Text; -using Xunit; -using Xunit.Abstractions; - -namespace Microsoft.AspNetCore.Certificates.Generation.Tests -{ - public class CertificateManagerTests - { - public CertificateManagerTests(ITestOutputHelper output) - { - Output = output; - } - - public const string TestCertificateSubject = "CN=aspnet.test"; - - public ITestOutputHelper Output { get; } - - [Fact] - public void EnsureCreateHttpsCertificate_CreatesACertificate_WhenThereAreNoHttpsCertificates() - { - try - { - // Arrange - const string CertificateName = nameof(EnsureCreateHttpsCertificate_CreatesACertificate_WhenThereAreNoHttpsCertificates) + ".cer"; - var manager = new CertificateManager(); - - manager.RemoveAllCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, TestCertificateSubject); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - manager.RemoveAllCertificates(CertificatePurpose.HTTPS, StoreName.Root, StoreLocation.CurrentUser, TestCertificateSubject); - } - - // Act - DateTimeOffset now = DateTimeOffset.UtcNow; - now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset); - var result = manager.EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1), CertificateName, trust: false, subject: TestCertificateSubject); - - // Assert - Assert.Equal(EnsureCertificateResult.Succeeded, result); - Assert.True(File.Exists(CertificateName)); - - var exportedCertificate = new X509Certificate2(File.ReadAllBytes(CertificateName)); - Assert.NotNull(exportedCertificate); - Assert.False(exportedCertificate.HasPrivateKey); - - var httpsCertificates = manager.ListCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, isValid: false); - var httpsCertificate = Assert.Single(httpsCertificates, c => c.Subject == TestCertificateSubject); - Assert.True(httpsCertificate.HasPrivateKey); - Assert.Equal(TestCertificateSubject, httpsCertificate.Subject); - Assert.Equal(TestCertificateSubject, httpsCertificate.Issuer); - Assert.Equal("sha256RSA", httpsCertificate.SignatureAlgorithm.FriendlyName); - Assert.Equal("1.2.840.113549.1.1.11", httpsCertificate.SignatureAlgorithm.Value); - - Assert.Equal(now.LocalDateTime, httpsCertificate.NotBefore); - Assert.Equal(now.AddYears(1).LocalDateTime, httpsCertificate.NotAfter); - Assert.Contains( - httpsCertificate.Extensions.OfType(), - e => e is X509BasicConstraintsExtension basicConstraints && - basicConstraints.Critical == true && - basicConstraints.CertificateAuthority == false && - basicConstraints.HasPathLengthConstraint == false && - basicConstraints.PathLengthConstraint == 0); - - Assert.Contains( - httpsCertificate.Extensions.OfType(), - e => e is X509KeyUsageExtension keyUsage && - keyUsage.Critical == true && - keyUsage.KeyUsages == X509KeyUsageFlags.KeyEncipherment); - - Assert.Contains( - httpsCertificate.Extensions.OfType(), - e => e is X509EnhancedKeyUsageExtension enhancedKeyUsage && - enhancedKeyUsage.Critical == true && - enhancedKeyUsage.EnhancedKeyUsages.OfType().Single() is Oid keyUsage && - keyUsage.Value == "1.3.6.1.5.5.7.3.1"); - - // Subject alternative name - Assert.Contains( - httpsCertificate.Extensions.OfType(), - e => e.Critical == true && - e.Oid.Value == "2.5.29.17"); - - // ASP.NET HTTPS Development certificate extension - Assert.Contains( - httpsCertificate.Extensions.OfType(), - e => e.Critical == false && - e.Oid.Value == "1.3.6.1.4.1.311.84.1.1" && - Encoding.ASCII.GetString(e.RawData) == "ASP.NET Core HTTPS development certificate"); - - Assert.Equal(httpsCertificate.GetCertHashString(), exportedCertificate.GetCertHashString()); - - } - catch (Exception e) - { - Output.WriteLine(e.Message); - ListCertificates(Output); - throw; - } - } - - private void ListCertificates(ITestOutputHelper output) - { - using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser)) - { - store.Open(OpenFlags.ReadOnly); - var certificates = store.Certificates; - foreach (var certificate in certificates) - { - Output.WriteLine($"Certificate: '{Convert.ToBase64String(certificate.Export(X509ContentType.Cert))}'."); - certificate.Dispose(); - } - - store.Close(); - } - } - - [Fact] - public void EnsureCreateHttpsCertificate_DoesNotCreateACertificate_WhenThereIsAnExistingHttpsCertificates() - { - // Arrange - const string CertificateName = nameof(EnsureCreateHttpsCertificate_DoesNotCreateACertificate_WhenThereIsAnExistingHttpsCertificates) + ".pfx"; - var certificatePassword = Guid.NewGuid().ToString(); - - var manager = new CertificateManager(); - - manager.RemoveAllCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, TestCertificateSubject); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - manager.RemoveAllCertificates(CertificatePurpose.HTTPS, StoreName.Root, StoreLocation.CurrentUser, TestCertificateSubject); - } - - DateTimeOffset now = DateTimeOffset.UtcNow; - now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset); - manager.EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1), path: null, trust: false, subject: TestCertificateSubject); - - var httpsCertificate = manager.ListCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, isValid: false).Single(c => c.Subject == TestCertificateSubject); - - // Act - var result = manager.EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1), CertificateName, trust: false, includePrivateKey: true, password: certificatePassword, subject: TestCertificateSubject); - - // Assert - Assert.Equal(EnsureCertificateResult.ValidCertificatePresent, result); - Assert.True(File.Exists(CertificateName)); - - var exportedCertificate = new X509Certificate2(File.ReadAllBytes(CertificateName), certificatePassword); - Assert.NotNull(exportedCertificate); - Assert.True(exportedCertificate.HasPrivateKey); - - - Assert.Equal(httpsCertificate.GetCertHashString(), exportedCertificate.GetCertHashString()); - } - - [Fact(Skip = "Requires user interaction")] - public void EnsureAspNetCoreHttpsDevelopmentCertificate_ReturnsCorrectResult_WhenUserCancelsTrustStepOnWindows() - { - var manager = new CertificateManager(); - - manager.RemoveAllCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, TestCertificateSubject); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - manager.RemoveAllCertificates(CertificatePurpose.HTTPS, StoreName.Root, StoreLocation.CurrentUser, TestCertificateSubject); - } - - DateTimeOffset now = DateTimeOffset.UtcNow; - now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset); - var trustFailed = manager.EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1), path: null, trust: true, subject: TestCertificateSubject); - - Assert.Equal(EnsureCertificateResult.UserCancelledTrustStep, trustFailed); - } - - [Fact(Skip = "Requires user interaction")] - public void EnsureAspNetCoreHttpsDevelopmentCertificate_CanRemoveCertificates() - { - var manager = new CertificateManager(); - - DateTimeOffset now = DateTimeOffset.UtcNow; - now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset); - manager.EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1), path: null, trust: true, subject: TestCertificateSubject); - - manager.CleanupHttpsCertificates(TestCertificateSubject); - - Assert.Empty(manager.ListCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, isValid: false).Where(c => c.Subject == TestCertificateSubject)); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - Assert.Empty(manager.ListCertificates(CertificatePurpose.HTTPS, StoreName.Root, StoreLocation.CurrentUser, isValid: false).Where(c => c.Subject == TestCertificateSubject)); - } - } - - [Fact] - public void EnsureCreateIdentityTokenSigningCertificate_CreatesACertificate_WhenThereAreNoHttpsCertificates() - { - // Arrange - const string CertificateName = nameof(EnsureCreateIdentityTokenSigningCertificate_CreatesACertificate_WhenThereAreNoHttpsCertificates) + ".cer"; - var manager = new CertificateManager(); - - manager.RemoveAllCertificates(CertificatePurpose.Signing, StoreName.My, StoreLocation.CurrentUser, TestCertificateSubject); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - manager.RemoveAllCertificates(CertificatePurpose.Signing, StoreName.Root, StoreLocation.CurrentUser, TestCertificateSubject); - } - - // Act - DateTimeOffset now = DateTimeOffset.UtcNow; - now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset); - var result = manager.EnsureAspNetCoreApplicationTokensDevelopmentCertificate(now, now.AddYears(1), CertificateName, trust: false, subject: TestCertificateSubject); - - // Assert - Assert.Equal(EnsureCertificateResult.Succeeded, result); - Assert.True(File.Exists(CertificateName)); - - var exportedCertificate = new X509Certificate2(File.ReadAllBytes(CertificateName)); - Assert.NotNull(exportedCertificate); - Assert.False(exportedCertificate.HasPrivateKey); - - var identityCertificates = manager.ListCertificates(CertificatePurpose.Signing, StoreName.My, StoreLocation.CurrentUser, isValid: false); - var identityCertificate = Assert.Single(identityCertificates, i => i.Subject == TestCertificateSubject); - Assert.True(identityCertificate.HasPrivateKey); - Assert.Equal(TestCertificateSubject, identityCertificate.Subject); - Assert.Equal(TestCertificateSubject, identityCertificate.Issuer); - Assert.Equal("sha256RSA", identityCertificate.SignatureAlgorithm.FriendlyName); - Assert.Equal("1.2.840.113549.1.1.11", identityCertificate.SignatureAlgorithm.Value); - - Assert.Equal(now.LocalDateTime, identityCertificate.NotBefore); - Assert.Equal(now.AddYears(1).LocalDateTime, identityCertificate.NotAfter); - Assert.Contains( - identityCertificate.Extensions.OfType(), - e => e is X509BasicConstraintsExtension basicConstraints && - basicConstraints.Critical == true && - basicConstraints.CertificateAuthority == false && - basicConstraints.HasPathLengthConstraint == false && - basicConstraints.PathLengthConstraint == 0); - - Assert.Contains( - identityCertificate.Extensions.OfType(), - e => e is X509KeyUsageExtension keyUsage && - keyUsage.Critical == true && - keyUsage.KeyUsages == X509KeyUsageFlags.DigitalSignature); - - Assert.Contains( - identityCertificate.Extensions.OfType(), - e => e is X509EnhancedKeyUsageExtension enhancedKeyUsage && - enhancedKeyUsage.Critical == true && - enhancedKeyUsage.EnhancedKeyUsages.OfType().Single() is Oid keyUsage && - keyUsage.Value == "1.3.6.1.5.5.7.3.1"); - - // ASP.NET Core Identity Json Web Token signing development certificate - Assert.Contains( - identityCertificate.Extensions.OfType(), - e => e.Critical == false && - e.Oid.Value == "1.3.6.1.4.1.311.84.1.2" && - Encoding.ASCII.GetString(e.RawData) == "ASP.NET Core Identity Json Web Token signing development certificate"); - - Assert.Equal(identityCertificate.GetCertHashString(), exportedCertificate.GetCertHashString()); - } - - [Fact] - public void EnsureCreateIdentityTokenSigningCertificate_DoesNotCreateACertificate_WhenThereIsAnExistingHttpsCertificates() - { - // Arrange - const string CertificateName = nameof(EnsureCreateIdentityTokenSigningCertificate_DoesNotCreateACertificate_WhenThereIsAnExistingHttpsCertificates) + ".pfx"; - var certificatePassword = Guid.NewGuid().ToString(); - - var manager = new CertificateManager(); - - manager.RemoveAllCertificates(CertificatePurpose.Signing, StoreName.My, StoreLocation.CurrentUser, TestCertificateSubject); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - manager.RemoveAllCertificates(CertificatePurpose.Signing, StoreName.Root, StoreLocation.CurrentUser, TestCertificateSubject); - } - - DateTimeOffset now = DateTimeOffset.UtcNow; - now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset); - manager.EnsureAspNetCoreApplicationTokensDevelopmentCertificate(now, now.AddYears(1), path: null, trust: false, subject: TestCertificateSubject); - - var identityTokenSigningCertificates = manager.ListCertificates(CertificatePurpose.Signing, StoreName.My, StoreLocation.CurrentUser, isValid: false).Single(c => c.Subject == TestCertificateSubject); - - // Act - var result = manager.EnsureAspNetCoreApplicationTokensDevelopmentCertificate(now, now.AddYears(1), CertificateName, trust: false, includePrivateKey: true, password: certificatePassword, subject: TestCertificateSubject); - - // Assert - Assert.Equal(EnsureCertificateResult.ValidCertificatePresent, result); - Assert.True(File.Exists(CertificateName)); - - var exportedCertificate = new X509Certificate2(File.ReadAllBytes(CertificateName), certificatePassword); - Assert.NotNull(exportedCertificate); - Assert.True(exportedCertificate.HasPrivateKey); - - Assert.Equal(identityTokenSigningCertificates.GetCertHashString(), exportedCertificate.GetCertHashString()); - } - } -} - -#endif \ No newline at end of file From e8d84a19624358ac6917d6da8d00af8298a9a3bf Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Thu, 13 Dec 2018 10:43:02 -0800 Subject: [PATCH 026/183] Remove Microsoft.Extensions.CopyOnWriteDictionary.Sources This code will move to aspnet/AspNetCore \n\nCommit migrated from https://github.com/dotnet/extensions/commit/468940be7f7f7ed15881b828c3a60fe8ec6d5913 --- .../CopyOnWriteDictionary.cs | 155 ---------------- .../CopyOnWriteDictionaryHolder.cs | 166 ------------------ .../CopyOnWriteDictionaryHolderTest.cs | 91 ---------- .../Shared.Tests/CopyOnWriteDictionaryTest.cs | 109 ------------ 4 files changed, 521 deletions(-) delete mode 100644 src/Shared/CopyOnWriteDictionary/CopyOnWriteDictionary.cs delete mode 100644 src/Shared/CopyOnWriteDictionary/CopyOnWriteDictionaryHolder.cs delete mode 100644 src/Shared/test/Shared.Tests/CopyOnWriteDictionaryHolderTest.cs delete mode 100644 src/Shared/test/Shared.Tests/CopyOnWriteDictionaryTest.cs diff --git a/src/Shared/CopyOnWriteDictionary/CopyOnWriteDictionary.cs b/src/Shared/CopyOnWriteDictionary/CopyOnWriteDictionary.cs deleted file mode 100644 index 1408059ad923..000000000000 --- a/src/Shared/CopyOnWriteDictionary/CopyOnWriteDictionary.cs +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections; -using System.Collections.Generic; - -namespace Microsoft.Extensions.Internal -{ - internal class CopyOnWriteDictionary : IDictionary - { - private readonly IDictionary _sourceDictionary; - private readonly IEqualityComparer _comparer; - private IDictionary _innerDictionary; - - public CopyOnWriteDictionary( - IDictionary sourceDictionary, - IEqualityComparer comparer) - { - if (sourceDictionary == null) - { - throw new ArgumentNullException(nameof(sourceDictionary)); - } - - if (comparer == null) - { - throw new ArgumentNullException(nameof(comparer)); - } - - _sourceDictionary = sourceDictionary; - _comparer = comparer; - } - - private IDictionary ReadDictionary - { - get - { - return _innerDictionary ?? _sourceDictionary; - } - } - - private IDictionary WriteDictionary - { - get - { - if (_innerDictionary == null) - { - _innerDictionary = new Dictionary(_sourceDictionary, - _comparer); - } - - return _innerDictionary; - } - } - - public virtual ICollection Keys - { - get - { - return ReadDictionary.Keys; - } - } - - public virtual ICollection Values - { - get - { - return ReadDictionary.Values; - } - } - - public virtual int Count - { - get - { - return ReadDictionary.Count; - } - } - - public virtual bool IsReadOnly - { - get - { - return false; - } - } - - public virtual TValue this[TKey key] - { - get - { - return ReadDictionary[key]; - } - set - { - WriteDictionary[key] = value; - } - } - - public virtual bool ContainsKey(TKey key) - { - return ReadDictionary.ContainsKey(key); - } - - public virtual void Add(TKey key, TValue value) - { - WriteDictionary.Add(key, value); - } - - public virtual bool Remove(TKey key) - { - return WriteDictionary.Remove(key); - } - - public virtual bool TryGetValue(TKey key, out TValue value) - { - return ReadDictionary.TryGetValue(key, out value); - } - - public virtual void Add(KeyValuePair item) - { - WriteDictionary.Add(item); - } - - public virtual void Clear() - { - WriteDictionary.Clear(); - } - - public virtual bool Contains(KeyValuePair item) - { - return ReadDictionary.Contains(item); - } - - public virtual void CopyTo(KeyValuePair[] array, int arrayIndex) - { - ReadDictionary.CopyTo(array, arrayIndex); - } - - public bool Remove(KeyValuePair item) - { - return WriteDictionary.Remove(item); - } - - public virtual IEnumerator> GetEnumerator() - { - return ReadDictionary.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } -} \ No newline at end of file diff --git a/src/Shared/CopyOnWriteDictionary/CopyOnWriteDictionaryHolder.cs b/src/Shared/CopyOnWriteDictionary/CopyOnWriteDictionaryHolder.cs deleted file mode 100644 index 7cd935e94018..000000000000 --- a/src/Shared/CopyOnWriteDictionary/CopyOnWriteDictionaryHolder.cs +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; - -namespace Microsoft.Extensions.Internal -{ - internal struct CopyOnWriteDictionaryHolder - { - private readonly Dictionary _source; - private Dictionary _copy; - - public CopyOnWriteDictionaryHolder(Dictionary source) - { - if (source == null) - { - throw new ArgumentNullException(nameof(source)); - } - - _source = source; - _copy = null; - } - - public CopyOnWriteDictionaryHolder(CopyOnWriteDictionaryHolder source) - { - _source = source._copy ?? source._source; - _copy = null; - } - - public bool HasBeenCopied => _copy != null; - - public Dictionary ReadDictionary - { - get - { - if (_copy != null) - { - return _copy; - } - else if (_source != null) - { - return _source; - } - else - { - // Default-Constructor case - _copy = new Dictionary(); - return _copy; - } - } - } - - public Dictionary WriteDictionary - { - get - { - if (_copy == null && _source == null) - { - // Default-Constructor case - _copy = new Dictionary(); - } - else if (_copy == null) - { - _copy = new Dictionary(_source, _source.Comparer); - } - - return _copy; - } - } - - public Dictionary.KeyCollection Keys - { - get - { - return ReadDictionary.Keys; - } - } - - public Dictionary.ValueCollection Values - { - get - { - return ReadDictionary.Values; - } - } - - public int Count - { - get - { - return ReadDictionary.Count; - } - } - - public bool IsReadOnly - { - get - { - return false; - } - } - - public TValue this[TKey key] - { - get - { - return ReadDictionary[key]; - } - set - { - WriteDictionary[key] = value; - } - } - - public bool ContainsKey(TKey key) - { - return ReadDictionary.ContainsKey(key); - } - - public void Add(TKey key, TValue value) - { - WriteDictionary.Add(key, value); - } - - public bool Remove(TKey key) - { - return WriteDictionary.Remove(key); - } - - public bool TryGetValue(TKey key, out TValue value) - { - return ReadDictionary.TryGetValue(key, out value); - } - - public void Add(KeyValuePair item) - { - ((ICollection>)WriteDictionary).Add(item); - } - - public void Clear() - { - WriteDictionary.Clear(); - } - - public bool Contains(KeyValuePair item) - { - return ((ICollection>)ReadDictionary).Contains(item); - } - - public void CopyTo(KeyValuePair[] array, int arrayIndex) - { - ((ICollection>)ReadDictionary).CopyTo(array, arrayIndex); - } - - public bool Remove(KeyValuePair item) - { - return ((ICollection>)WriteDictionary).Remove(item); - } - - public Dictionary.Enumerator GetEnumerator() - { - return ReadDictionary.GetEnumerator(); - } - } -} diff --git a/src/Shared/test/Shared.Tests/CopyOnWriteDictionaryHolderTest.cs b/src/Shared/test/Shared.Tests/CopyOnWriteDictionaryHolderTest.cs deleted file mode 100644 index 9a0951eb271a..000000000000 --- a/src/Shared/test/Shared.Tests/CopyOnWriteDictionaryHolderTest.cs +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Linq; -using Xunit; - -namespace Microsoft.Extensions.Internal -{ - public class CopyOnWriteDictionaryHolderTest - { - [Fact] - public void ReadOperation_DelegatesToSourceDictionary_IfNoMutationsArePerformed() - { - // Arrange - var source = new Dictionary(StringComparer.OrdinalIgnoreCase) - { - { "test-key", "test-value" }, - { "key2", "key2-value" } - }; - - var holder = new CopyOnWriteDictionaryHolder(source); - - // Act and Assert - Assert.Equal("key2-value", holder["key2"]); - Assert.Equal(2, holder.Count); - Assert.Equal(new string[] { "test-key", "key2" }, holder.Keys.ToArray()); - Assert.Equal(new object[] { "test-value", "key2-value" }, holder.Values.ToArray()); - Assert.True(holder.ContainsKey("test-key")); - - object value; - Assert.False(holder.TryGetValue("different-key", out value)); - - Assert.False(holder.HasBeenCopied); - Assert.Same(source, holder.ReadDictionary); - } - - [Fact] - public void ReadOperation_DoesNotDelegateToSourceDictionary_OnceAValueIsChanged() - { - // Arrange - var source = new Dictionary(StringComparer.OrdinalIgnoreCase) - { - { "key1", "value1" }, - { "key2", "value2" } - }; - - var holder = new CopyOnWriteDictionaryHolder(source); - - // Act - holder["key2"] = "value3"; - - // Assert - Assert.Equal("value2", source["key2"]); - Assert.Equal(2, holder.Count); - Assert.Equal("value1", holder["key1"]); - Assert.Equal("value3", holder["key2"]); - - Assert.True(holder.HasBeenCopied); - Assert.NotSame(source, holder.ReadDictionary); - } - - [Fact] - public void ReadOperation_DoesNotDelegateToSourceDictionary_OnceValueIsAdded() - { - // Arrange - var source = new Dictionary(StringComparer.OrdinalIgnoreCase) - { - { "key1", "value1" }, - { "key2", "value2" } - }; - - var holder = new CopyOnWriteDictionaryHolder(source); - - // Act - holder.Add("key3", "value3"); - holder.Remove("key1"); - - // Assert - Assert.Equal(2, source.Count); - Assert.Equal("value1", source["key1"]); - Assert.Equal(2, holder.Count); - Assert.Equal("value2", holder["KeY2"]); - Assert.Equal("value3", holder["key3"]); - - Assert.True(holder.HasBeenCopied); - Assert.NotSame(source, holder.ReadDictionary); - } - } -} diff --git a/src/Shared/test/Shared.Tests/CopyOnWriteDictionaryTest.cs b/src/Shared/test/Shared.Tests/CopyOnWriteDictionaryTest.cs deleted file mode 100644 index c1b54036d4f9..000000000000 --- a/src/Shared/test/Shared.Tests/CopyOnWriteDictionaryTest.cs +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using Moq; -using Xunit; - -namespace Microsoft.Extensions.Internal -{ - public class CopyOnWriteDictionaryTest - { - [Fact] - public void ReadOperation_DelegatesToSourceDictionary_IfNoMutationsArePerformed() - { - // Arrange - var values = new List(); - var enumerator = Mock.Of>>(); - var sourceDictionary = new Mock>(MockBehavior.Strict); - sourceDictionary - .SetupGet(d => d.Count) - .Returns(100) - .Verifiable(); - sourceDictionary - .SetupGet(d => d.Values) - .Returns(values) - .Verifiable(); - sourceDictionary - .Setup(d => d.ContainsKey("test-key")) - .Returns(value: true) - .Verifiable(); - sourceDictionary - .Setup(d => d.GetEnumerator()) - .Returns(enumerator) - .Verifiable(); - sourceDictionary - .Setup(d => d["key2"]) - .Returns("key2-value") - .Verifiable(); - object value; - sourceDictionary.Setup(d => d.TryGetValue("different-key", out value)) - .Returns(false) - .Verifiable(); - - var copyOnWriteDictionary = new CopyOnWriteDictionary(sourceDictionary.Object, - StringComparer.OrdinalIgnoreCase); - - // Act and Assert - Assert.Equal("key2-value", copyOnWriteDictionary["key2"]); - Assert.Equal(100, copyOnWriteDictionary.Count); - Assert.Same(values, copyOnWriteDictionary.Values); - Assert.True(copyOnWriteDictionary.ContainsKey("test-key")); - Assert.Same(enumerator, copyOnWriteDictionary.GetEnumerator()); - Assert.False(copyOnWriteDictionary.TryGetValue("different-key", out value)); - sourceDictionary.Verify(); - } - - [Fact] - public void ReadOperation_DoesNotDelegateToSourceDictionary_OnceAValueIsChanged() - { - // Arrange - var values = new List(); - var sourceDictionary = new Dictionary - { - { "key1", "value1" }, - { "key2", "value2" } - }; - var copyOnWriteDictionary = new CopyOnWriteDictionary( - sourceDictionary, - StringComparer.OrdinalIgnoreCase); - - // Act - copyOnWriteDictionary["key2"] = "value3"; - - // Assert - Assert.Equal("value2", sourceDictionary["key2"]); - Assert.Equal(2, copyOnWriteDictionary.Count); - Assert.Equal("value1", copyOnWriteDictionary["key1"]); - Assert.Equal("value3", copyOnWriteDictionary["key2"]); - } - - [Fact] - public void ReadOperation_DoesNotDelegateToSourceDictionary_OnceDictionaryIsModified() - { - // Arrange - var values = new List(); - var sourceDictionary = new Dictionary - { - { "key1", "value1" }, - { "key2", "value2" } - }; - var copyOnWriteDictionary = new CopyOnWriteDictionary( - sourceDictionary, - StringComparer.OrdinalIgnoreCase); - - // Act - copyOnWriteDictionary.Add("key3", "value3"); - copyOnWriteDictionary.Remove("key1"); - - - // Assert - Assert.Equal(2, sourceDictionary.Count); - Assert.Equal("value1", sourceDictionary["key1"]); - Assert.Equal(2, copyOnWriteDictionary.Count); - Assert.Equal("value2", copyOnWriteDictionary["KeY2"]); - Assert.Equal("value3", copyOnWriteDictionary["key3"]); - } - } -} From 2f36a1ecf9a0564f207c5c6b1a076e2d66367b39 Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Thu, 13 Dec 2018 11:01:34 -0800 Subject: [PATCH 027/183] Remove Microsoft.Extensions.ClosedGenericMatcher.Sources This code isn't used locally, so it will move to aspnet/AspNetCore \n\nCommit migrated from https://github.com/dotnet/extensions/commit/7305817fea3c4f20235c7bb377d8396928f8a9bb --- .../ClosedGenericMatcher.cs | 106 ------ .../Shared.Tests/ClosedGenericMatcherTest.cs | 360 ------------------ 2 files changed, 466 deletions(-) delete mode 100644 src/Shared/ClosedGenericMatcher/ClosedGenericMatcher.cs delete mode 100644 src/Shared/test/Shared.Tests/ClosedGenericMatcherTest.cs diff --git a/src/Shared/ClosedGenericMatcher/ClosedGenericMatcher.cs b/src/Shared/ClosedGenericMatcher/ClosedGenericMatcher.cs deleted file mode 100644 index f234c2edbc40..000000000000 --- a/src/Shared/ClosedGenericMatcher/ClosedGenericMatcher.cs +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Linq; -using System.Reflection; - -namespace Microsoft.Extensions.Internal -{ - /// - /// Helper related to generic interface definitions and implementing classes. - /// - internal static class ClosedGenericMatcher - { - /// - /// Determine whether is or implements a closed generic - /// created from . - /// - /// The of interest. - /// The open generic to match. Usually an interface. - /// - /// The closed generic created from that - /// is or implements. null if the two s have no such - /// relationship. - /// - /// - /// This method will return if is - /// typeof(KeyValuePair{,}), and is - /// typeof(KeyValuePair{string, object}). - /// - public static Type ExtractGenericInterface(Type queryType, Type interfaceType) - { - if (queryType == null) - { - throw new ArgumentNullException(nameof(queryType)); - } - - if (interfaceType == null) - { - throw new ArgumentNullException(nameof(interfaceType)); - } - - if (IsGenericInstantiation(queryType, interfaceType)) - { - // queryType matches (i.e. is a closed generic type created from) the open generic type. - return queryType; - } - - // Otherwise check all interfaces the type implements for a match. - // - If multiple different generic instantiations exists, we want the most derived one. - // - If that doesn't break the tie, then we sort alphabetically so that it's deterministic. - // - // We do this by looking at interfaces on the type, and recursing to the base type - // if we don't find any matches. - return GetGenericInstantiation(queryType, interfaceType); - } - - private static bool IsGenericInstantiation(Type candidate, Type interfaceType) - { - return - candidate.GetTypeInfo().IsGenericType && - candidate.GetGenericTypeDefinition() == interfaceType; - } - - private static Type GetGenericInstantiation(Type queryType, Type interfaceType) - { - Type bestMatch = null; - var interfaces = queryType.GetInterfaces(); - foreach (var @interface in interfaces) - { - if (IsGenericInstantiation(@interface, interfaceType)) - { - if (bestMatch == null) - { - bestMatch = @interface; - } - else if (StringComparer.Ordinal.Compare(@interface.FullName, bestMatch.FullName) < 0) - { - bestMatch = @interface; - } - else - { - // There are two matches at this level of the class hierarchy, but @interface is after - // bestMatch in the sort order. - } - } - } - - if (bestMatch != null) - { - return bestMatch; - } - - // BaseType will be null for object and interfaces, which means we've reached 'bottom'. - var baseType = queryType?.GetTypeInfo().BaseType; - if (baseType == null) - { - return null; - } - else - { - return GetGenericInstantiation(baseType, interfaceType); - } - } - } -} \ No newline at end of file diff --git a/src/Shared/test/Shared.Tests/ClosedGenericMatcherTest.cs b/src/Shared/test/Shared.Tests/ClosedGenericMatcherTest.cs deleted file mode 100644 index e71a7926921a..000000000000 --- a/src/Shared/test/Shared.Tests/ClosedGenericMatcherTest.cs +++ /dev/null @@ -1,360 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using Xunit; - -namespace Microsoft.Extensions.Internal -{ - public class ClosedGenericMatcherTest - { - // queryType, interfaceType, expectedResult - public static TheoryData ExtractGenericInterfaceDataSet - { - get - { - return new TheoryData - { - // Closed generic types that match given open generic type. - { - typeof(IEnumerable), - typeof(IEnumerable<>), - typeof(IEnumerable) - }, - { - typeof(IReadOnlyList), - typeof(IReadOnlyList<>), - typeof(IReadOnlyList) - }, - { - typeof(KeyValuePair), - typeof(KeyValuePair<,>), - typeof(KeyValuePair) - }, - // Closed generic interfaces that implement sub-interface of given open generic type. - { - typeof(ICollection), - typeof(IEnumerable<>), - typeof(IEnumerable) - }, - { - typeof(IReadOnlyList), - typeof(IEnumerable<>), - typeof(IEnumerable) - }, - { - typeof(IDictionary), - typeof(IEnumerable<>), - typeof(IEnumerable>) - }, - // Class that implements closed generic based on given open generic interface. - { - typeof(BaseClass), - typeof(IDictionary<,>), - typeof(IDictionary) - }, - { - typeof(BaseClass), - typeof(IEquatable<>), - typeof(IEquatable) - }, - { - typeof(BaseClass), - typeof(ICollection<>), - typeof(ICollection>) - }, - // Derived class that implements closed generic based on given open generic interface. - { - typeof(DerivedClass), - typeof(IDictionary<,>), - typeof(IDictionary) - }, - { - typeof(DerivedClass), - typeof(IEquatable<>), - typeof(IEquatable) - }, - { - typeof(DerivedClass), - typeof(ICollection<>), - typeof(ICollection>) - }, - // Derived class that also implements another interface. - { - typeof(DerivedClassWithComparable), - typeof(IDictionary<,>), - typeof(IDictionary) - }, - { - typeof(DerivedClassWithComparable), - typeof(IEquatable<>), - typeof(IEquatable) - }, - { - typeof(DerivedClassWithComparable), - typeof(ICollection<>), - typeof(ICollection>) - }, - { - typeof(DerivedClassWithComparable), - typeof(IComparable<>), - typeof(IComparable) - }, - // Derived class using system implementation. - { - typeof(DerivedClassFromSystemImplementation), - typeof(ICollection<>), - typeof(ICollection) - }, - { - typeof(DerivedClassFromSystemImplementation), - typeof(IReadOnlyList<>), - typeof(IReadOnlyList) - }, - { - typeof(DerivedClassFromSystemImplementation), - typeof(IEnumerable<>), - typeof(IEnumerable) - }, - // Not given an open generic type. - { - typeof(IEnumerable), - typeof(IEnumerable), - null - }, - { - typeof(IEnumerable), - typeof(IEnumerable), - null - }, - { - typeof(IReadOnlyList), - typeof(BaseClass), - null - }, - { - typeof(KeyValuePair<,>), - typeof(KeyValuePair), - null - }, - // Not a match. - { - typeof(IEnumerable), - typeof(IReadOnlyList<>), - null - }, - { - typeof(IList), - typeof(IReadOnlyList<>), - null - }, - { - typeof(IDictionary), - typeof(KeyValuePair<,>), - null - }, - }; - } - } - - [Theory] - [MemberData(nameof(ExtractGenericInterfaceDataSet))] - public void ExtractGenericInterface_ReturnsExpectedType( - Type queryType, - Type interfaceType, - Type expectedResult) - { - // Arrange & Act - var result = ClosedGenericMatcher.ExtractGenericInterface(queryType, interfaceType); - - // Assert - Assert.Equal(expectedResult, result); - } - - // IEnumerable is preferred because it is defined on the more-derived type. - [Fact] - public void ExtractGenericInterface_MultipleDefinitionsInherited() - { - // Arrange - var type = typeof(TwoIEnumerableImplementationsInherited); - - // Act - var result = ClosedGenericMatcher.ExtractGenericInterface(type, typeof(IEnumerable<>)); - - // Sort - Assert.Equal(typeof(IEnumerable), result); - } - - // IEnumerable is preferred because we sort by Ordinal on the full name. - [Fact] - public void ExtractGenericInterface_MultipleDefinitionsOnSameType() - { - // Arrange - var type = typeof(TwoIEnumerableImplementationsOnSameClass); - - // Act - var result = ClosedGenericMatcher.ExtractGenericInterface(type, typeof(IEnumerable<>)); - - // Sort - Assert.Equal(typeof(IEnumerable), result); - } - - private class TwoIEnumerableImplementationsOnSameClass : IEnumerable, IEnumerable - { - IEnumerator IEnumerable.GetEnumerator() - { - throw new NotImplementedException(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - throw new NotImplementedException(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - throw new NotImplementedException(); - } - } - - private class TwoIEnumerableImplementationsInherited : List, IEnumerable - { - IEnumerator IEnumerable.GetEnumerator() - { - throw new NotImplementedException(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - throw new NotImplementedException(); - } - } - - private class BaseClass : IDictionary, IEquatable - { - object IDictionary.this[string key] - { - get - { - throw new NotImplementedException(); - } - - set - { - throw new NotImplementedException(); - } - } - - int ICollection>.Count - { - get - { - throw new NotImplementedException(); - } - } - - bool ICollection>.IsReadOnly - { - get - { - throw new NotImplementedException(); - } - } - - ICollection IDictionary.Keys - { - get - { - throw new NotImplementedException(); - } - } - - ICollection IDictionary.Values - { - get - { - throw new NotImplementedException(); - } - } - - public bool Equals(BaseClass other) - { - throw new NotImplementedException(); - } - - void ICollection>.Add(KeyValuePair item) - { - throw new NotImplementedException(); - } - - void IDictionary.Add(string key, object value) - { - throw new NotImplementedException(); - } - - void ICollection>.Clear() - { - throw new NotImplementedException(); - } - - bool ICollection>.Contains(KeyValuePair item) - { - throw new NotImplementedException(); - } - - bool IDictionary.ContainsKey(string key) - { - throw new NotImplementedException(); - } - - void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) - { - throw new NotImplementedException(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - throw new NotImplementedException(); - } - - IEnumerator> IEnumerable>.GetEnumerator() - { - throw new NotImplementedException(); - } - - bool ICollection>.Remove(KeyValuePair item) - { - throw new NotImplementedException(); - } - - bool IDictionary.Remove(string key) - { - throw new NotImplementedException(); - } - - bool IDictionary.TryGetValue(string key, out object value) - { - throw new NotImplementedException(); - } - } - - private class DerivedClass : BaseClass - { - } - - private class DerivedClassWithComparable : DerivedClass, IComparable - { - public int CompareTo(DerivedClassWithComparable other) - { - throw new NotImplementedException(); - } - } - - private class DerivedClassFromSystemImplementation : Collection - { - } - } -} \ No newline at end of file From bbf0ebdd91190dc412cf38a44d445035441ece9f Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Thu, 13 Dec 2018 11:04:12 -0800 Subject: [PATCH 028/183] Remove Microsoft.Extensions.ObjectMethodExecutor.Sources \n\nCommit migrated from https://github.com/dotnet/extensions/commit/71a672e28701ea6f3cc4034ba11891f7ce38189f --- .../ObjectMethodExecutor/AwaitableInfo.cs | 127 ---- .../CoercedAwaitableInfo.cs | 55 -- .../ObjectMethodExecutor.cs | 340 ---------- .../ObjectMethodExecutorAwaitable.cs | 114 ---- .../ObjectMethodExecutorFSharpSupport.cs | 151 ----- .../Shared.Tests/ObjectMethodExecutorTest.cs | 634 ------------------ 6 files changed, 1421 deletions(-) delete mode 100644 src/Shared/ObjectMethodExecutor/AwaitableInfo.cs delete mode 100644 src/Shared/ObjectMethodExecutor/CoercedAwaitableInfo.cs delete mode 100644 src/Shared/ObjectMethodExecutor/ObjectMethodExecutor.cs delete mode 100644 src/Shared/ObjectMethodExecutor/ObjectMethodExecutorAwaitable.cs delete mode 100644 src/Shared/ObjectMethodExecutor/ObjectMethodExecutorFSharpSupport.cs delete mode 100644 src/Shared/test/Shared.Tests/ObjectMethodExecutorTest.cs diff --git a/src/Shared/ObjectMethodExecutor/AwaitableInfo.cs b/src/Shared/ObjectMethodExecutor/AwaitableInfo.cs deleted file mode 100644 index 431b83a6e561..000000000000 --- a/src/Shared/ObjectMethodExecutor/AwaitableInfo.cs +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Linq; -using System.Reflection; -using System.Runtime.CompilerServices; - -namespace Microsoft.Extensions.Internal -{ - internal struct AwaitableInfo - { - public Type AwaiterType { get; } - public PropertyInfo AwaiterIsCompletedProperty { get; } - public MethodInfo AwaiterGetResultMethod { get; } - public MethodInfo AwaiterOnCompletedMethod { get; } - public MethodInfo AwaiterUnsafeOnCompletedMethod { get; } - public Type ResultType { get; } - public MethodInfo GetAwaiterMethod { get; } - - public AwaitableInfo( - Type awaiterType, - PropertyInfo awaiterIsCompletedProperty, - MethodInfo awaiterGetResultMethod, - MethodInfo awaiterOnCompletedMethod, - MethodInfo awaiterUnsafeOnCompletedMethod, - Type resultType, - MethodInfo getAwaiterMethod) - { - AwaiterType = awaiterType; - AwaiterIsCompletedProperty = awaiterIsCompletedProperty; - AwaiterGetResultMethod = awaiterGetResultMethod; - AwaiterOnCompletedMethod = awaiterOnCompletedMethod; - AwaiterUnsafeOnCompletedMethod = awaiterUnsafeOnCompletedMethod; - ResultType = resultType; - GetAwaiterMethod = getAwaiterMethod; - } - - public static bool IsTypeAwaitable(Type type, out AwaitableInfo awaitableInfo) - { - // Based on Roslyn code: http://source.roslyn.io/#Microsoft.CodeAnalysis.Workspaces/Shared/Extensions/ISymbolExtensions.cs,db4d48ba694b9347 - - // Awaitable must have method matching "object GetAwaiter()" - var getAwaiterMethod = type.GetRuntimeMethods().FirstOrDefault(m => - m.Name.Equals("GetAwaiter", StringComparison.OrdinalIgnoreCase) - && m.GetParameters().Length == 0 - && m.ReturnType != null); - if (getAwaiterMethod == null) - { - awaitableInfo = default(AwaitableInfo); - return false; - } - - var awaiterType = getAwaiterMethod.ReturnType; - - // Awaiter must have property matching "bool IsCompleted { get; }" - var isCompletedProperty = awaiterType.GetRuntimeProperties().FirstOrDefault(p => - p.Name.Equals("IsCompleted", StringComparison.OrdinalIgnoreCase) - && p.PropertyType == typeof(bool) - && p.GetMethod != null); - if (isCompletedProperty == null) - { - awaitableInfo = default(AwaitableInfo); - return false; - } - - // Awaiter must implement INotifyCompletion - var awaiterInterfaces = awaiterType.GetInterfaces(); - var implementsINotifyCompletion = awaiterInterfaces.Any(t => t == typeof(INotifyCompletion)); - if (!implementsINotifyCompletion) - { - awaitableInfo = default(AwaitableInfo); - return false; - } - - // INotifyCompletion supplies a method matching "void OnCompleted(Action action)" - var iNotifyCompletionMap = awaiterType - .GetTypeInfo() - .GetRuntimeInterfaceMap(typeof(INotifyCompletion)); - var onCompletedMethod = iNotifyCompletionMap.InterfaceMethods.Single(m => - m.Name.Equals("OnCompleted", StringComparison.OrdinalIgnoreCase) - && m.ReturnType == typeof(void) - && m.GetParameters().Length == 1 - && m.GetParameters()[0].ParameterType == typeof(Action)); - - // Awaiter optionally implements ICriticalNotifyCompletion - var implementsICriticalNotifyCompletion = awaiterInterfaces.Any(t => t == typeof(ICriticalNotifyCompletion)); - MethodInfo unsafeOnCompletedMethod; - if (implementsICriticalNotifyCompletion) - { - // ICriticalNotifyCompletion supplies a method matching "void UnsafeOnCompleted(Action action)" - var iCriticalNotifyCompletionMap = awaiterType - .GetTypeInfo() - .GetRuntimeInterfaceMap(typeof(ICriticalNotifyCompletion)); - unsafeOnCompletedMethod = iCriticalNotifyCompletionMap.InterfaceMethods.Single(m => - m.Name.Equals("UnsafeOnCompleted", StringComparison.OrdinalIgnoreCase) - && m.ReturnType == typeof(void) - && m.GetParameters().Length == 1 - && m.GetParameters()[0].ParameterType == typeof(Action)); - } - else - { - unsafeOnCompletedMethod = null; - } - - // Awaiter must have method matching "void GetResult" or "T GetResult()" - var getResultMethod = awaiterType.GetRuntimeMethods().FirstOrDefault(m => - m.Name.Equals("GetResult") - && m.GetParameters().Length == 0); - if (getResultMethod == null) - { - awaitableInfo = default(AwaitableInfo); - return false; - } - - awaitableInfo = new AwaitableInfo( - awaiterType, - isCompletedProperty, - getResultMethod, - onCompletedMethod, - unsafeOnCompletedMethod, - getResultMethod.ReturnType, - getAwaiterMethod); - return true; - } - } -} diff --git a/src/Shared/ObjectMethodExecutor/CoercedAwaitableInfo.cs b/src/Shared/ObjectMethodExecutor/CoercedAwaitableInfo.cs deleted file mode 100644 index 4e48ef09a15e..000000000000 --- a/src/Shared/ObjectMethodExecutor/CoercedAwaitableInfo.cs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Linq.Expressions; - -namespace Microsoft.Extensions.Internal -{ - internal struct CoercedAwaitableInfo - { - public AwaitableInfo AwaitableInfo { get; } - public Expression CoercerExpression { get; } - public Type CoercerResultType { get; } - public bool RequiresCoercion => CoercerExpression != null; - - public CoercedAwaitableInfo(AwaitableInfo awaitableInfo) - { - AwaitableInfo = awaitableInfo; - CoercerExpression = null; - CoercerResultType = null; - } - - public CoercedAwaitableInfo(Expression coercerExpression, Type coercerResultType, AwaitableInfo coercedAwaitableInfo) - { - CoercerExpression = coercerExpression; - CoercerResultType = coercerResultType; - AwaitableInfo = coercedAwaitableInfo; - } - - public static bool IsTypeAwaitable(Type type, out CoercedAwaitableInfo info) - { - if (AwaitableInfo.IsTypeAwaitable(type, out var directlyAwaitableInfo)) - { - info = new CoercedAwaitableInfo(directlyAwaitableInfo); - return true; - } - - // It's not directly awaitable, but maybe we can coerce it. - // Currently we support coercing FSharpAsync. - if (ObjectMethodExecutorFSharpSupport.TryBuildCoercerFromFSharpAsyncToAwaitable(type, - out var coercerExpression, - out var coercerResultType)) - { - if (AwaitableInfo.IsTypeAwaitable(coercerResultType, out var coercedAwaitableInfo)) - { - info = new CoercedAwaitableInfo(coercerExpression, coercerResultType, coercedAwaitableInfo); - return true; - } - } - - info = default(CoercedAwaitableInfo); - return false; - } - } -} diff --git a/src/Shared/ObjectMethodExecutor/ObjectMethodExecutor.cs b/src/Shared/ObjectMethodExecutor/ObjectMethodExecutor.cs deleted file mode 100644 index f8e5b70f0ddd..000000000000 --- a/src/Shared/ObjectMethodExecutor/ObjectMethodExecutor.cs +++ /dev/null @@ -1,340 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Linq.Expressions; -using System.Reflection; - -namespace Microsoft.Extensions.Internal -{ - internal class ObjectMethodExecutor - { - private readonly object[] _parameterDefaultValues; - private readonly MethodExecutorAsync _executorAsync; - private readonly MethodExecutor _executor; - - private static readonly ConstructorInfo _objectMethodExecutorAwaitableConstructor = - typeof(ObjectMethodExecutorAwaitable).GetConstructor(new[] { - typeof(object), // customAwaitable - typeof(Func), // getAwaiterMethod - typeof(Func), // isCompletedMethod - typeof(Func), // getResultMethod - typeof(Action), // onCompletedMethod - typeof(Action) // unsafeOnCompletedMethod - }); - - private ObjectMethodExecutor(MethodInfo methodInfo, TypeInfo targetTypeInfo, object[] parameterDefaultValues) - { - if (methodInfo == null) - { - throw new ArgumentNullException(nameof(methodInfo)); - } - - MethodInfo = methodInfo; - MethodParameters = methodInfo.GetParameters(); - TargetTypeInfo = targetTypeInfo; - MethodReturnType = methodInfo.ReturnType; - - var isAwaitable = CoercedAwaitableInfo.IsTypeAwaitable(MethodReturnType, out var coercedAwaitableInfo); - - IsMethodAsync = isAwaitable; - AsyncResultType = isAwaitable ? coercedAwaitableInfo.AwaitableInfo.ResultType : null; - - // Upstream code may prefer to use the sync-executor even for async methods, because if it knows - // that the result is a specific Task where T is known, then it can directly cast to that type - // and await it without the extra heap allocations involved in the _executorAsync code path. - _executor = GetExecutor(methodInfo, targetTypeInfo); - - if (IsMethodAsync) - { - _executorAsync = GetExecutorAsync(methodInfo, targetTypeInfo, coercedAwaitableInfo); - } - - _parameterDefaultValues = parameterDefaultValues; - } - - private delegate ObjectMethodExecutorAwaitable MethodExecutorAsync(object target, object[] parameters); - - private delegate object MethodExecutor(object target, object[] parameters); - - private delegate void VoidMethodExecutor(object target, object[] parameters); - - public MethodInfo MethodInfo { get; } - - public ParameterInfo[] MethodParameters { get; } - - public TypeInfo TargetTypeInfo { get; } - - public Type AsyncResultType { get; } - - // This field is made internal set because it is set in unit tests. - public Type MethodReturnType { get; internal set; } - - public bool IsMethodAsync { get; } - - public static ObjectMethodExecutor Create(MethodInfo methodInfo, TypeInfo targetTypeInfo) - { - return new ObjectMethodExecutor(methodInfo, targetTypeInfo, null); - } - - public static ObjectMethodExecutor Create(MethodInfo methodInfo, TypeInfo targetTypeInfo, object[] parameterDefaultValues) - { - if (parameterDefaultValues == null) - { - throw new ArgumentNullException(nameof(parameterDefaultValues)); - } - - return new ObjectMethodExecutor(methodInfo, targetTypeInfo, parameterDefaultValues); - } - - /// - /// Executes the configured method on . This can be used whether or not - /// the configured method is asynchronous. - /// - /// - /// Even if the target method is asynchronous, it's desirable to invoke it using Execute rather than - /// ExecuteAsync if you know at compile time what the return type is, because then you can directly - /// "await" that value (via a cast), and then the generated code will be able to reference the - /// resulting awaitable as a value-typed variable. If you use ExecuteAsync instead, the generated - /// code will have to treat the resulting awaitable as a boxed object, because it doesn't know at - /// compile time what type it would be. - /// - /// The object whose method is to be executed. - /// Parameters to pass to the method. - /// The method return value. - public object Execute(object target, object[] parameters) - { - return _executor(target, parameters); - } - - /// - /// Executes the configured method on . This can only be used if the configured - /// method is asynchronous. - /// - /// - /// If you don't know at compile time the type of the method's returned awaitable, you can use ExecuteAsync, - /// which supplies an awaitable-of-object. This always works, but can incur several extra heap allocations - /// as compared with using Execute and then using "await" on the result value typecasted to the known - /// awaitable type. The possible extra heap allocations are for: - /// - /// 1. The custom awaitable (though usually there's a heap allocation for this anyway, since normally - /// it's a reference type, and you normally create a new instance per call). - /// 2. The custom awaiter (whether or not it's a value type, since if it's not, you need a new instance - /// of it, and if it is, it will have to be boxed so the calling code can reference it as an object). - /// 3. The async result value, if it's a value type (it has to be boxed as an object, since the calling - /// code doesn't know what type it's going to be). - /// - /// The object whose method is to be executed. - /// Parameters to pass to the method. - /// An object that you can "await" to get the method return value. - public ObjectMethodExecutorAwaitable ExecuteAsync(object target, object[] parameters) - { - return _executorAsync(target, parameters); - } - - public object GetDefaultValueForParameter(int index) - { - if (_parameterDefaultValues == null) - { - throw new InvalidOperationException($"Cannot call {nameof(GetDefaultValueForParameter)}, because no parameter default values were supplied."); - } - - if (index < 0 || index > MethodParameters.Length - 1) - { - throw new ArgumentOutOfRangeException(nameof(index)); - } - - return _parameterDefaultValues[index]; - } - - private static MethodExecutor GetExecutor(MethodInfo methodInfo, TypeInfo targetTypeInfo) - { - // Parameters to executor - var targetParameter = Expression.Parameter(typeof(object), "target"); - var parametersParameter = Expression.Parameter(typeof(object[]), "parameters"); - - // Build parameter list - var parameters = new List(); - var paramInfos = methodInfo.GetParameters(); - for (int i = 0; i < paramInfos.Length; i++) - { - var paramInfo = paramInfos[i]; - var valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i)); - var valueCast = Expression.Convert(valueObj, paramInfo.ParameterType); - - // valueCast is "(Ti) parameters[i]" - parameters.Add(valueCast); - } - - // Call method - var instanceCast = Expression.Convert(targetParameter, targetTypeInfo.AsType()); - var methodCall = Expression.Call(instanceCast, methodInfo, parameters); - - // methodCall is "((Ttarget) target) method((T0) parameters[0], (T1) parameters[1], ...)" - // Create function - if (methodCall.Type == typeof(void)) - { - var lambda = Expression.Lambda(methodCall, targetParameter, parametersParameter); - var voidExecutor = lambda.Compile(); - return WrapVoidMethod(voidExecutor); - } - else - { - // must coerce methodCall to match ActionExecutor signature - var castMethodCall = Expression.Convert(methodCall, typeof(object)); - var lambda = Expression.Lambda(castMethodCall, targetParameter, parametersParameter); - return lambda.Compile(); - } - } - - private static MethodExecutor WrapVoidMethod(VoidMethodExecutor executor) - { - return delegate (object target, object[] parameters) - { - executor(target, parameters); - return null; - }; - } - - private static MethodExecutorAsync GetExecutorAsync( - MethodInfo methodInfo, - TypeInfo targetTypeInfo, - CoercedAwaitableInfo coercedAwaitableInfo) - { - // Parameters to executor - var targetParameter = Expression.Parameter(typeof(object), "target"); - var parametersParameter = Expression.Parameter(typeof(object[]), "parameters"); - - // Build parameter list - var parameters = new List(); - var paramInfos = methodInfo.GetParameters(); - for (int i = 0; i < paramInfos.Length; i++) - { - var paramInfo = paramInfos[i]; - var valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i)); - var valueCast = Expression.Convert(valueObj, paramInfo.ParameterType); - - // valueCast is "(Ti) parameters[i]" - parameters.Add(valueCast); - } - - // Call method - var instanceCast = Expression.Convert(targetParameter, targetTypeInfo.AsType()); - var methodCall = Expression.Call(instanceCast, methodInfo, parameters); - - // Using the method return value, construct an ObjectMethodExecutorAwaitable based on - // the info we have about its implementation of the awaitable pattern. Note that all - // the funcs/actions we construct here are precompiled, so that only one instance of - // each is preserved throughout the lifetime of the ObjectMethodExecutor. - - // var getAwaiterFunc = (object awaitable) => - // (object)((CustomAwaitableType)awaitable).GetAwaiter(); - var customAwaitableParam = Expression.Parameter(typeof(object), "awaitable"); - var awaitableInfo = coercedAwaitableInfo.AwaitableInfo; - var postCoercionMethodReturnType = coercedAwaitableInfo.CoercerResultType ?? methodInfo.ReturnType; - var getAwaiterFunc = Expression.Lambda>( - Expression.Convert( - Expression.Call( - Expression.Convert(customAwaitableParam, postCoercionMethodReturnType), - awaitableInfo.GetAwaiterMethod), - typeof(object)), - customAwaitableParam).Compile(); - - // var isCompletedFunc = (object awaiter) => - // ((CustomAwaiterType)awaiter).IsCompleted; - var isCompletedParam = Expression.Parameter(typeof(object), "awaiter"); - var isCompletedFunc = Expression.Lambda>( - Expression.MakeMemberAccess( - Expression.Convert(isCompletedParam, awaitableInfo.AwaiterType), - awaitableInfo.AwaiterIsCompletedProperty), - isCompletedParam).Compile(); - - var getResultParam = Expression.Parameter(typeof(object), "awaiter"); - Func getResultFunc; - if (awaitableInfo.ResultType == typeof(void)) - { - // var getResultFunc = (object awaiter) => - // { - // ((CustomAwaiterType)awaiter).GetResult(); // We need to invoke this to surface any exceptions - // return (object)null; - // }; - getResultFunc = Expression.Lambda>( - Expression.Block( - Expression.Call( - Expression.Convert(getResultParam, awaitableInfo.AwaiterType), - awaitableInfo.AwaiterGetResultMethod), - Expression.Constant(null) - ), - getResultParam).Compile(); - } - else - { - // var getResultFunc = (object awaiter) => - // (object)((CustomAwaiterType)awaiter).GetResult(); - getResultFunc = Expression.Lambda>( - Expression.Convert( - Expression.Call( - Expression.Convert(getResultParam, awaitableInfo.AwaiterType), - awaitableInfo.AwaiterGetResultMethod), - typeof(object)), - getResultParam).Compile(); - } - - // var onCompletedFunc = (object awaiter, Action continuation) => { - // ((CustomAwaiterType)awaiter).OnCompleted(continuation); - // }; - var onCompletedParam1 = Expression.Parameter(typeof(object), "awaiter"); - var onCompletedParam2 = Expression.Parameter(typeof(Action), "continuation"); - var onCompletedFunc = Expression.Lambda>( - Expression.Call( - Expression.Convert(onCompletedParam1, awaitableInfo.AwaiterType), - awaitableInfo.AwaiterOnCompletedMethod, - onCompletedParam2), - onCompletedParam1, - onCompletedParam2).Compile(); - - Action unsafeOnCompletedFunc = null; - if (awaitableInfo.AwaiterUnsafeOnCompletedMethod != null) - { - // var unsafeOnCompletedFunc = (object awaiter, Action continuation) => { - // ((CustomAwaiterType)awaiter).UnsafeOnCompleted(continuation); - // }; - var unsafeOnCompletedParam1 = Expression.Parameter(typeof(object), "awaiter"); - var unsafeOnCompletedParam2 = Expression.Parameter(typeof(Action), "continuation"); - unsafeOnCompletedFunc = Expression.Lambda>( - Expression.Call( - Expression.Convert(unsafeOnCompletedParam1, awaitableInfo.AwaiterType), - awaitableInfo.AwaiterUnsafeOnCompletedMethod, - unsafeOnCompletedParam2), - unsafeOnCompletedParam1, - unsafeOnCompletedParam2).Compile(); - } - - // If we need to pass the method call result through a coercer function to get an - // awaitable, then do so. - var coercedMethodCall = coercedAwaitableInfo.RequiresCoercion - ? Expression.Invoke(coercedAwaitableInfo.CoercerExpression, methodCall) - : (Expression)methodCall; - - // return new ObjectMethodExecutorAwaitable( - // (object)coercedMethodCall, - // getAwaiterFunc, - // isCompletedFunc, - // getResultFunc, - // onCompletedFunc, - // unsafeOnCompletedFunc); - var returnValueExpression = Expression.New( - _objectMethodExecutorAwaitableConstructor, - Expression.Convert(coercedMethodCall, typeof(object)), - Expression.Constant(getAwaiterFunc), - Expression.Constant(isCompletedFunc), - Expression.Constant(getResultFunc), - Expression.Constant(onCompletedFunc), - Expression.Constant(unsafeOnCompletedFunc, typeof(Action))); - - var lambda = Expression.Lambda(returnValueExpression, targetParameter, parametersParameter); - return lambda.Compile(); - } - } -} diff --git a/src/Shared/ObjectMethodExecutor/ObjectMethodExecutorAwaitable.cs b/src/Shared/ObjectMethodExecutor/ObjectMethodExecutorAwaitable.cs deleted file mode 100644 index 7509b86b2ba5..000000000000 --- a/src/Shared/ObjectMethodExecutor/ObjectMethodExecutorAwaitable.cs +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Runtime.CompilerServices; - -namespace Microsoft.Extensions.Internal -{ - /// - /// Provides a common awaitable structure that can - /// return, regardless of whether the underlying value is a System.Task, an FSharpAsync, or an - /// application-defined custom awaitable. - /// - internal struct ObjectMethodExecutorAwaitable - { - private readonly object _customAwaitable; - private readonly Func _getAwaiterMethod; - private readonly Func _isCompletedMethod; - private readonly Func _getResultMethod; - private readonly Action _onCompletedMethod; - private readonly Action _unsafeOnCompletedMethod; - - // Perf note: since we're requiring the customAwaitable to be supplied here as an object, - // this will trigger a further allocation if it was a value type (i.e., to box it). We can't - // fix this by making the customAwaitable type generic, because the calling code typically - // does not know the type of the awaitable/awaiter at compile-time anyway. - // - // However, we could fix it by not passing the customAwaitable here at all, and instead - // passing a func that maps directly from the target object (e.g., controller instance), - // target method (e.g., action method info), and params array to the custom awaiter in the - // GetAwaiter() method below. In effect, by delaying the actual method call until the - // upstream code calls GetAwaiter on this ObjectMethodExecutorAwaitable instance. - // This optimization is not currently implemented because: - // [1] It would make no difference when the awaitable was an object type, which is - // by far the most common scenario (e.g., System.Task). - // [2] It would be complex - we'd need some kind of object pool to track all the parameter - // arrays until we needed to use them in GetAwaiter(). - // We can reconsider this in the future if there's a need to optimize for ValueTask - // or other value-typed awaitables. - - public ObjectMethodExecutorAwaitable( - object customAwaitable, - Func getAwaiterMethod, - Func isCompletedMethod, - Func getResultMethod, - Action onCompletedMethod, - Action unsafeOnCompletedMethod) - { - _customAwaitable = customAwaitable; - _getAwaiterMethod = getAwaiterMethod; - _isCompletedMethod = isCompletedMethod; - _getResultMethod = getResultMethod; - _onCompletedMethod = onCompletedMethod; - _unsafeOnCompletedMethod = unsafeOnCompletedMethod; - } - - public Awaiter GetAwaiter() - { - var customAwaiter = _getAwaiterMethod(_customAwaitable); - return new Awaiter(customAwaiter, _isCompletedMethod, _getResultMethod, _onCompletedMethod, _unsafeOnCompletedMethod); - } - - public struct Awaiter : ICriticalNotifyCompletion - { - private readonly object _customAwaiter; - private readonly Func _isCompletedMethod; - private readonly Func _getResultMethod; - private readonly Action _onCompletedMethod; - private readonly Action _unsafeOnCompletedMethod; - - public Awaiter( - object customAwaiter, - Func isCompletedMethod, - Func getResultMethod, - Action onCompletedMethod, - Action unsafeOnCompletedMethod) - { - _customAwaiter = customAwaiter; - _isCompletedMethod = isCompletedMethod; - _getResultMethod = getResultMethod; - _onCompletedMethod = onCompletedMethod; - _unsafeOnCompletedMethod = unsafeOnCompletedMethod; - } - - public bool IsCompleted => _isCompletedMethod(_customAwaiter); - - public object GetResult() => _getResultMethod(_customAwaiter); - - public void OnCompleted(Action continuation) - { - _onCompletedMethod(_customAwaiter, continuation); - } - - public void UnsafeOnCompleted(Action continuation) - { - // If the underlying awaitable implements ICriticalNotifyCompletion, use its UnsafeOnCompleted. - // If not, fall back on using its OnCompleted. - // - // Why this is safe: - // - Implementing ICriticalNotifyCompletion is a way of saying the caller can choose whether it - // needs the execution context to be preserved (which it signals by calling OnCompleted), or - // that it doesn't (which it signals by calling UnsafeOnCompleted). Obviously it's faster *not* - // to preserve and restore the context, so we prefer that where possible. - // - If a caller doesn't need the execution context to be preserved and hence calls UnsafeOnCompleted, - // there's no harm in preserving it anyway - it's just a bit of wasted cost. That's what will happen - // if a caller sees that the proxy implements ICriticalNotifyCompletion but the proxy chooses to - // pass the call on to the underlying awaitable's OnCompleted method. - - var underlyingMethodToUse = _unsafeOnCompletedMethod ?? _onCompletedMethod; - underlyingMethodToUse(_customAwaiter, continuation); - } - } - } -} \ No newline at end of file diff --git a/src/Shared/ObjectMethodExecutor/ObjectMethodExecutorFSharpSupport.cs b/src/Shared/ObjectMethodExecutor/ObjectMethodExecutorFSharpSupport.cs deleted file mode 100644 index 2198c0ce4506..000000000000 --- a/src/Shared/ObjectMethodExecutor/ObjectMethodExecutorFSharpSupport.cs +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; -using System.Threading; -using System.Threading.Tasks; - -namespace Microsoft.Extensions.Internal -{ - /// - /// Helper for detecting whether a given type is FSharpAsync`1, and if so, supplying - /// an for mapping instances of that type to a C# awaitable. - /// - /// - /// The main design goal here is to avoid taking a compile-time dependency on - /// FSharp.Core.dll, because non-F# applications wouldn't use it. So all the references - /// to FSharp types have to be constructed dynamically at runtime. - /// - internal static class ObjectMethodExecutorFSharpSupport - { - private static object _fsharpValuesCacheLock = new object(); - private static Assembly _fsharpCoreAssembly; - private static MethodInfo _fsharpAsyncStartAsTaskGenericMethod; - private static PropertyInfo _fsharpOptionOfTaskCreationOptionsNoneProperty; - private static PropertyInfo _fsharpOptionOfCancellationTokenNoneProperty; - - public static bool TryBuildCoercerFromFSharpAsyncToAwaitable( - Type possibleFSharpAsyncType, - out Expression coerceToAwaitableExpression, - out Type awaitableType) - { - var methodReturnGenericType = possibleFSharpAsyncType.IsGenericType - ? possibleFSharpAsyncType.GetGenericTypeDefinition() - : null; - - if (!IsFSharpAsyncOpenGenericType(methodReturnGenericType)) - { - coerceToAwaitableExpression = null; - awaitableType = null; - return false; - } - - var awaiterResultType = possibleFSharpAsyncType.GetGenericArguments().Single(); - awaitableType = typeof(Task<>).MakeGenericType(awaiterResultType); - - // coerceToAwaitableExpression = (object fsharpAsync) => - // { - // return (object)FSharpAsync.StartAsTask( - // (Microsoft.FSharp.Control.FSharpAsync)fsharpAsync, - // FSharpOption.None, - // FSharpOption.None); - // }; - var startAsTaskClosedMethod = _fsharpAsyncStartAsTaskGenericMethod - .MakeGenericMethod(awaiterResultType); - var coerceToAwaitableParam = Expression.Parameter(typeof(object)); - coerceToAwaitableExpression = Expression.Lambda( - Expression.Convert( - Expression.Call( - startAsTaskClosedMethod, - Expression.Convert(coerceToAwaitableParam, possibleFSharpAsyncType), - Expression.MakeMemberAccess(null, _fsharpOptionOfTaskCreationOptionsNoneProperty), - Expression.MakeMemberAccess(null, _fsharpOptionOfCancellationTokenNoneProperty)), - typeof(object)), - coerceToAwaitableParam); - - return true; - } - - private static bool IsFSharpAsyncOpenGenericType(Type possibleFSharpAsyncGenericType) - { - var typeFullName = possibleFSharpAsyncGenericType?.FullName; - if (!string.Equals(typeFullName, "Microsoft.FSharp.Control.FSharpAsync`1", StringComparison.Ordinal)) - { - return false; - } - - lock (_fsharpValuesCacheLock) - { - if (_fsharpCoreAssembly != null) - { - // Since we've already found the real FSharpAsync.Core assembly, we just have - // to check that the supplied FSharpAsync`1 type is the one from that assembly. - return possibleFSharpAsyncGenericType.Assembly == _fsharpCoreAssembly; - } - else - { - // We'll keep trying to find the FSharp types/values each time any type called - // FSharpAsync`1 is supplied. - return TryPopulateFSharpValueCaches(possibleFSharpAsyncGenericType); - } - } - } - - private static bool TryPopulateFSharpValueCaches(Type possibleFSharpAsyncGenericType) - { - var assembly = possibleFSharpAsyncGenericType.Assembly; - var fsharpOptionType = assembly.GetType("Microsoft.FSharp.Core.FSharpOption`1"); - var fsharpAsyncType = assembly.GetType("Microsoft.FSharp.Control.FSharpAsync"); - - if (fsharpOptionType == null || fsharpAsyncType == null) - { - return false; - } - - // Get a reference to FSharpOption.None - var fsharpOptionOfTaskCreationOptionsType = fsharpOptionType - .MakeGenericType(typeof(TaskCreationOptions)); - _fsharpOptionOfTaskCreationOptionsNoneProperty = fsharpOptionOfTaskCreationOptionsType - .GetTypeInfo() - .GetRuntimeProperty("None"); - - // Get a reference to FSharpOption.None - var fsharpOptionOfCancellationTokenType = fsharpOptionType - .MakeGenericType(typeof(CancellationToken)); - _fsharpOptionOfCancellationTokenNoneProperty = fsharpOptionOfCancellationTokenType - .GetTypeInfo() - .GetRuntimeProperty("None"); - - // Get a reference to FSharpAsync.StartAsTask<> - var fsharpAsyncMethods = fsharpAsyncType - .GetRuntimeMethods() - .Where(m => m.Name.Equals("StartAsTask", StringComparison.Ordinal)); - foreach (var candidateMethodInfo in fsharpAsyncMethods) - { - var parameters = candidateMethodInfo.GetParameters(); - if (parameters.Length == 3 - && TypesHaveSameIdentity(parameters[0].ParameterType, possibleFSharpAsyncGenericType) - && parameters[1].ParameterType == fsharpOptionOfTaskCreationOptionsType - && parameters[2].ParameterType == fsharpOptionOfCancellationTokenType) - { - // This really does look like the correct method (and hence assembly). - _fsharpAsyncStartAsTaskGenericMethod = candidateMethodInfo; - _fsharpCoreAssembly = assembly; - break; - } - } - - return _fsharpCoreAssembly != null; - } - - private static bool TypesHaveSameIdentity(Type type1, Type type2) - { - return type1.Assembly == type2.Assembly - && string.Equals(type1.Namespace, type2.Namespace, StringComparison.Ordinal) - && string.Equals(type1.Name, type2.Name, StringComparison.Ordinal); - } - } -} diff --git a/src/Shared/test/Shared.Tests/ObjectMethodExecutorTest.cs b/src/Shared/test/Shared.Tests/ObjectMethodExecutorTest.cs deleted file mode 100644 index 1c26ef1de157..000000000000 --- a/src/Shared/test/Shared.Tests/ObjectMethodExecutorTest.cs +++ /dev/null @@ -1,634 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using Microsoft.FSharp.Control; -using Microsoft.FSharp.Core; -using System; -using System.Collections.Generic; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Threading; -using System.Threading.Tasks; -using Xunit; - -namespace Microsoft.Extensions.Internal -{ - public class ObjectMethodExecutorTest - { - private TestObject _targetObject = new TestObject(); - private TypeInfo targetTypeInfo = typeof(TestObject).GetTypeInfo(); - - [Fact] - public void ExecuteValueMethod() - { - var executor = GetExecutorForMethod("ValueMethod"); - var result = executor.Execute( - _targetObject, - new object[] { 10, 20 }); - Assert.False(executor.IsMethodAsync); - Assert.Equal(30, (int)result); - } - - [Fact] - public void ExecuteVoidValueMethod() - { - var executor = GetExecutorForMethod("VoidValueMethod"); - var result = executor.Execute( - _targetObject, - new object[] { 10 }); - Assert.False(executor.IsMethodAsync); - Assert.Null(result); - } - - [Fact] - public void ExecuteValueMethodWithReturnType() - { - var executor = GetExecutorForMethod("ValueMethodWithReturnType"); - var result = executor.Execute( - _targetObject, - new object[] { 10 }); - var resultObject = Assert.IsType(result); - Assert.False(executor.IsMethodAsync); - Assert.Equal("Hello", resultObject.value); - } - - [Fact] - public void ExecuteValueMethodUpdateValue() - { - var executor = GetExecutorForMethod("ValueMethodUpdateValue"); - var parameter = new TestObject(); - var result = executor.Execute( - _targetObject, - new object[] { parameter }); - var resultObject = Assert.IsType(result); - Assert.False(executor.IsMethodAsync); - Assert.Equal("HelloWorld", resultObject.value); - } - - [Fact] - public void ExecuteValueMethodWithReturnTypeThrowsException() - { - var executor = GetExecutorForMethod("ValueMethodWithReturnTypeThrowsException"); - var parameter = new TestObject(); - Assert.False(executor.IsMethodAsync); - Assert.Throws( - () => executor.Execute( - _targetObject, - new object[] { parameter })); - } - - [Fact] - public async Task ExecuteValueMethodAsync() - { - var executor = GetExecutorForMethod("ValueMethodAsync"); - var result = await executor.ExecuteAsync( - _targetObject, - new object[] { 10, 20 }); - Assert.True(executor.IsMethodAsync); - Assert.Equal(30, (int)result); - } - - [Fact] - public async Task ExecuteValueMethodWithReturnTypeAsync() - { - var executor = GetExecutorForMethod("ValueMethodWithReturnTypeAsync"); - var result = await executor.ExecuteAsync( - _targetObject, - new object[] { 10 }); - var resultObject = Assert.IsType(result); - Assert.True(executor.IsMethodAsync); - Assert.Equal("Hello", resultObject.value); - } - - [Fact] - public async Task ExecuteValueMethodUpdateValueAsync() - { - var executor = GetExecutorForMethod("ValueMethodUpdateValueAsync"); - var parameter = new TestObject(); - var result = await executor.ExecuteAsync( - _targetObject, - new object[] { parameter }); - var resultObject = Assert.IsType(result); - Assert.True(executor.IsMethodAsync); - Assert.Equal("HelloWorld", resultObject.value); - } - - [Fact] - public async Task ExecuteValueMethodWithReturnTypeThrowsExceptionAsync() - { - var executor = GetExecutorForMethod("ValueMethodWithReturnTypeThrowsExceptionAsync"); - var parameter = new TestObject(); - Assert.True(executor.IsMethodAsync); - await Assert.ThrowsAsync( - async () => await executor.ExecuteAsync( - _targetObject, - new object[] { parameter })); - } - - [Fact] - public async Task ExecuteValueMethodWithReturnVoidThrowsExceptionAsync() - { - var executor = GetExecutorForMethod("ValueMethodWithReturnVoidThrowsExceptionAsync"); - var parameter = new TestObject(); - Assert.True(executor.IsMethodAsync); - await Assert.ThrowsAsync( - async () => await executor.ExecuteAsync( - _targetObject, - new object[] { parameter })); - } - - [Fact] - public void GetDefaultValueForParameters_ReturnsSuppliedValues() - { - var suppliedDefaultValues = new object[] { 123, "test value" }; - var executor = GetExecutorForMethod("MethodWithMultipleParameters", suppliedDefaultValues); - Assert.Equal(suppliedDefaultValues[0], executor.GetDefaultValueForParameter(0)); - Assert.Equal(suppliedDefaultValues[1], executor.GetDefaultValueForParameter(1)); - Assert.Throws(() => executor.GetDefaultValueForParameter(2)); - } - - [Fact] - public void GetDefaultValueForParameters_ThrowsIfNoneWereSupplied() - { - var executor = GetExecutorForMethod("MethodWithMultipleParameters"); - Assert.Throws(() => executor.GetDefaultValueForParameter(0)); - } - - [Fact] - public async void TargetMethodReturningCustomAwaitableOfReferenceType_CanInvokeViaExecute() - { - // Arrange - var executor = GetExecutorForMethod("CustomAwaitableOfReferenceTypeAsync"); - - // Act - var result = await (TestAwaitable)executor.Execute(_targetObject, new object[] { "Hello", 123 }); - - // Assert - Assert.True(executor.IsMethodAsync); - Assert.Same(typeof(TestObject), executor.AsyncResultType); - Assert.NotNull(result); - Assert.Equal("Hello 123", result.value); - } - - [Fact] - public async void TargetMethodReturningCustomAwaitableOfValueType_CanInvokeViaExecute() - { - // Arrange - var executor = GetExecutorForMethod("CustomAwaitableOfValueTypeAsync"); - - // Act - var result = await (TestAwaitable)executor.Execute(_targetObject, new object[] { 123, 456 }); - - // Assert - Assert.True(executor.IsMethodAsync); - Assert.Same(typeof(int), executor.AsyncResultType); - Assert.Equal(579, result); - } - - [Fact] - public async void TargetMethodReturningCustomAwaitableOfReferenceType_CanInvokeViaExecuteAsync() - { - // Arrange - var executor = GetExecutorForMethod("CustomAwaitableOfReferenceTypeAsync"); - - // Act - var result = await executor.ExecuteAsync(_targetObject, new object[] { "Hello", 123 }); - - // Assert - Assert.True(executor.IsMethodAsync); - Assert.Same(typeof(TestObject), executor.AsyncResultType); - Assert.NotNull(result); - Assert.IsType(result); - Assert.Equal("Hello 123", ((TestObject)result).value); - } - - [Fact] - public async void TargetMethodReturningCustomAwaitableOfValueType_CanInvokeViaExecuteAsync() - { - // Arrange - var executor = GetExecutorForMethod("CustomAwaitableOfValueTypeAsync"); - - // Act - var result = await executor.ExecuteAsync(_targetObject, new object[] { 123, 456 }); - - // Assert - Assert.True(executor.IsMethodAsync); - Assert.Same(typeof(int), executor.AsyncResultType); - Assert.NotNull(result); - Assert.IsType(result); - Assert.Equal(579, (int)result); - } - - [Fact] - public async void TargetMethodReturningAwaitableOfVoidType_CanInvokeViaExecuteAsync() - { - // Arrange - var executor = GetExecutorForMethod("VoidValueMethodAsync"); - - // Act - var result = await executor.ExecuteAsync(_targetObject, new object[] { 123 }); - - // Assert - Assert.True(executor.IsMethodAsync); - Assert.Same(typeof(void), executor.AsyncResultType); - Assert.Null(result); - } - - [Fact] - public async void TargetMethodReturningAwaitableWithICriticalNotifyCompletion_UsesUnsafeOnCompleted() - { - // Arrange - var executor = GetExecutorForMethod("CustomAwaitableWithICriticalNotifyCompletion"); - - // Act - var result = await executor.ExecuteAsync(_targetObject, new object[0]); - - // Assert - Assert.True(executor.IsMethodAsync); - Assert.Same(typeof(string), executor.AsyncResultType); - Assert.Equal("Used UnsafeOnCompleted", (string)result); - } - - [Fact] - public async void TargetMethodReturningAwaitableWithoutICriticalNotifyCompletion_UsesOnCompleted() - { - // Arrange - var executor = GetExecutorForMethod("CustomAwaitableWithoutICriticalNotifyCompletion"); - - // Act - var result = await executor.ExecuteAsync(_targetObject, new object[0]); - - // Assert - Assert.True(executor.IsMethodAsync); - Assert.Same(typeof(string), executor.AsyncResultType); - Assert.Equal("Used OnCompleted", (string)result); - } - - [Fact] - public async void TargetMethodReturningValueTaskOfValueType_CanBeInvokedViaExecute() - { - // Arrange - var executor = GetExecutorForMethod("ValueTaskOfValueType"); - - // Act - var result = await (ValueTask)executor.Execute(_targetObject, new object[] { 123 }); - - // Assert - Assert.True(executor.IsMethodAsync); - Assert.Same(typeof(int), executor.AsyncResultType); - Assert.Equal(123, result); - } - - [Fact] - public async void TargetMethodReturningValueTaskOfReferenceType_CanBeInvokedViaExecute() - { - // Arrange - var executor = GetExecutorForMethod("ValueTaskOfReferenceType"); - - // Act - var result = await (ValueTask)executor.Execute(_targetObject, new object[] { "test result" }); - - // Assert - Assert.True(executor.IsMethodAsync); - Assert.Same(typeof(string), executor.AsyncResultType); - Assert.Equal("test result", result); - } - - [Fact] - public async void TargetMethodReturningValueTaskOfValueType_CanBeInvokedViaExecuteAsync() - { - // Arrange - var executor = GetExecutorForMethod("ValueTaskOfValueType"); - - // Act - var result = await executor.ExecuteAsync(_targetObject, new object[] { 123 }); - - // Assert - Assert.True(executor.IsMethodAsync); - Assert.Same(typeof(int), executor.AsyncResultType); - Assert.NotNull(result); - Assert.Equal(123, (int)result); - } - - [Fact] - public async void TargetMethodReturningValueTaskOfReferenceType_CanBeInvokedViaExecuteAsync() - { - // Arrange - var executor = GetExecutorForMethod("ValueTaskOfReferenceType"); - - // Act - var result = await executor.ExecuteAsync(_targetObject, new object[] { "test result" }); - - // Assert - Assert.True(executor.IsMethodAsync); - Assert.Same(typeof(string), executor.AsyncResultType); - Assert.Equal("test result", result); - } - - [Fact] - public async void TargetMethodReturningFSharpAsync_CanBeInvokedViaExecute() - { - // Arrange - var executor = GetExecutorForMethod("FSharpAsyncMethod"); - - // Act - var fsharpAsync = (FSharpAsync)executor.Execute(_targetObject, new object[] { "test result" }); - var result = await FSharpAsync.StartAsTask(fsharpAsync, - FSharpOption.None, - FSharpOption.None); - - // Assert - Assert.True(executor.IsMethodAsync); - Assert.Same(typeof(string), executor.AsyncResultType); - Assert.Equal("test result", result); - } - - [Fact] - public async void TargetMethodReturningFailingFSharpAsync_CanBeInvokedViaExecute() - { - // Arrange - var executor = GetExecutorForMethod("FSharpAsyncFailureMethod"); - - // Act - var fsharpAsync = (FSharpAsync)executor.Execute(_targetObject, new object[] { "test result" }); - var resultTask = FSharpAsync.StartAsTask(fsharpAsync, - FSharpOption.None, - FSharpOption.None); - - // Assert - Assert.True(executor.IsMethodAsync); - Assert.Same(typeof(string), executor.AsyncResultType); - - var exception = await Assert.ThrowsAsync(async () => await resultTask); - Assert.IsType(exception.InnerException); - Assert.Equal("Test exception", exception.InnerException.Message); - } - - [Fact] - public async void TargetMethodReturningFSharpAsync_CanBeInvokedViaExecuteAsync() - { - // Arrange - var executor = GetExecutorForMethod("FSharpAsyncMethod"); - - // Act - var result = await executor.ExecuteAsync(_targetObject, new object[] { "test result" }); - - // Assert - Assert.True(executor.IsMethodAsync); - Assert.Same(typeof(string), executor.AsyncResultType); - Assert.Equal("test result", result); - } - - [Fact] - public async void TargetMethodReturningFailingFSharpAsync_CanBeInvokedViaExecuteAsync() - { - // Arrange - var executor = GetExecutorForMethod("FSharpAsyncFailureMethod"); - - // Act - var resultTask = executor.ExecuteAsync(_targetObject, new object[] { "test result" }); - - // Assert - Assert.True(executor.IsMethodAsync); - Assert.Same(typeof(string), executor.AsyncResultType); - - var exception = await Assert.ThrowsAsync(async () => await resultTask); - Assert.IsType(exception.InnerException); - Assert.Equal("Test exception", exception.InnerException.Message); - } - - private ObjectMethodExecutor GetExecutorForMethod(string methodName) - { - var method = typeof(TestObject).GetMethod(methodName); - return ObjectMethodExecutor.Create(method, targetTypeInfo); - } - - private ObjectMethodExecutor GetExecutorForMethod(string methodName, object[] parameterDefaultValues) - { - var method = typeof(TestObject).GetMethod(methodName); - return ObjectMethodExecutor.Create(method, targetTypeInfo, parameterDefaultValues); - } - - public class TestObject - { - public string value; - public int ValueMethod(int i, int j) - { - return i + j; - } - - public void VoidValueMethod(int i) - { - - } - - public TestObject ValueMethodWithReturnType(int i) - { - return new TestObject() { value = "Hello" }; ; - } - - public TestObject ValueMethodWithReturnTypeThrowsException(TestObject i) - { - throw new NotImplementedException("Not Implemented Exception"); - } - - public TestObject ValueMethodUpdateValue(TestObject parameter) - { - parameter.value = "HelloWorld"; - return parameter; - } - - public Task ValueMethodAsync(int i, int j) - { - return Task.FromResult(i + j); - } - - public async Task VoidValueMethodAsync(int i) - { - await ValueMethodAsync(3, 4); - } - public Task ValueMethodWithReturnTypeAsync(int i) - { - return Task.FromResult(new TestObject() { value = "Hello" }); - } - - public async Task ValueMethodWithReturnVoidThrowsExceptionAsync(TestObject i) - { - await Task.CompletedTask; - throw new NotImplementedException("Not Implemented Exception"); - } - - public async Task ValueMethodWithReturnTypeThrowsExceptionAsync(TestObject i) - { - await Task.CompletedTask; - throw new NotImplementedException("Not Implemented Exception"); - } - - public Task ValueMethodUpdateValueAsync(TestObject parameter) - { - parameter.value = "HelloWorld"; - return Task.FromResult(parameter); - } - - public TestAwaitable CustomAwaitableOfReferenceTypeAsync( - string input1, - int input2) - { - return new TestAwaitable(new TestObject - { - value = $"{input1} {input2}" - }); - } - - public TestAwaitable CustomAwaitableOfValueTypeAsync( - int input1, - int input2) - { - return new TestAwaitable(input1 + input2); - } - - public TestAwaitableWithICriticalNotifyCompletion CustomAwaitableWithICriticalNotifyCompletion() - { - return new TestAwaitableWithICriticalNotifyCompletion(); - } - - public TestAwaitableWithoutICriticalNotifyCompletion CustomAwaitableWithoutICriticalNotifyCompletion() - { - return new TestAwaitableWithoutICriticalNotifyCompletion(); - } - - public ValueTask ValueTaskOfValueType(int result) - { - return new ValueTask(result); - } - - public ValueTask ValueTaskOfReferenceType(string result) - { - return new ValueTask(result); - } - - public void MethodWithMultipleParameters(int valueTypeParam, string referenceTypeParam) - { - } - - public FSharpAsync FSharpAsyncMethod(string parameter) - { - return FSharpAsync.AwaitTask(Task.FromResult(parameter)); - } - - public FSharpAsync FSharpAsyncFailureMethod(string parameter) - { - return FSharpAsync.AwaitTask( - Task.FromException(new InvalidOperationException("Test exception"))); - } - } - - public class TestAwaitable - { - private T _result; - private bool _isCompleted; - private List _onCompletedCallbacks = new List(); - - public TestAwaitable(T result) - { - _result = result; - - // Simulate a brief delay before completion - ThreadPool.QueueUserWorkItem(_ => - { - Thread.Sleep(100); - SetCompleted(); - }); - } - - private void SetCompleted() - { - _isCompleted = true; - - foreach (var callback in _onCompletedCallbacks) - { - callback(); - } - } - - public TestAwaiter GetAwaiter() - { - return new TestAwaiter(this); - } - - public struct TestAwaiter : INotifyCompletion - { - private TestAwaitable _owner; - - public TestAwaiter(TestAwaitable owner) : this() - { - _owner = owner; - } - - public bool IsCompleted => _owner._isCompleted; - - public void OnCompleted(Action continuation) - { - if (_owner._isCompleted) - { - continuation(); - } - else - { - _owner._onCompletedCallbacks.Add(continuation); - } - } - - public T GetResult() - { - return _owner._result; - } - } - } - - public class TestAwaitableWithICriticalNotifyCompletion - { - public TestAwaiterWithICriticalNotifyCompletion GetAwaiter() - => new TestAwaiterWithICriticalNotifyCompletion(); - } - - public class TestAwaitableWithoutICriticalNotifyCompletion - { - public TestAwaiterWithoutICriticalNotifyCompletion GetAwaiter() - => new TestAwaiterWithoutICriticalNotifyCompletion(); - } - - public class TestAwaiterWithICriticalNotifyCompletion - : CompletionTrackingAwaiterBase, ICriticalNotifyCompletion - { - } - - public class TestAwaiterWithoutICriticalNotifyCompletion - : CompletionTrackingAwaiterBase, INotifyCompletion - { - } - - public class CompletionTrackingAwaiterBase - { - private string _result; - - public bool IsCompleted { get; private set; } - - public string GetResult() => _result; - - public void OnCompleted(Action continuation) - { - _result = "Used OnCompleted"; - IsCompleted = true; - continuation(); - } - - public void UnsafeOnCompleted(Action continuation) - { - _result = "Used UnsafeOnCompleted"; - IsCompleted = true; - continuation(); - } - } - } -} From 921060b2cac2be3c71b1989676e6c37f80831a9c Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Thu, 13 Dec 2018 11:15:55 -0800 Subject: [PATCH 029/183] Remove Microsoft.Extensions.PropertyActivator.Sources and Microsoft.Extensions.PropertyHelper.Sources \n\nCommit migrated from https://github.com/dotnet/extensions/commit/6c11818fe9883e6ef5bb0dd85ff460b9de92737a --- .../PropertyActivator/PropertyActivator.cs | 110 --- src/Shared/PropertyHelper/PropertyHelper.cs | 526 ----------- .../Microsoft.AspNetCore.Shared.Tests.csproj | 1 - .../Shared.Tests/PropertyActivatorTest.cs | 187 ---- .../test/Shared.Tests/PropertyHelperTest.cs | 831 ------------------ 5 files changed, 1655 deletions(-) delete mode 100644 src/Shared/PropertyActivator/PropertyActivator.cs delete mode 100644 src/Shared/PropertyHelper/PropertyHelper.cs delete mode 100644 src/Shared/test/Shared.Tests/PropertyActivatorTest.cs delete mode 100644 src/Shared/test/Shared.Tests/PropertyHelperTest.cs diff --git a/src/Shared/PropertyActivator/PropertyActivator.cs b/src/Shared/PropertyActivator/PropertyActivator.cs deleted file mode 100644 index 925f6a76ae0b..000000000000 --- a/src/Shared/PropertyActivator/PropertyActivator.cs +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Linq; -using System.Reflection; - -namespace Microsoft.Extensions.Internal -{ - internal class PropertyActivator - { - private readonly Func _valueAccessor; - private readonly Action _fastPropertySetter; - - public PropertyActivator( - PropertyInfo propertyInfo, - Func valueAccessor) - { - if (propertyInfo == null) - { - throw new ArgumentNullException(nameof(propertyInfo)); - } - - if (valueAccessor == null) - { - throw new ArgumentNullException(nameof(valueAccessor)); - } - - PropertyInfo = propertyInfo; - _valueAccessor = valueAccessor; - _fastPropertySetter = PropertyHelper.MakeFastPropertySetter(propertyInfo); - } - - public PropertyInfo PropertyInfo { get; private set; } - - public object Activate(object instance, TContext context) - { - if (instance == null) - { - throw new ArgumentNullException(nameof(instance)); - } - - var value = _valueAccessor(context); - _fastPropertySetter(instance, value); - return value; - } - - public static PropertyActivator[] GetPropertiesToActivate( - Type type, - Type activateAttributeType, - Func> createActivateInfo) - { - if (type == null) - { - throw new ArgumentNullException(nameof(type)); - } - - if (activateAttributeType == null) - { - throw new ArgumentNullException(nameof(activateAttributeType)); - } - - if (createActivateInfo == null) - { - throw new ArgumentNullException(nameof(createActivateInfo)); - } - - return GetPropertiesToActivate(type, activateAttributeType, createActivateInfo, includeNonPublic: false); - } - - public static PropertyActivator[] GetPropertiesToActivate( - Type type, - Type activateAttributeType, - Func> createActivateInfo, - bool includeNonPublic) - { - if (type == null) - { - throw new ArgumentNullException(nameof(type)); - } - - if (activateAttributeType == null) - { - throw new ArgumentNullException(nameof(activateAttributeType)); - } - - if (createActivateInfo == null) - { - throw new ArgumentNullException(nameof(createActivateInfo)); - } - - var properties = type.GetRuntimeProperties() - .Where((property) => - { - return - property.IsDefined(activateAttributeType) && - property.GetIndexParameters().Length == 0 && - property.SetMethod != null && - !property.SetMethod.IsStatic; - }); - - if (!includeNonPublic) - { - properties = properties.Where(property => property.SetMethod.IsPublic); - } - - return properties.Select(createActivateInfo).ToArray(); - } - } -} \ No newline at end of file diff --git a/src/Shared/PropertyHelper/PropertyHelper.cs b/src/Shared/PropertyHelper/PropertyHelper.cs deleted file mode 100644 index 27ba5661a423..000000000000 --- a/src/Shared/PropertyHelper/PropertyHelper.cs +++ /dev/null @@ -1,526 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Reflection; - -namespace Microsoft.Extensions.Internal -{ - internal class PropertyHelper - { - // Delegate type for a by-ref property getter - private delegate TValue ByRefFunc(ref TDeclaringType arg); - - private static readonly MethodInfo CallPropertyGetterOpenGenericMethod = - typeof(PropertyHelper).GetTypeInfo().GetDeclaredMethod(nameof(CallPropertyGetter)); - - private static readonly MethodInfo CallPropertyGetterByReferenceOpenGenericMethod = - typeof(PropertyHelper).GetTypeInfo().GetDeclaredMethod(nameof(CallPropertyGetterByReference)); - - private static readonly MethodInfo CallNullSafePropertyGetterOpenGenericMethod = - typeof(PropertyHelper).GetTypeInfo().GetDeclaredMethod(nameof(CallNullSafePropertyGetter)); - - private static readonly MethodInfo CallNullSafePropertyGetterByReferenceOpenGenericMethod = - typeof(PropertyHelper).GetTypeInfo().GetDeclaredMethod(nameof(CallNullSafePropertyGetterByReference)); - - private static readonly MethodInfo CallPropertySetterOpenGenericMethod = - typeof(PropertyHelper).GetTypeInfo().GetDeclaredMethod(nameof(CallPropertySetter)); - - // Using an array rather than IEnumerable, as target will be called on the hot path numerous times. - private static readonly ConcurrentDictionary PropertiesCache = - new ConcurrentDictionary(); - - private static readonly ConcurrentDictionary VisiblePropertiesCache = - new ConcurrentDictionary(); - - private Action _valueSetter; - private Func _valueGetter; - - /// - /// Initializes a fast . - /// This constructor does not cache the helper. For caching, use . - /// - public PropertyHelper(PropertyInfo property) - { - if (property == null) - { - throw new ArgumentNullException(nameof(property)); - } - - Property = property; - Name = property.Name; - } - - /// - /// Gets the backing . - /// - public PropertyInfo Property { get; } - - /// - /// Gets (or sets in derived types) the property name. - /// - public virtual string Name { get; protected set; } - - /// - /// Gets the property value getter. - /// - public Func ValueGetter - { - get - { - if (_valueGetter == null) - { - _valueGetter = MakeFastPropertyGetter(Property); - } - - return _valueGetter; - } - } - - /// - /// Gets the property value setter. - /// - public Action ValueSetter - { - get - { - if (_valueSetter == null) - { - _valueSetter = MakeFastPropertySetter(Property); - } - - return _valueSetter; - } - } - - /// - /// Returns the property value for the specified . - /// - /// The object whose property value will be returned. - /// The property value. - public object GetValue(object instance) - { - return ValueGetter(instance); - } - - /// - /// Sets the property value for the specified . - /// - /// The object whose property value will be set. - /// The property value. - public void SetValue(object instance, object value) - { - ValueSetter(instance, value); - } - - /// - /// Creates and caches fast property helpers that expose getters for every public get property on the - /// underlying type. - /// - /// The type info to extract property accessors for. - /// A cached array of all public properties of the specified type. - /// - public static PropertyHelper[] GetProperties(TypeInfo typeInfo) - { - return GetProperties(typeInfo.AsType()); - } - - /// - /// Creates and caches fast property helpers that expose getters for every public get property on the - /// specified type. - /// - /// The type to extract property accessors for. - /// A cached array of all public properties of the specified type. - /// - public static PropertyHelper[] GetProperties(Type type) - { - return GetProperties(type, CreateInstance, PropertiesCache); - } - - /// - /// - /// Creates and caches fast property helpers that expose getters for every non-hidden get property - /// on the specified type. - /// - /// - /// excludes properties defined on base types that have been - /// hidden by definitions using the new keyword. - /// - /// - /// The type info to extract property accessors for. - /// - /// A cached array of all public properties of the specified type. - /// - public static PropertyHelper[] GetVisibleProperties(TypeInfo typeInfo) - { - return GetVisibleProperties(typeInfo.AsType(), CreateInstance, PropertiesCache, VisiblePropertiesCache); - } - - /// - /// - /// Creates and caches fast property helpers that expose getters for every non-hidden get property - /// on the specified type. - /// - /// - /// excludes properties defined on base types that have been - /// hidden by definitions using the new keyword. - /// - /// - /// The type to extract property accessors for. - /// - /// A cached array of all public properties of the specified type. - /// - public static PropertyHelper[] GetVisibleProperties(Type type) - { - return GetVisibleProperties(type, CreateInstance, PropertiesCache, VisiblePropertiesCache); - } - - /// - /// Creates a single fast property getter. The result is not cached. - /// - /// propertyInfo to extract the getter for. - /// a fast getter. - /// - /// This method is more memory efficient than a dynamically compiled lambda, and about the - /// same speed. - /// - public static Func MakeFastPropertyGetter(PropertyInfo propertyInfo) - { - Debug.Assert(propertyInfo != null); - - return MakeFastPropertyGetter( - propertyInfo, - CallPropertyGetterOpenGenericMethod, - CallPropertyGetterByReferenceOpenGenericMethod); - } - - /// - /// Creates a single fast property getter which is safe for a null input object. The result is not cached. - /// - /// propertyInfo to extract the getter for. - /// a fast getter. - /// - /// This method is more memory efficient than a dynamically compiled lambda, and about the - /// same speed. - /// - public static Func MakeNullSafeFastPropertyGetter(PropertyInfo propertyInfo) - { - Debug.Assert(propertyInfo != null); - - return MakeFastPropertyGetter( - propertyInfo, - CallNullSafePropertyGetterOpenGenericMethod, - CallNullSafePropertyGetterByReferenceOpenGenericMethod); - } - - private static Func MakeFastPropertyGetter( - PropertyInfo propertyInfo, - MethodInfo propertyGetterWrapperMethod, - MethodInfo propertyGetterByRefWrapperMethod) - { - Debug.Assert(propertyInfo != null); - - // Must be a generic method with a Func<,> parameter - Debug.Assert(propertyGetterWrapperMethod != null); - Debug.Assert(propertyGetterWrapperMethod.IsGenericMethodDefinition); - Debug.Assert(propertyGetterWrapperMethod.GetParameters().Length == 2); - - // Must be a generic method with a ByRefFunc<,> parameter - Debug.Assert(propertyGetterByRefWrapperMethod != null); - Debug.Assert(propertyGetterByRefWrapperMethod.IsGenericMethodDefinition); - Debug.Assert(propertyGetterByRefWrapperMethod.GetParameters().Length == 2); - - var getMethod = propertyInfo.GetMethod; - Debug.Assert(getMethod != null); - Debug.Assert(!getMethod.IsStatic); - Debug.Assert(getMethod.GetParameters().Length == 0); - - // Instance methods in the CLR can be turned into static methods where the first parameter - // is open over "target". This parameter is always passed by reference, so we have a code - // path for value types and a code path for reference types. - if (getMethod.DeclaringType.GetTypeInfo().IsValueType) - { - // Create a delegate (ref TDeclaringType) -> TValue - return MakeFastPropertyGetter( - typeof(ByRefFunc<,>), - getMethod, - propertyGetterByRefWrapperMethod); - } - else - { - // Create a delegate TDeclaringType -> TValue - return MakeFastPropertyGetter( - typeof(Func<,>), - getMethod, - propertyGetterWrapperMethod); - } - } - - private static Func MakeFastPropertyGetter( - Type openGenericDelegateType, - MethodInfo propertyGetMethod, - MethodInfo openGenericWrapperMethod) - { - var typeInput = propertyGetMethod.DeclaringType; - var typeOutput = propertyGetMethod.ReturnType; - - var delegateType = openGenericDelegateType.MakeGenericType(typeInput, typeOutput); - var propertyGetterDelegate = propertyGetMethod.CreateDelegate(delegateType); - - var wrapperDelegateMethod = openGenericWrapperMethod.MakeGenericMethod(typeInput, typeOutput); - var accessorDelegate = wrapperDelegateMethod.CreateDelegate( - typeof(Func), - propertyGetterDelegate); - - return (Func)accessorDelegate; - } - - /// - /// Creates a single fast property setter for reference types. The result is not cached. - /// - /// propertyInfo to extract the setter for. - /// a fast getter. - /// - /// This method is more memory efficient than a dynamically compiled lambda, and about the - /// same speed. This only works for reference types. - /// - public static Action MakeFastPropertySetter(PropertyInfo propertyInfo) - { - Debug.Assert(propertyInfo != null); - Debug.Assert(!propertyInfo.DeclaringType.GetTypeInfo().IsValueType); - - var setMethod = propertyInfo.SetMethod; - Debug.Assert(setMethod != null); - Debug.Assert(!setMethod.IsStatic); - Debug.Assert(setMethod.ReturnType == typeof(void)); - var parameters = setMethod.GetParameters(); - Debug.Assert(parameters.Length == 1); - - // Instance methods in the CLR can be turned into static methods where the first parameter - // is open over "target". This parameter is always passed by reference, so we have a code - // path for value types and a code path for reference types. - var typeInput = setMethod.DeclaringType; - var parameterType = parameters[0].ParameterType; - - // Create a delegate TDeclaringType -> { TDeclaringType.Property = TValue; } - var propertySetterAsAction = - setMethod.CreateDelegate(typeof(Action<,>).MakeGenericType(typeInput, parameterType)); - var callPropertySetterClosedGenericMethod = - CallPropertySetterOpenGenericMethod.MakeGenericMethod(typeInput, parameterType); - var callPropertySetterDelegate = - callPropertySetterClosedGenericMethod.CreateDelegate( - typeof(Action), propertySetterAsAction); - - return (Action)callPropertySetterDelegate; - } - - /// - /// Given an object, adds each instance property with a public get method as a key and its - /// associated value to a dictionary. - /// - /// If the object is already an instance, then a copy - /// is returned. - /// - /// - /// The implementation of PropertyHelper will cache the property accessors per-type. This is - /// faster when the same type is used multiple times with ObjectToDictionary. - /// - public static IDictionary ObjectToDictionary(object value) - { - var dictionary = value as IDictionary; - if (dictionary != null) - { - return new Dictionary(dictionary, StringComparer.OrdinalIgnoreCase); - } - - dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); - - if (value != null) - { - foreach (var helper in GetProperties(value.GetType())) - { - dictionary[helper.Name] = helper.GetValue(value); - } - } - - return dictionary; - } - - private static PropertyHelper CreateInstance(PropertyInfo property) - { - return new PropertyHelper(property); - } - - // Called via reflection - private static object CallPropertyGetter( - Func getter, - object target) - { - return getter((TDeclaringType)target); - } - - // Called via reflection - private static object CallPropertyGetterByReference( - ByRefFunc getter, - object target) - { - var unboxed = (TDeclaringType)target; - return getter(ref unboxed); - } - - // Called via reflection - private static object CallNullSafePropertyGetter( - Func getter, - object target) - { - if (target == null) - { - return null; - } - - return getter((TDeclaringType)target); - } - - // Called via reflection - private static object CallNullSafePropertyGetterByReference( - ByRefFunc getter, - object target) - { - if (target == null) - { - return null; - } - - var unboxed = (TDeclaringType)target; - return getter(ref unboxed); - } - - private static void CallPropertySetter( - Action setter, - object target, - object value) - { - setter((TDeclaringType)target, (TValue)value); - } - - protected static PropertyHelper[] GetVisibleProperties( - Type type, - Func createPropertyHelper, - ConcurrentDictionary allPropertiesCache, - ConcurrentDictionary visiblePropertiesCache) - { - PropertyHelper[] result; - if (visiblePropertiesCache.TryGetValue(type, out result)) - { - return result; - } - - // The simple and common case, this is normal POCO object - no need to allocate. - var allPropertiesDefinedOnType = true; - var allProperties = GetProperties(type, createPropertyHelper, allPropertiesCache); - foreach (var propertyHelper in allProperties) - { - if (propertyHelper.Property.DeclaringType != type) - { - allPropertiesDefinedOnType = false; - break; - } - } - - if (allPropertiesDefinedOnType) - { - result = allProperties; - visiblePropertiesCache.TryAdd(type, result); - return result; - } - - // There's some inherited properties here, so we need to check for hiding via 'new'. - var filteredProperties = new List(allProperties.Length); - foreach (var propertyHelper in allProperties) - { - var declaringType = propertyHelper.Property.DeclaringType; - if (declaringType == type) - { - filteredProperties.Add(propertyHelper); - continue; - } - - // If this property was declared on a base type then look for the definition closest to the - // the type to see if we should include it. - var ignoreProperty = false; - - // Walk up the hierarchy until we find the type that actually declares this - // PropertyInfo. - var currentTypeInfo = type.GetTypeInfo(); - var declaringTypeInfo = declaringType.GetTypeInfo(); - while (currentTypeInfo != null && currentTypeInfo != declaringTypeInfo) - { - // We've found a 'more proximal' public definition - var declaredProperty = currentTypeInfo.GetDeclaredProperty(propertyHelper.Name); - if (declaredProperty != null) - { - ignoreProperty = true; - break; - } - - currentTypeInfo = currentTypeInfo.BaseType?.GetTypeInfo(); - } - - if (!ignoreProperty) - { - filteredProperties.Add(propertyHelper); - } - } - - result = filteredProperties.ToArray(); - visiblePropertiesCache.TryAdd(type, result); - return result; - } - - protected static PropertyHelper[] GetProperties( - Type type, - Func createPropertyHelper, - ConcurrentDictionary cache) - { - // Unwrap nullable types. This means Nullable.Value and Nullable.HasValue will not be - // part of the sequence of properties returned by this method. - type = Nullable.GetUnderlyingType(type) ?? type; - - PropertyHelper[] helpers; - if (!cache.TryGetValue(type, out helpers)) - { - // We avoid loading indexed properties using the Where statement. - var properties = type.GetRuntimeProperties().Where(IsInterestingProperty); - - var typeInfo = type.GetTypeInfo(); - if (typeInfo.IsInterface) - { - // Reflection does not return information about inherited properties on the interface itself. - properties = properties.Concat(typeInfo.ImplementedInterfaces.SelectMany( - interfaceType => interfaceType.GetRuntimeProperties().Where(IsInterestingProperty))); - } - - helpers = properties.Select(p => createPropertyHelper(p)).ToArray(); - cache.TryAdd(type, helpers); - } - - return helpers; - } - - // Indexed properties are not useful (or valid) for grabbing properties off an object. - private static bool IsInterestingProperty(PropertyInfo property) - { - // For improving application startup time, do not use GetIndexParameters() api early in this check as it - // creates a copy of parameter array and also we would like to check for the presence of a get method - // and short circuit asap. - return property.GetMethod != null && - property.GetMethod.IsPublic && - !property.GetMethod.IsStatic && - property.GetMethod.GetParameters().Length == 0; - } - } -} diff --git a/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj b/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj index 05b321a6d60d..67ff52821bef 100644 --- a/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj +++ b/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj @@ -21,7 +21,6 @@ - diff --git a/src/Shared/test/Shared.Tests/PropertyActivatorTest.cs b/src/Shared/test/Shared.Tests/PropertyActivatorTest.cs deleted file mode 100644 index a5cb1605b38c..000000000000 --- a/src/Shared/test/Shared.Tests/PropertyActivatorTest.cs +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Reflection; -using Xunit; - -namespace Microsoft.Extensions.Internal -{ - public class PropertyActivatorTest - { - [Fact] - public void Activate_InvokesValueAccessorWithExpectedValue() - { - // Arrange - var instance = new TestClass(); - var typeInfo = instance.GetType().GetTypeInfo(); - var property = typeInfo.GetDeclaredProperty("IntProperty"); - var invokedWith = -1; - var activator = new PropertyActivator( - property, - valueAccessor: (val) => - { - invokedWith = val; - return val; - }); - - // Act - activator.Activate(instance, 123); - - // Assert - Assert.Equal(123, invokedWith); - } - - [Fact] - public void Activate_SetsPropertyValue() - { - // Arrange - var instance = new TestClass(); - var typeInfo = instance.GetType().GetTypeInfo(); - var property = typeInfo.GetDeclaredProperty("IntProperty"); - var activator = new PropertyActivator(property, valueAccessor: (val) => val + 1); - - // Act - activator.Activate(instance, 123); - - // Assert - Assert.Equal(124, instance.IntProperty); - } - - [Fact] - public void GetPropertiesToActivate_RestrictsActivatableProperties() - { - // Arrange - var instance = new TestClass(); - var typeInfo = instance.GetType().GetTypeInfo(); - var expectedPropertyInfo = typeInfo.GetDeclaredProperty("ActivatableProperty"); - - // Act - var propertiesToActivate = PropertyActivator.GetPropertiesToActivate( - type: typeof(TestClass), - activateAttributeType: typeof(TestActivateAttribute), - createActivateInfo: - (propertyInfo) => new PropertyActivator(propertyInfo, valueAccessor: (val) => val + 1)); - - // Assert - Assert.Collection( - propertiesToActivate, - (activator) => - { - Assert.Equal(expectedPropertyInfo, activator.PropertyInfo); - }); - } - - [Fact] - public void GetPropertiesToActivate_CanCreateCustomPropertyActivators() - { - // Arrange - var instance = new TestClass(); - var typeInfo = instance.GetType().GetTypeInfo(); - var expectedPropertyInfo = typeInfo.GetDeclaredProperty("IntProperty"); - - // Act - var propertiesToActivate = PropertyActivator.GetPropertiesToActivate( - type: typeof(TestClass), - activateAttributeType: typeof(TestActivateAttribute), - createActivateInfo: - (propertyInfo) => new PropertyActivator(expectedPropertyInfo, valueAccessor: (val) => val + 1)); - - // Assert - Assert.Collection( - propertiesToActivate, - (activator) => - { - Assert.Equal(expectedPropertyInfo, activator.PropertyInfo); - }); - } - - [Fact] - public void GetPropertiesToActivate_ExcludesNonPublic() - { - // Arrange - var instance = new TestClassWithPropertyVisiblity(); - var typeInfo = instance.GetType().GetTypeInfo(); - var expectedPropertyInfo = typeInfo.GetDeclaredProperty("Public"); - - // Act - var propertiesToActivate = PropertyActivator.GetPropertiesToActivate( - typeof(TestClassWithPropertyVisiblity), - typeof(TestActivateAttribute), - (propertyInfo) => new PropertyActivator(propertyInfo, valueAccessor: (val) => val)); - - // Assert - Assert.Single(propertiesToActivate); - Assert.Single(propertiesToActivate, p => p.PropertyInfo == expectedPropertyInfo); - } - - [Fact] - public void GetPropertiesToActivate_IncludesNonPublic() - { - // Arrange - var instance = new TestClassWithPropertyVisiblity(); - var typeInfo = instance.GetType().GetTypeInfo(); - - // Act - var propertiesToActivate = PropertyActivator.GetPropertiesToActivate( - typeof(TestClassWithPropertyVisiblity), - typeof(TestActivateAttribute), - (propertyInfo) => new PropertyActivator(propertyInfo, valueAccessor: (val) => val), - includeNonPublic: true); - - // Assert - Assert.Equal(5, propertiesToActivate.Length); - } - - private class TestClass - { - public int IntProperty { get; set; } - - [TestActivate] - public int ActivatableProperty { get; set; } - - [TestActivate] - public int NoSetterActivatableProperty { get; } - - [TestActivate] - public int this[int something] // Not activatable - { - get - { - return 0; - } - } - - [TestActivate] - public static int StaticActivatablProperty { get; set; } - } - - private class TestClassWithPropertyVisiblity - { - [TestActivate] - public int Public { get; set; } - - [TestActivate] - protected int Protected { get; set; } - - [TestActivate] - internal int Internal { get; set; } - - [TestActivate] - protected internal int ProtectedInternal {get; set; } - - [TestActivate] - private int Private { get; set; } - } - - [AttributeUsage(AttributeTargets.Property)] - private class TestActivateAttribute : Attribute - { - } - - private class ActivationInfo - { - public string Name { get; set; } - } - } -} diff --git a/src/Shared/test/Shared.Tests/PropertyHelperTest.cs b/src/Shared/test/Shared.Tests/PropertyHelperTest.cs deleted file mode 100644 index 19cf08b3705a..000000000000 --- a/src/Shared/test/Shared.Tests/PropertyHelperTest.cs +++ /dev/null @@ -1,831 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using Xunit; - -namespace Microsoft.Extensions.Internal -{ - public class PropertyHelperTest - { - [Fact] - public void PropertyHelper_ReturnsNameCorrectly() - { - // Arrange - var anonymous = new { foo = "bar" }; - var property = PropertyHelper.GetProperties(anonymous.GetType()).First().Property; - - // Act - var helper = new PropertyHelper(property); - - // Assert - Assert.Equal("foo", property.Name); - Assert.Equal("foo", helper.Name); - } - - [Fact] - public void PropertyHelper_ReturnsValueCorrectly() - { - // Arrange - var anonymous = new { bar = "baz" }; - var property = PropertyHelper.GetProperties(anonymous.GetType()).First().Property; - - // Act - var helper = new PropertyHelper(property); - - // Assert - Assert.Equal("bar", helper.Name); - Assert.Equal("baz", helper.GetValue(anonymous)); - } - - [Fact] - public void PropertyHelper_ReturnsGetterDelegate() - { - // Arrange - var anonymous = new { bar = "baz" }; - var property = PropertyHelper.GetProperties(anonymous.GetType()).First().Property; - - // Act - var helper = new PropertyHelper(property); - - // Assert - Assert.NotNull(helper.ValueGetter); - Assert.Equal("baz", helper.ValueGetter(anonymous)); - } - - [Fact] - public void SetValue_SetsPropertyValue() - { - // Arrange - var expected = "new value"; - var instance = new BaseClass { PropA = "old value" }; - var helper = PropertyHelper.GetProperties( - instance.GetType()).First(prop => prop.Name == "PropA"); - - // Act - helper.SetValue(instance, expected); - - // Assert - Assert.Equal(expected, instance.PropA); - } - - [Fact] - public void PropertyHelper_ReturnsSetterDelegate() - { - // Arrange - var expected = "new value"; - var instance = new BaseClass { PropA = "old value" }; - var helper = PropertyHelper.GetProperties( - instance.GetType()).First(prop => prop.Name == "PropA"); - - // Act and Assert - Assert.NotNull(helper.ValueSetter); - helper.ValueSetter(instance, expected); - - // Assert - Assert.Equal(expected, instance.PropA); - } - - [Fact] - public void PropertyHelper_ReturnsValueCorrectly_ForValueTypes() - { - // Arrange - var anonymous = new { foo = 32 }; - var property = PropertyHelper.GetProperties(anonymous.GetType()).First().Property; - - // Act - var helper = new PropertyHelper(property); - - // Assert - Assert.Equal("foo", helper.Name); - Assert.Equal(32, helper.GetValue(anonymous)); - } - - [Fact] - public void PropertyHelper_ReturnsCachedPropertyHelper() - { - // Arrange - var anonymous = new { foo = "bar" }; - - // Act - var helpers1 = PropertyHelper.GetProperties(anonymous.GetType().GetTypeInfo()); - var helpers2 = PropertyHelper.GetProperties(anonymous.GetType().GetTypeInfo()); - - // Assert - Assert.Single(helpers1); - Assert.Same(helpers1, helpers2); - Assert.Same(helpers1[0], helpers2[0]); - } - - [Fact] - public void PropertyHelper_DoesNotChangeUnderscores() - { - // Arrange - var anonymous = new { bar_baz2 = "foo" }; - - // Act + Assert - var helper = Assert.Single(PropertyHelper.GetProperties(anonymous.GetType().GetTypeInfo())); - Assert.Equal("bar_baz2", helper.Name); - } - - [Fact] - public void PropertyHelper_DoesNotFindPrivateProperties() - { - // Arrange - var anonymous = new PrivateProperties(); - - // Act + Assert - var helper = Assert.Single(PropertyHelper.GetProperties(anonymous.GetType().GetTypeInfo())); - Assert.Equal("Prop1", helper.Name); - } - - [Fact] - public void PropertyHelper_DoesNotFindStaticProperties() - { - // Arrange - var anonymous = new Static(); - - // Act + Assert - var helper = Assert.Single(PropertyHelper.GetProperties(anonymous.GetType().GetTypeInfo())); - Assert.Equal("Prop5", helper.Name); - } - - [Fact] - public void PropertyHelper_DoesNotFindSetOnlyProperties() - { - // Arrange - var anonymous = new SetOnly(); - - // Act + Assert - var helper = Assert.Single(PropertyHelper.GetProperties(anonymous.GetType().GetTypeInfo())); - Assert.Equal("Prop6", helper.Name); - } - - [Theory] - [InlineData(typeof(int?))] - [InlineData(typeof(DayOfWeek?))] - public void PropertyHelper_WorksForNullablePrimitiveAndEnumTypes(Type nullableType) - { - // Act - var properties = PropertyHelper.GetProperties(nullableType); - - // Assert - Assert.Empty(properties); - } - - [Fact] - public void PropertyHelper_UnwrapsNullableTypes() - { - // Arrange - var myType = typeof(MyStruct?); - - // Act - var properties = PropertyHelper.GetProperties(myType); - - // Assert - var property = Assert.Single(properties); - Assert.Equal("Foo", property.Name); - } - - [Fact] - public void PropertyHelper_WorksForStruct() - { - // Arrange - var anonymous = new MyProperties(); - - anonymous.IntProp = 3; - anonymous.StringProp = "Five"; - - // Act + Assert - var helper1 = Assert.Single(PropertyHelper.GetProperties(anonymous.GetType().GetTypeInfo()).Where(prop => prop.Name == "IntProp")); - var helper2 = Assert.Single(PropertyHelper.GetProperties(anonymous.GetType().GetTypeInfo()).Where(prop => prop.Name == "StringProp")); - Assert.Equal(3, helper1.GetValue(anonymous)); - Assert.Equal("Five", helper2.GetValue(anonymous)); - } - - [Fact] - public void PropertyHelper_ForDerivedClass() - { - // Arrange - var derived = new DerivedClass { PropA = "propAValue", PropB = "propBValue" }; - - // Act - var helpers = PropertyHelper.GetProperties(derived.GetType().GetTypeInfo()).ToArray(); - - // Assert - Assert.NotNull(helpers); - Assert.Equal(2, helpers.Length); - - var propAHelper = Assert.Single(helpers.Where(h => h.Name == "PropA")); - var propBHelper = Assert.Single(helpers.Where(h => h.Name == "PropB")); - - Assert.Equal("propAValue", propAHelper.GetValue(derived)); - Assert.Equal("propBValue", propBHelper.GetValue(derived)); - } - - [Fact] - public void PropertyHelper_ForDerivedClass_WithNew() - { - // Arrange - var derived = new DerivedClassWithNew { PropA = "propAValue" }; - - // Act - var helpers = PropertyHelper.GetProperties(derived.GetType().GetTypeInfo()).ToArray(); - - // Assert - Assert.NotNull(helpers); - Assert.Equal(2, helpers.Length); - - var propAHelper = Assert.Single(helpers.Where(h => h.Name == "PropA")); - var propBHelper = Assert.Single(helpers.Where(h => h.Name == "PropB")); - - Assert.Equal("propAValue", propAHelper.GetValue(derived)); - Assert.Equal("Newed", propBHelper.GetValue(derived)); - } - - [Fact] - public void PropertyHelper_ForDerived_WithVirtual() - { - // Arrange - var derived = new DerivedClassWithOverride { PropA = "propAValue", PropB = "propBValue" }; - - // Act - var helpers = PropertyHelper.GetProperties(derived.GetType().GetTypeInfo()).ToArray(); - - // Assert - Assert.NotNull(helpers); - Assert.Equal(2, helpers.Length); - - var propAHelper = Assert.Single(helpers.Where(h => h.Name == "PropA")); - var propBHelper = Assert.Single(helpers.Where(h => h.Name == "PropB")); - - Assert.Equal("OverridenpropAValue", propAHelper.GetValue(derived)); - Assert.Equal("propBValue", propBHelper.GetValue(derived)); - } - - [Fact] - public void PropertyHelper_ForInterface_ReturnsExpectedProperties() - { - // Arrange - var expectedNames = new[] { "Count", "IsReadOnly" }; - - // Act - var helpers = PropertyHelper.GetProperties(typeof(ICollection)); - - // Assert - Assert.Collection( - helpers.OrderBy(helper => helper.Name, StringComparer.Ordinal), - helper => { Assert.Equal(expectedNames[0], helper.Name, StringComparer.Ordinal); }, - helper => { Assert.Equal(expectedNames[1], helper.Name, StringComparer.Ordinal); }); - } - - [Fact] - public void PropertyHelper_ForDerivedInterface_ReturnsAllProperties() - { - // Arrange - var expectedNames = new[] { "Count", "IsReadOnly", "Keys", "Values" }; - - // Act - var helpers = PropertyHelper.GetProperties(typeof(IDictionary)); - - // Assert - Assert.Collection( - helpers.OrderBy(helper => helper.Name, StringComparer.Ordinal), - helper => { Assert.Equal(expectedNames[0], helper.Name, StringComparer.Ordinal); }, - helper => { Assert.Equal(expectedNames[1], helper.Name, StringComparer.Ordinal); }, - helper => { Assert.Equal(expectedNames[2], helper.Name, StringComparer.Ordinal); }, - helper => { Assert.Equal(expectedNames[3], helper.Name, StringComparer.Ordinal); }); - } - - [Fact] - public void GetProperties_ExcludesIndexersAndPropertiesWithoutPublicGetters() - { - // Arrange - var type = typeof(DerivedClassWithNonReadableProperties); - - // Act - var result = PropertyHelper.GetProperties(type).ToArray(); - - // Assert - Assert.Equal(3, result.Length); - Assert.Equal("Visible", result[0].Name); - Assert.Equal("PropA", result[1].Name); - Assert.Equal("PropB", result[2].Name); - } - - [Fact] - public void GetVisibleProperties_NoHiddenProperty() - { - // Arrange - var type = typeof(string); - - // Act - var result = PropertyHelper.GetVisibleProperties(type).ToArray(); - - // Assert - var property = Assert.Single(result); - Assert.Equal("Length", property.Name); - Assert.Equal(typeof(int), property.Property.PropertyType); - } - - [Fact] - public void GetVisibleProperties_HiddenProperty() - { - // Arrange - var type = typeof(DerivedHiddenProperty); - - // Act - var result = PropertyHelper.GetVisibleProperties(type).ToArray(); - - // Assert - Assert.Equal(2, result.Length); - Assert.Equal("Id", result[0].Name); - Assert.Equal(typeof(string), result[0].Property.PropertyType); - Assert.Equal("Name", result[1].Name); - Assert.Equal(typeof(string), result[1].Property.PropertyType); - } - - [Fact] - public void GetVisibleProperties_HiddenProperty_TwoLevels() - { - // Arrange - var type = typeof(DerivedHiddenProperty2); - - // Act - var result = PropertyHelper.GetVisibleProperties(type).ToArray(); - - // Assert - Assert.Equal(2, result.Length); - Assert.Equal("Id", result[0].Name); - Assert.Equal(typeof(Guid), result[0].Property.PropertyType); - Assert.Equal("Name", result[1].Name); - Assert.Equal(typeof(string), result[1].Property.PropertyType); - } - - [Fact] - public void GetVisibleProperties_NoHiddenPropertyWithTypeInfoInput() - { - // Arrange - var type = typeof(string); - - // Act - var result = PropertyHelper.GetVisibleProperties(type.GetTypeInfo()).ToArray(); - - // Assert - var property = Assert.Single(result); - Assert.Equal("Length", property.Name); - Assert.Equal(typeof(int), property.Property.PropertyType); - } - - [Fact] - public void GetVisibleProperties_HiddenPropertyWithTypeInfoInput() - { - // Arrange - var type = typeof(DerivedHiddenProperty); - - // Act - var result = PropertyHelper.GetVisibleProperties(type.GetTypeInfo()).ToArray(); - - // Assert - Assert.Equal(2, result.Length); - Assert.Equal("Id", result[0].Name); - Assert.Equal(typeof(string), result[0].Property.PropertyType); - Assert.Equal("Name", result[1].Name); - Assert.Equal(typeof(string), result[1].Property.PropertyType); - } - - [Fact] - public void GetVisibleProperties_HiddenProperty_TwoLevelsWithTypeInfoInput() - { - // Arrange - var type = typeof(DerivedHiddenProperty2); - - // Act - var result = PropertyHelper.GetVisibleProperties(type.GetTypeInfo()).ToArray(); - - // Assert - Assert.Equal(2, result.Length); - Assert.Equal("Id", result[0].Name); - Assert.Equal(typeof(Guid), result[0].Property.PropertyType); - Assert.Equal("Name", result[1].Name); - Assert.Equal(typeof(string), result[1].Property.PropertyType); - } - - [Fact] - public void MakeFastPropertySetter_SetsPropertyValues_ForPublicAndNobPublicProperties() - { - // Arrange - var instance = new BaseClass(); - var typeInfo = instance.GetType().GetTypeInfo(); - var publicProperty = typeInfo.GetDeclaredProperty("PropA"); - var protectedProperty = typeInfo.GetDeclaredProperty("PropProtected"); - var publicPropertySetter = PropertyHelper.MakeFastPropertySetter(publicProperty); - var protectedPropertySetter = PropertyHelper.MakeFastPropertySetter(protectedProperty); - - // Act - publicPropertySetter(instance, "TestPublic"); - protectedPropertySetter(instance, "TestProtected"); - - // Assert - Assert.Equal("TestPublic", instance.PropA); - Assert.Equal("TestProtected", instance.GetPropProtected()); - } - - [Fact] - public void MakeFastPropertySetter_SetsPropertyValues_ForOverridenProperties() - { - // Arrange - var instance = new DerivedClassWithOverride(); - var typeInfo = instance.GetType().GetTypeInfo(); - var property = typeInfo.GetDeclaredProperty("PropA"); - var propertySetter = PropertyHelper.MakeFastPropertySetter(property); - - // Act - propertySetter(instance, "Test value"); - - // Assert - Assert.Equal("OverridenTest value", instance.PropA); - } - - [Fact] - public void MakeFastPropertySetter_SetsPropertyValues_ForNewedProperties() - { - // Arrange - var instance = new DerivedClassWithNew(); - var typeInfo = instance.GetType().GetTypeInfo(); - var property = typeInfo.GetDeclaredProperty("PropB"); - var propertySetter = PropertyHelper.MakeFastPropertySetter(property); - - // Act - propertySetter(instance, "Test value"); - - // Assert - Assert.Equal("NewedTest value", instance.PropB); - } - - [Fact] - public void MakeFastPropertyGetter_ReferenceType_ForNullObject_Throws() - { - // Arrange - var property = PropertyHelper - .GetProperties(typeof(BaseClass)) - .Single(p => p.Name == nameof(BaseClass.PropA)); - - var accessor = PropertyHelper.MakeFastPropertyGetter(property.Property); - - // Act & Assert - Assert.Throws(() => accessor(null)); - } - - [Fact] - public void MakeFastPropertyGetter_ValueType_ForNullObject_Throws() - { - // Arrange - var property = PropertyHelper - .GetProperties(typeof(MyProperties)) - .Single(p => p.Name == nameof(MyProperties.StringProp)); - - var accessor = PropertyHelper.MakeFastPropertyGetter(property.Property); - - // Act & Assert - Assert.Throws(() => accessor(null)); - } - - [Fact] - public void MakeNullSafeFastPropertyGetter_ReferenceType_Success() - { - // Arrange - var property = PropertyHelper - .GetProperties(typeof(BaseClass)) - .Single(p => p.Name == nameof(BaseClass.PropA)); - - var accessor = PropertyHelper.MakeNullSafeFastPropertyGetter(property.Property); - - // Act - var value = accessor(new BaseClass() { PropA = "Hi" }); - - // Assert - Assert.Equal("Hi", value); - } - - [Fact] - public void MakeNullSafeFastPropertyGetter_ValueType_Success() - { - // Arrange - var property = PropertyHelper - .GetProperties(typeof(MyProperties)) - .Single(p => p.Name == nameof(MyProperties.StringProp)); - - var accessor = PropertyHelper.MakeNullSafeFastPropertyGetter(property.Property); - - // Act - var value = accessor(new MyProperties() { StringProp = "Hi" }); - - // Assert - Assert.Equal("Hi", value); - } - - [Fact] - public void MakeNullSafeFastPropertyGetter_ReferenceType_ForNullObject_ReturnsNull() - { - // Arrange - var property = PropertyHelper - .GetProperties(typeof(BaseClass)) - .Single(p => p.Name == nameof(BaseClass.PropA)); - - var accessor = PropertyHelper.MakeNullSafeFastPropertyGetter(property.Property); - - // Act - var value = accessor(null); - - // Assert - Assert.Null(value); - } - - [Fact] - public void MakeNullSafeFastPropertyGetter_ValueType_ForNullObject_ReturnsNull() - { - // Arrange - var property = PropertyHelper - .GetProperties(typeof(MyProperties)) - .Single(p => p.Name == nameof(MyProperties.StringProp)); - - var accessor = PropertyHelper.MakeNullSafeFastPropertyGetter(property.Property); - - // Act - var value = accessor(null); - - // Assert - Assert.Null(value); - } - - public static TheoryData> IgnoreCaseTestData - { - get - { - return new TheoryData> - { - { - new - { - selected = true, - SeLeCtEd = false - }, - new KeyValuePair("selected", false) - }, - { - new - { - SeLeCtEd = false, - selected = true - }, - new KeyValuePair("SeLeCtEd", true) - }, - { - new - { - SelECTeD = false, - SeLECTED = true - }, - new KeyValuePair("SelECTeD", true) - } - }; - } - } - - [Theory] - [MemberData(nameof(IgnoreCaseTestData))] - public void ObjectToDictionary_IgnoresPropertyCase(object testObject, - KeyValuePair expectedEntry) - { - // Act - var result = PropertyHelper.ObjectToDictionary(testObject); - - // Assert - var entry = Assert.Single(result); - Assert.Equal(expectedEntry, entry); - } - - [Fact] - public void ObjectToDictionary_WithNullObject_ReturnsEmptyDictionary() - { - // Arrange - object value = null; - - // Act - var dictValues = PropertyHelper.ObjectToDictionary(value); - - // Assert - Assert.NotNull(dictValues); - Assert.Equal(0, dictValues.Count); - } - - [Fact] - public void ObjectToDictionary_WithPlainObjectType_ReturnsEmptyDictionary() - { - // Arrange - var value = new object(); - - // Act - var dictValues = PropertyHelper.ObjectToDictionary(value); - - // Assert - Assert.NotNull(dictValues); - Assert.Equal(0, dictValues.Count); - } - - [Fact] - public void ObjectToDictionary_WithPrimitiveType_LooksUpPublicProperties() - { - // Arrange - var value = "test"; - - // Act - var dictValues = PropertyHelper.ObjectToDictionary(value); - - // Assert - Assert.NotNull(dictValues); - Assert.Equal(1, dictValues.Count); - Assert.Equal(4, dictValues["Length"]); - } - - [Fact] - public void ObjectToDictionary_WithAnonymousType_LooksUpProperties() - { - // Arrange - var value = new { test = "value", other = 1 }; - - // Act - var dictValues = PropertyHelper.ObjectToDictionary(value); - - // Assert - Assert.NotNull(dictValues); - Assert.Equal(2, dictValues.Count); - Assert.Equal("value", dictValues["test"]); - Assert.Equal(1, dictValues["other"]); - } - - [Fact] - public void ObjectToDictionary_ReturnsCaseInsensitiveDictionary() - { - // Arrange - var value = new { TEST = "value", oThEr = 1 }; - - // Act - var dictValues = PropertyHelper.ObjectToDictionary(value); - - // Assert - Assert.NotNull(dictValues); - Assert.Equal(2, dictValues.Count); - Assert.Equal("value", dictValues["test"]); - Assert.Equal(1, dictValues["other"]); - } - - [Fact] - public void ObjectToDictionary_ReturnsInheritedProperties() - { - // Arrange - var value = new ThreeDPoint() { X = 5, Y = 10, Z = 17 }; - - // Act - var dictValues = PropertyHelper.ObjectToDictionary(value); - - // Assert - Assert.NotNull(dictValues); - Assert.Equal(3, dictValues.Count); - Assert.Equal(5, dictValues["X"]); - Assert.Equal(10, dictValues["Y"]); - Assert.Equal(17, dictValues["Z"]); - } - - private class Point - { - public int X { get; set; } - public int Y { get; set; } - } - - private class ThreeDPoint : Point - { - public int Z { get; set; } - } - - private class Static - { - public static int Prop2 { get; set; } - public int Prop5 { get; set; } - } - - private struct MyProperties - { - public int IntProp { get; set; } - public string StringProp { get; set; } - } - - private class SetOnly - { - public int Prop2 { set { } } - public int Prop6 { get; set; } - } - - private class PrivateProperties - { - public int Prop1 { get; set; } - protected int Prop2 { get; set; } - private int Prop3 { get; set; } - } - - public class BaseClass - { - public string PropA { get; set; } - - protected string PropProtected { get; set; } - - public string GetPropProtected() - { - return PropProtected; - } - } - - public class DerivedClass : BaseClass - { - public string PropB { get; set; } - } - - public class BaseClassWithVirtual - { - public virtual string PropA { get; set; } - public string PropB { get; set; } - } - - public class DerivedClassWithNew : BaseClassWithVirtual - { - private string _value = "Newed"; - - public new string PropB - { - get { return _value; } - set { _value = "Newed" + value; } - } - } - - public class DerivedClassWithOverride : BaseClassWithVirtual - { - private string _value = "Overriden"; - - public override string PropA - { - get { return _value; } - set { _value = "Overriden" + value; } - } - } - - private class DerivedClassWithNonReadableProperties : BaseClassWithVirtual - { - public string this[int index] - { - get { return string.Empty; } - set { } - } - - public int Visible { get; set; } - - private string NotVisible { get; set; } - - public string NotVisible2 { private get; set; } - - public string NotVisible3 - { - set { } - } - - public static string NotVisible4 { get; set; } - } - - private struct MyStruct - { - public string Foo { get; set; } - } - - private class BaseHiddenProperty - { - public int Id { get; set; } - } - - private class DerivedHiddenProperty : BaseHiddenProperty - { - public new string Id { get; set; } - - public string Name { get; set; } - } - - private class DerivedHiddenProperty2 : DerivedHiddenProperty - { - public new Guid Id { get; set; } - - public new string Name { get; private set; } - } - } -} From e0051303d1c1147665f88b83ce721294538da131 Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Thu, 13 Dec 2018 11:19:00 -0800 Subject: [PATCH 030/183] Remove Microsoft.Extensions.RazorViews.Sources \n\nCommit migrated from https://github.com/dotnet/extensions/commit/3f5df6ddbc64e49290f969909378b5138e12ce93 --- src/Shared/RazorViews/AttributeValue.cs | 38 --- src/Shared/RazorViews/BaseView.cs | 279 ------------------ src/Shared/RazorViews/HelperResult.cs | 34 --- .../Microsoft.AspNetCore.Shared.Tests.csproj | 1 - 4 files changed, 352 deletions(-) delete mode 100644 src/Shared/RazorViews/AttributeValue.cs delete mode 100644 src/Shared/RazorViews/BaseView.cs delete mode 100644 src/Shared/RazorViews/HelperResult.cs diff --git a/src/Shared/RazorViews/AttributeValue.cs b/src/Shared/RazorViews/AttributeValue.cs deleted file mode 100644 index 7a066a7040b3..000000000000 --- a/src/Shared/RazorViews/AttributeValue.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; - -namespace Microsoft.Extensions.RazorViews -{ - internal class AttributeValue - { - public AttributeValue(string prefix, object value, bool literal) - { - Prefix = prefix; - Value = value; - Literal = literal; - } - - public string Prefix { get; } - - public object Value { get; } - - public bool Literal { get; } - - public static AttributeValue FromTuple(Tuple value) - { - return new AttributeValue(value.Item1, value.Item2, value.Item3); - } - - public static AttributeValue FromTuple(Tuple value) - { - return new AttributeValue(value.Item1, value.Item2, value.Item3); - } - - public static implicit operator AttributeValue(Tuple value) - { - return FromTuple(value); - } - } -} \ No newline at end of file diff --git a/src/Shared/RazorViews/BaseView.cs b/src/Shared/RazorViews/BaseView.cs deleted file mode 100644 index a171d8d1f215..000000000000 --- a/src/Shared/RazorViews/BaseView.cs +++ /dev/null @@ -1,279 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Text; -using System.Text.Encodings.Web; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; - -namespace Microsoft.Extensions.RazorViews -{ - /// - /// Infrastructure - /// - internal abstract class BaseView - { - private static readonly Encoding UTF8NoBOM = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); - private readonly Stack _textWriterStack = new Stack(); - - /// - /// The request context - /// - protected HttpContext Context { get; private set; } - - /// - /// The request - /// - protected HttpRequest Request { get; private set; } - - /// - /// The response - /// - protected HttpResponse Response { get; private set; } - - /// - /// The output stream - /// - protected TextWriter Output { get; private set; } - - /// - /// Html encoder used to encode content. - /// - protected HtmlEncoder HtmlEncoder { get; set; } = HtmlEncoder.Default; - - /// - /// Url encoder used to encode content. - /// - protected UrlEncoder UrlEncoder { get; set; } = UrlEncoder.Default; - - /// - /// JavaScript encoder used to encode content. - /// - protected JavaScriptEncoder JavaScriptEncoder { get; set; } = JavaScriptEncoder.Default; - - /// - /// Execute an individual request - /// - /// - public async Task ExecuteAsync(HttpContext context) - { - Context = context; - Request = Context.Request; - Response = Context.Response; - Output = new StreamWriter(Response.Body, UTF8NoBOM, 4096, leaveOpen: true); - await ExecuteAsync(); - Output.Dispose(); - } - - /// - /// Execute an individual request - /// - public abstract Task ExecuteAsync(); - - protected virtual void PushWriter(TextWriter writer) - { - if (writer == null) - { - throw new ArgumentNullException(nameof(writer)); - } - - _textWriterStack.Push(Output); - Output = writer; - } - - protected virtual TextWriter PopWriter() - { - Output = _textWriterStack.Pop(); - return Output; - } - - /// - /// Write the given value without HTML encoding directly to . - /// - /// The to write. - protected void WriteLiteral(object value) - { - WriteLiteral(Convert.ToString(value, CultureInfo.InvariantCulture)); - } - - /// - /// Write the given value without HTML encoding directly to . - /// - /// The to write. - protected void WriteLiteral(string value) - { - if (!string.IsNullOrEmpty(value)) - { - Output.Write(value); - } - } - - private List AttributeValues { get; set; } - - protected void WriteAttributeValue(string thingy, int startPostion, object value, int endValue, int dealyo, bool yesno) - { - if (AttributeValues == null) - { - AttributeValues = new List(); - } - - AttributeValues.Add(value.ToString()); - } - - private string AttributeEnding { get; set; } - - protected void BeginWriteAttribute(string name, string begining, int startPosition, string ending, int endPosition, int thingy) - { - Debug.Assert(string.IsNullOrEmpty(AttributeEnding)); - - Output.Write(begining); - AttributeEnding = ending; - } - - protected void EndWriteAttribute() - { - Debug.Assert(!string.IsNullOrEmpty(AttributeEnding)); - - var attributes = string.Join(" ", AttributeValues); - Output.Write(attributes); - AttributeValues = null; - - Output.Write(AttributeEnding); - AttributeEnding = null; - } - - /// - /// Writes the given attribute to the given writer - /// - /// The name of the attribute to write - /// The value of the prefix - /// The value of the suffix - /// The s to write. - protected void WriteAttribute( - string name, - string leader, - string trailer, - params AttributeValue[] values) - { - if (name == null) - { - throw new ArgumentNullException(nameof(name)); - } - - if (leader == null) - { - throw new ArgumentNullException(nameof(leader)); - } - - if (trailer == null) - { - throw new ArgumentNullException(nameof(trailer)); - } - - WriteLiteral(leader); - foreach (var value in values) - { - WriteLiteral(value.Prefix); - - // The special cases here are that the value we're writing might already be a string, or that the - // value might be a bool. If the value is the bool 'true' we want to write the attribute name - // instead of the string 'true'. If the value is the bool 'false' we don't want to write anything. - // Otherwise the value is another object (perhaps an HtmlString) and we'll ask it to format itself. - string stringValue; - if (value.Value is bool) - { - if ((bool)value.Value) - { - stringValue = name; - } - else - { - continue; - } - } - else - { - stringValue = value.Value as string; - } - - // Call the WriteTo(string) overload when possible - if (value.Literal && stringValue != null) - { - WriteLiteral(stringValue); - } - else if (value.Literal) - { - WriteLiteral(value.Value); - } - else if (stringValue != null) - { - Write(stringValue); - } - else - { - Write(value.Value); - } - } - WriteLiteral(trailer); - } - - /// - /// is invoked - /// - /// The to invoke - protected void Write(HelperResult result) - { - Write(result); - } - - /// - /// Writes the specified to . - /// - /// The to write. - /// - /// is invoked for types. - /// For all other types, the encoded result of is written to - /// . - /// - protected void Write(object value) - { - if (value is HelperResult helperResult) - { - helperResult.WriteTo(Output); - } - else - { - Write(Convert.ToString(value, CultureInfo.InvariantCulture)); - } - } - - /// - /// Writes the specified with HTML encoding to . - /// - /// The to write. - protected void Write(string value) - { - WriteLiteral(HtmlEncoder.Encode(value)); - } - - protected string HtmlEncodeAndReplaceLineBreaks(string input) - { - if (string.IsNullOrEmpty(input)) - { - return string.Empty; - } - - // Split on line breaks before passing it through the encoder. - return string.Join("
" + Environment.NewLine, - input.Split(new[] { "\r\n" }, StringSplitOptions.None) - .SelectMany(s => s.Split(new[] { '\r', '\n' }, StringSplitOptions.None)) - .Select(HtmlEncoder.Encode)); - } - } -} \ No newline at end of file diff --git a/src/Shared/RazorViews/HelperResult.cs b/src/Shared/RazorViews/HelperResult.cs deleted file mode 100644 index c79944aae646..000000000000 --- a/src/Shared/RazorViews/HelperResult.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.IO; - -namespace Microsoft.Extensions.RazorViews -{ - /// - /// Represents a deferred write operation in a . - /// - internal class HelperResult - { - /// - /// Creates a new instance of . - /// - /// The delegate to invoke when is called. - public HelperResult(Action action) - { - WriteAction = action; - } - - public Action WriteAction { get; } - - /// - /// Method invoked to produce content from the . - /// - /// The instance to write to. - public void WriteTo(TextWriter writer) - { - WriteAction(writer); - } - } -} \ No newline at end of file diff --git a/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj b/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj index 67ff52821bef..014c17aba3c0 100644 --- a/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj +++ b/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj @@ -10,7 +10,6 @@ From 01a4a273d7fcaa1714e249786ee6f88b004de73e Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Thu, 13 Dec 2018 11:26:34 -0800 Subject: [PATCH 031/183] Remove Microsoft.Extensions.SecurityHelper.Sources \n\nCommit migrated from https://github.com/dotnet/extensions/commit/c41abe44afd7890ae81abef88263a9c8a782f68b --- src/Shared/SecurityHelper/SecurityHelper.cs | 40 -------- .../test/Shared.Tests/SecurityHelperTests.cs | 93 ------------------- 2 files changed, 133 deletions(-) delete mode 100644 src/Shared/SecurityHelper/SecurityHelper.cs delete mode 100644 src/Shared/test/Shared.Tests/SecurityHelperTests.cs diff --git a/src/Shared/SecurityHelper/SecurityHelper.cs b/src/Shared/SecurityHelper/SecurityHelper.cs deleted file mode 100644 index 408ef6b22439..000000000000 --- a/src/Shared/SecurityHelper/SecurityHelper.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Linq; -using System.Security.Claims; - -namespace Microsoft.Extensions.Internal -{ - /// - /// Helper code used when implementing authentication middleware - /// - internal static class SecurityHelper - { - /// - /// Add all ClaimsIdentities from an additional ClaimPrincipal to the ClaimsPrincipal - /// Merges a new claims principal, placing all new identities first, and eliminating - /// any empty unauthenticated identities from context.User - /// - /// The containing existing . - /// The containing to be added. - public static ClaimsPrincipal MergeUserPrincipal(ClaimsPrincipal existingPrincipal, ClaimsPrincipal additionalPrincipal) - { - var newPrincipal = new ClaimsPrincipal(); - - // New principal identities go first - if (additionalPrincipal != null) - { - newPrincipal.AddIdentities(additionalPrincipal.Identities); - } - - // Then add any existing non empty or authenticated identities - if (existingPrincipal != null) - { - newPrincipal.AddIdentities(existingPrincipal.Identities.Where(i => i.IsAuthenticated || i.Claims.Any())); - } - return newPrincipal; - } - } -} diff --git a/src/Shared/test/Shared.Tests/SecurityHelperTests.cs b/src/Shared/test/Shared.Tests/SecurityHelperTests.cs deleted file mode 100644 index 8e7515ad36e7..000000000000 --- a/src/Shared/test/Shared.Tests/SecurityHelperTests.cs +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Linq; -using System.Security.Claims; -using System.Security.Principal; -using Xunit; - -namespace Microsoft.Extensions.Internal -{ - public class SecurityHelperTests - { - [Fact] - public void AddingToAnonymousIdentityDoesNotKeepAnonymousIdentity() - { - var user = SecurityHelper.MergeUserPrincipal(new ClaimsPrincipal(), new GenericPrincipal(new GenericIdentity("Test1", "Alpha"), new string[0])); - - Assert.NotNull(user); - Assert.Equal("Alpha", user.Identity.AuthenticationType); - Assert.Equal("Test1", user.Identity.Name); - Assert.IsAssignableFrom(user); - Assert.IsAssignableFrom(user.Identity); - Assert.Single(user.Identities); - } - - [Fact] - public void AddingExistingIdentityChangesDefaultButPreservesPrior() - { - ClaimsPrincipal user = new GenericPrincipal(new GenericIdentity("Test1", "Alpha"), null); - - Assert.Equal("Alpha", user.Identity.AuthenticationType); - Assert.Equal("Test1", user.Identity.Name); - - user = SecurityHelper.MergeUserPrincipal(user, new GenericPrincipal(new GenericIdentity("Test2", "Beta"), new string[0])); - - Assert.Equal("Beta", user.Identity.AuthenticationType); - Assert.Equal("Test2", user.Identity.Name); - - user = SecurityHelper.MergeUserPrincipal(user, new GenericPrincipal(new GenericIdentity("Test3", "Gamma"), new string[0])); - - Assert.Equal("Gamma", user.Identity.AuthenticationType); - Assert.Equal("Test3", user.Identity.Name); - - Assert.Equal(3, user.Identities.Count()); - Assert.Equal("Test3", user.Identities.Skip(0).First().Name); - Assert.Equal("Test2", user.Identities.Skip(1).First().Name); - Assert.Equal("Test1", user.Identities.Skip(2).First().Name); - } - - [Fact] - public void AddingPreservesNewIdentitiesAndDropsEmpty() - { - var existingPrincipal = new ClaimsPrincipal(new ClaimsIdentity()); - var identityNoAuthTypeWithClaim = new ClaimsIdentity(); - identityNoAuthTypeWithClaim.AddClaim(new Claim("identityNoAuthTypeWithClaim", "yes")); - existingPrincipal.AddIdentity(identityNoAuthTypeWithClaim); - var identityEmptyWithAuthType = new ClaimsIdentity("empty"); - existingPrincipal.AddIdentity(identityEmptyWithAuthType); - - Assert.False(existingPrincipal.Identity.IsAuthenticated); - - var newPrincipal = new ClaimsPrincipal(); - var newEmptyIdentity = new ClaimsIdentity(); - var identityTwo = new ClaimsIdentity("yep"); - newPrincipal.AddIdentity(newEmptyIdentity); - newPrincipal.AddIdentity(identityTwo); - - var user = SecurityHelper.MergeUserPrincipal(existingPrincipal, newPrincipal); - - // Preserve newPrincipal order - Assert.False(user.Identity.IsAuthenticated); - Assert.Null(user.Identity.Name); - - Assert.Equal(4, user.Identities.Count()); - Assert.Equal(newEmptyIdentity, user.Identities.Skip(0).First()); - Assert.Equal(identityTwo, user.Identities.Skip(1).First()); - Assert.Equal(identityNoAuthTypeWithClaim, user.Identities.Skip(2).First()); - Assert.Equal(identityEmptyWithAuthType, user.Identities.Skip(3).First()); - - // This merge should drop newEmptyIdentity since its empty - user = SecurityHelper.MergeUserPrincipal(user, new GenericPrincipal(new GenericIdentity("Test3", "Gamma"), new string[0])); - - Assert.Equal("Gamma", user.Identity.AuthenticationType); - Assert.Equal("Test3", user.Identity.Name); - - Assert.Equal(4, user.Identities.Count()); - Assert.Equal("Test3", user.Identities.Skip(0).First().Name); - Assert.Equal(identityTwo, user.Identities.Skip(1).First()); - Assert.Equal(identityNoAuthTypeWithClaim, user.Identities.Skip(2).First()); - Assert.Equal(identityEmptyWithAuthType, user.Identities.Skip(3).First()); - } - } -} From cb018faf533439473089e227268c8f69a9c44b76 Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Thu, 13 Dec 2018 11:37:00 -0800 Subject: [PATCH 032/183] Remove Microsoft.Extensions.StackTrace.Sources \n\nCommit migrated from https://github.com/dotnet/extensions/commit/1713ada32d3543e17350381f8477ce40715bfd59 --- .../ExceptionDetails/ExceptionDetails.cs | 29 -- .../ExceptionDetailsProvider.cs | 170 --------- .../StackFrame/MethodDisplayInfo.cs | 49 --- .../StackFrame/ParameterDisplayInfo.cs | 33 -- .../StackFrame/PortablePdbReader.cs | 135 ------- .../StackTrace/StackFrame/StackFrameInfo.cs | 18 - .../StackFrame/StackFrameSourceCodeInfo.cs | 54 --- .../StackTrace/StackFrame/StackTraceHelper.cs | 261 ------------- .../Microsoft.AspNetCore.Shared.Tests.csproj | 7 - src/Shared/test/Shared.Tests/Readme.txt | 4 - .../test/Shared.Tests/StackTraceHelperTest.cs | 345 ------------------ .../testassets/ThrowingLibrary/Thrower.cs | 20 - .../ThrowingLibrary/ThrowingLibrary.csproj | 8 - 13 files changed, 1133 deletions(-) delete mode 100644 src/Shared/StackTrace/ExceptionDetails/ExceptionDetails.cs delete mode 100644 src/Shared/StackTrace/ExceptionDetails/ExceptionDetailsProvider.cs delete mode 100644 src/Shared/StackTrace/StackFrame/MethodDisplayInfo.cs delete mode 100644 src/Shared/StackTrace/StackFrame/ParameterDisplayInfo.cs delete mode 100644 src/Shared/StackTrace/StackFrame/PortablePdbReader.cs delete mode 100644 src/Shared/StackTrace/StackFrame/StackFrameInfo.cs delete mode 100644 src/Shared/StackTrace/StackFrame/StackFrameSourceCodeInfo.cs delete mode 100644 src/Shared/StackTrace/StackFrame/StackTraceHelper.cs delete mode 100644 src/Shared/test/Shared.Tests/Readme.txt delete mode 100644 src/Shared/test/Shared.Tests/StackTraceHelperTest.cs delete mode 100644 src/Shared/test/testassets/ThrowingLibrary/Thrower.cs delete mode 100644 src/Shared/test/testassets/ThrowingLibrary/ThrowingLibrary.csproj diff --git a/src/Shared/StackTrace/ExceptionDetails/ExceptionDetails.cs b/src/Shared/StackTrace/ExceptionDetails/ExceptionDetails.cs deleted file mode 100644 index 8862611136e7..000000000000 --- a/src/Shared/StackTrace/ExceptionDetails/ExceptionDetails.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; - -namespace Microsoft.Extensions.StackTrace.Sources -{ - /// - /// Contains details for individual exception messages. - /// - internal class ExceptionDetails - { - /// - /// An individual exception - /// - public Exception Error { get; set; } - - /// - /// The generated stack frames - /// - public IEnumerable StackFrames { get; set; } - - /// - /// Gets or sets the summary message. - /// - public string ErrorMessage { get; set; } - } -} diff --git a/src/Shared/StackTrace/ExceptionDetails/ExceptionDetailsProvider.cs b/src/Shared/StackTrace/ExceptionDetails/ExceptionDetailsProvider.cs deleted file mode 100644 index 2d1dd20710ba..000000000000 --- a/src/Shared/StackTrace/ExceptionDetails/ExceptionDetailsProvider.cs +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using Microsoft.Extensions.FileProviders; - -namespace Microsoft.Extensions.StackTrace.Sources -{ - internal class ExceptionDetailsProvider - { - private readonly IFileProvider _fileProvider; - private readonly int _sourceCodeLineCount; - - public ExceptionDetailsProvider(IFileProvider fileProvider, int sourceCodeLineCount) - { - _fileProvider = fileProvider; - _sourceCodeLineCount = sourceCodeLineCount; - } - - public IEnumerable GetDetails(Exception exception) - { - var exceptions = FlattenAndReverseExceptionTree(exception); - - foreach (var ex in exceptions) - { - yield return new ExceptionDetails - { - Error = ex, - StackFrames = StackTraceHelper.GetFrames(ex) - .Select(frame => GetStackFrameSourceCodeInfo( - frame.MethodDisplayInfo.ToString(), - frame.FilePath, - frame.LineNumber)) - }; - } - } - - private static IEnumerable FlattenAndReverseExceptionTree(Exception ex) - { - // ReflectionTypeLoadException is special because the details are in - // the LoaderExceptions property - var typeLoadException = ex as ReflectionTypeLoadException; - if (typeLoadException != null) - { - var typeLoadExceptions = new List(); - foreach (var loadException in typeLoadException.LoaderExceptions) - { - typeLoadExceptions.AddRange(FlattenAndReverseExceptionTree(loadException)); - } - - typeLoadExceptions.Add(ex); - return typeLoadExceptions; - } - - var list = new List(); - if (ex is AggregateException aggregateException) - { - list.Add(ex); - foreach (var innerException in aggregateException.Flatten().InnerExceptions) - { - list.Add(innerException); - } - } - - else - { - while (ex != null) - { - list.Add(ex); - ex = ex.InnerException; - } - list.Reverse(); - } - - return list; - } - - // make it internal to enable unit testing - internal StackFrameSourceCodeInfo GetStackFrameSourceCodeInfo(string method, string filePath, int lineNumber) - { - var stackFrame = new StackFrameSourceCodeInfo - { - Function = method, - File = filePath, - Line = lineNumber - }; - - if (string.IsNullOrEmpty(stackFrame.File)) - { - return stackFrame; - } - - IEnumerable lines = null; - if (File.Exists(stackFrame.File)) - { - lines = File.ReadLines(stackFrame.File); - } - else - { - // Handle relative paths and embedded files - var fileInfo = _fileProvider.GetFileInfo(stackFrame.File); - if (fileInfo.Exists) - { - // ReadLines doesn't accept a stream. Use ReadLines as its more efficient - // relative to reading lines via stream reader - if (!string.IsNullOrEmpty(fileInfo.PhysicalPath)) - { - lines = File.ReadLines(fileInfo.PhysicalPath); - } - else - { - lines = ReadLines(fileInfo); - } - } - } - - if (lines != null) - { - ReadFrameContent(stackFrame, lines, stackFrame.Line, stackFrame.Line); - } - - return stackFrame; - } - - // make it internal to enable unit testing - internal void ReadFrameContent( - StackFrameSourceCodeInfo frame, - IEnumerable allLines, - int errorStartLineNumberInFile, - int errorEndLineNumberInFile) - { - // Get the line boundaries in the file to be read and read all these lines at once into an array. - var preErrorLineNumberInFile = Math.Max(errorStartLineNumberInFile - _sourceCodeLineCount, 1); - var postErrorLineNumberInFile = errorEndLineNumberInFile + _sourceCodeLineCount; - var codeBlock = allLines - .Skip(preErrorLineNumberInFile - 1) - .Take(postErrorLineNumberInFile - preErrorLineNumberInFile + 1) - .ToArray(); - - var numOfErrorLines = (errorEndLineNumberInFile - errorStartLineNumberInFile) + 1; - var errorStartLineNumberInArray = errorStartLineNumberInFile - preErrorLineNumberInFile; - - frame.PreContextLine = preErrorLineNumberInFile; - frame.PreContextCode = codeBlock.Take(errorStartLineNumberInArray).ToArray(); - frame.ContextCode = codeBlock - .Skip(errorStartLineNumberInArray) - .Take(numOfErrorLines) - .ToArray(); - frame.PostContextCode = codeBlock - .Skip(errorStartLineNumberInArray + numOfErrorLines) - .ToArray(); - } - - private static IEnumerable ReadLines(IFileInfo fileInfo) - { - using (var reader = new StreamReader(fileInfo.CreateReadStream())) - { - string line; - while ((line = reader.ReadLine()) != null) - { - yield return line; - } - } - } - } -} diff --git a/src/Shared/StackTrace/StackFrame/MethodDisplayInfo.cs b/src/Shared/StackTrace/StackFrame/MethodDisplayInfo.cs deleted file mode 100644 index b1c0ccc18871..000000000000 --- a/src/Shared/StackTrace/StackFrame/MethodDisplayInfo.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Microsoft.Extensions.StackTrace.Sources -{ - internal class MethodDisplayInfo - { - public string DeclaringTypeName { get; set; } - - public string Name { get; set; } - - public string GenericArguments { get; set; } - - public string SubMethod { get; set; } - - public IEnumerable Parameters { get; set; } - - public override string ToString() - { - var builder = new StringBuilder(); - if (!string.IsNullOrEmpty(DeclaringTypeName)) - { - builder - .Append(DeclaringTypeName) - .Append("."); - } - - builder.Append(Name); - builder.Append(GenericArguments); - - builder.Append("("); - builder.Append(string.Join(", ", Parameters.Select(p => p.ToString()))); - builder.Append(")"); - - if (!string.IsNullOrEmpty(SubMethod)) - { - builder.Append("+"); - builder.Append(SubMethod); - builder.Append("()"); - } - - return builder.ToString(); - } - } -} diff --git a/src/Shared/StackTrace/StackFrame/ParameterDisplayInfo.cs b/src/Shared/StackTrace/StackFrame/ParameterDisplayInfo.cs deleted file mode 100644 index 1199a8386d2f..000000000000 --- a/src/Shared/StackTrace/StackFrame/ParameterDisplayInfo.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Text; - -namespace Microsoft.Extensions.StackTrace.Sources -{ - internal class ParameterDisplayInfo - { - public string Name { get; set; } - - public string Type { get; set; } - - public string Prefix { get; set; } - - public override string ToString() - { - var builder = new StringBuilder(); - if (!string.IsNullOrEmpty(Prefix)) - { - builder - .Append(Prefix) - .Append(" "); - } - - builder.Append(Type); - builder.Append(" "); - builder.Append(Name); - - return builder.ToString(); - } - } -} diff --git a/src/Shared/StackTrace/StackFrame/PortablePdbReader.cs b/src/Shared/StackTrace/StackFrame/PortablePdbReader.cs deleted file mode 100644 index ff6a4947f80e..000000000000 --- a/src/Shared/StackTrace/StackFrame/PortablePdbReader.cs +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Reflection; -using System.Reflection.Metadata; -using System.Reflection.Metadata.Ecma335; -using System.Reflection.PortableExecutable; - -namespace Microsoft.Extensions.StackTrace.Sources -{ - internal class PortablePdbReader : IDisposable - { - private readonly Dictionary _cache = - new Dictionary(StringComparer.Ordinal); - - public void PopulateStackFrame(StackFrameInfo frameInfo, MethodBase method, int IlOffset) - { - if (method.Module.Assembly.IsDynamic) - { - return; - } - - var metadataReader = GetMetadataReader(method.Module.Assembly.Location); - - if (metadataReader == null) - { - return; - } - - var methodToken = MetadataTokens.Handle(method.MetadataToken); - - Debug.Assert(methodToken.Kind == HandleKind.MethodDefinition); - - var handle = ((MethodDefinitionHandle)methodToken).ToDebugInformationHandle(); - - if (!handle.IsNil) - { - var methodDebugInfo = metadataReader.GetMethodDebugInformation(handle); - var sequencePoints = methodDebugInfo.GetSequencePoints(); - SequencePoint? bestPointSoFar = null; - - foreach (var point in sequencePoints) - { - if (point.Offset > IlOffset) - { - break; - } - - if (point.StartLine != SequencePoint.HiddenLine) - { - bestPointSoFar = point; - } - } - - if (bestPointSoFar.HasValue) - { - frameInfo.LineNumber = bestPointSoFar.Value.StartLine; - frameInfo.FilePath = metadataReader.GetString(metadataReader.GetDocument(bestPointSoFar.Value.Document).Name); - } - } - } - - private MetadataReader GetMetadataReader(string assemblyPath) - { - MetadataReaderProvider provider = null; - if (!_cache.TryGetValue(assemblyPath, out provider)) - { - var pdbPath = GetPdbPath(assemblyPath); - - if (!string.IsNullOrEmpty(pdbPath) && File.Exists(pdbPath) && IsPortable(pdbPath)) - { - var pdbStream = File.OpenRead(pdbPath); - provider = MetadataReaderProvider.FromPortablePdbStream(pdbStream); - } - - _cache[assemblyPath] = provider; - } - - return provider?.GetMetadataReader(); - } - - private static string GetPdbPath(string assemblyPath) - { - if (string.IsNullOrEmpty(assemblyPath)) - { - return null; - } - - if (File.Exists(assemblyPath)) - { - var peStream = File.OpenRead(assemblyPath); - - using (var peReader = new PEReader(peStream)) - { - foreach (var entry in peReader.ReadDebugDirectory()) - { - if (entry.Type == DebugDirectoryEntryType.CodeView) - { - var codeViewData = peReader.ReadCodeViewDebugDirectoryData(entry); - var peDirectory = Path.GetDirectoryName(assemblyPath); - return Path.Combine(peDirectory, Path.GetFileName(codeViewData.Path)); - } - } - } - } - - return null; - } - - private static bool IsPortable(string pdbPath) - { - using (var pdbStream = File.OpenRead(pdbPath)) - { - return pdbStream.ReadByte() == 'B' && - pdbStream.ReadByte() == 'S' && - pdbStream.ReadByte() == 'J' && - pdbStream.ReadByte() == 'B'; - } - } - - public void Dispose() - { - foreach (var entry in _cache) - { - entry.Value?.Dispose(); - } - - _cache.Clear(); - } - } -} diff --git a/src/Shared/StackTrace/StackFrame/StackFrameInfo.cs b/src/Shared/StackTrace/StackFrame/StackFrameInfo.cs deleted file mode 100644 index ffd91f213c08..000000000000 --- a/src/Shared/StackTrace/StackFrame/StackFrameInfo.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Diagnostics; - -namespace Microsoft.Extensions.StackTrace.Sources -{ - internal class StackFrameInfo - { - public int LineNumber { get; set; } - - public string FilePath { get; set; } - - public StackFrame StackFrame { get; set; } - - public MethodDisplayInfo MethodDisplayInfo { get; set; } - } -} diff --git a/src/Shared/StackTrace/StackFrame/StackFrameSourceCodeInfo.cs b/src/Shared/StackTrace/StackFrame/StackFrameSourceCodeInfo.cs deleted file mode 100644 index 2932e083b132..000000000000 --- a/src/Shared/StackTrace/StackFrame/StackFrameSourceCodeInfo.cs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Collections.Generic; -using System.Linq; - -namespace Microsoft.Extensions.StackTrace.Sources -{ - /// - /// Contains the source code where the exception occurred. - /// - internal class StackFrameSourceCodeInfo - { - /// - /// Function containing instruction - /// - public string Function { get; set; } - - /// - /// File containing the instruction - /// - public string File { get; set; } - - /// - /// The line number of the instruction - /// - public int Line { get; set; } - - /// - /// The line preceding the frame line - /// - public int PreContextLine { get; set; } - - /// - /// Lines of code before the actual error line(s). - /// - public IEnumerable PreContextCode { get; set; } = Enumerable.Empty(); - - /// - /// Line(s) of code responsible for the error. - /// - public IEnumerable ContextCode { get; set; } = Enumerable.Empty(); - - /// - /// Lines of code after the actual error line(s). - /// - public IEnumerable PostContextCode { get; set; } = Enumerable.Empty(); - - /// - /// Specific error details for this stack frame. - /// - public string ErrorDetails { get; set; } - } -} diff --git a/src/Shared/StackTrace/StackFrame/StackTraceHelper.cs b/src/Shared/StackTrace/StackFrame/StackTraceHelper.cs deleted file mode 100644 index 5ce9a4090387..000000000000 --- a/src/Shared/StackTrace/StackFrame/StackTraceHelper.cs +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.ExceptionServices; -using Microsoft.Extensions.Internal; - -namespace Microsoft.Extensions.StackTrace.Sources -{ - internal class StackTraceHelper - { - public static IList GetFrames(Exception exception) - { - var frames = new List(); - - if (exception == null) - { - return frames; - } - - using (var portablePdbReader = new PortablePdbReader()) - { - var needFileInfo = true; - var stackTrace = new System.Diagnostics.StackTrace(exception, needFileInfo); - var stackFrames = stackTrace.GetFrames(); - - if (stackFrames == null) - { - return frames; - } - - for (var i = 0; i < stackFrames.Length; i++) - { - var frame = stackFrames[i]; - var method = frame.GetMethod(); - - // Always show last stackFrame - if (!ShowInStackTrace(method) && i < stackFrames.Length - 1) - { - continue; - } - - var stackFrame = new StackFrameInfo - { - StackFrame = frame, - FilePath = frame.GetFileName(), - LineNumber = frame.GetFileLineNumber(), - MethodDisplayInfo = GetMethodDisplayString(frame.GetMethod()), - }; - - if (string.IsNullOrEmpty(stackFrame.FilePath)) - { - // .NET Framework and older versions of mono don't support portable PDBs - // so we read it manually to get file name and line information - portablePdbReader.PopulateStackFrame(stackFrame, method, frame.GetILOffset()); - } - - frames.Add(stackFrame); - } - - return frames; - } - } - - internal static MethodDisplayInfo GetMethodDisplayString(MethodBase method) - { - // Special case: no method available - if (method == null) - { - return null; - } - - var methodDisplayInfo = new MethodDisplayInfo(); - - // Type name - var type = method.DeclaringType; - - var methodName = method.Name; - - if (type != null && type.IsDefined(typeof(CompilerGeneratedAttribute)) && - (typeof(IAsyncStateMachine).IsAssignableFrom(type) || typeof(IEnumerator).IsAssignableFrom(type))) - { - // Convert StateMachine methods to correct overload +MoveNext() - if (TryResolveStateMachineMethod(ref method, out type)) - { - methodDisplayInfo.SubMethod = methodName; - } - } - // ResolveStateMachineMethod may have set declaringType to null - if (type != null) - { - methodDisplayInfo.DeclaringTypeName = TypeNameHelper.GetTypeDisplayName(type, includeGenericParameterNames: true); - } - - // Method name - methodDisplayInfo.Name = method.Name; - if (method.IsGenericMethod) - { - var genericArguments = string.Join(", ", method.GetGenericArguments() - .Select(arg => TypeNameHelper.GetTypeDisplayName(arg, fullName: false, includeGenericParameterNames: true))); - methodDisplayInfo.GenericArguments += "<" + genericArguments + ">"; - } - - // Method parameters - methodDisplayInfo.Parameters = method.GetParameters().Select(parameter => - { - var parameterType = parameter.ParameterType; - - var prefix = string.Empty; - if (parameter.IsOut) - { - prefix = "out"; - } - else if (parameterType != null && parameterType.IsByRef) - { - prefix = "ref"; - } - - var parameterTypeString = "?"; - if (parameterType != null) - { - if (parameterType.IsByRef) - { - parameterType = parameterType.GetElementType(); - } - - parameterTypeString = TypeNameHelper.GetTypeDisplayName(parameterType, fullName: false, includeGenericParameterNames: true); - } - - return new ParameterDisplayInfo - { - Prefix = prefix, - Name = parameter.Name, - Type = parameterTypeString, - }; - }); - - return methodDisplayInfo; - } - - private static bool ShowInStackTrace(MethodBase method) - { - Debug.Assert(method != null); - - // Don't show any methods marked with the StackTraceHiddenAttribute - // https://github.com/dotnet/coreclr/pull/14652 - if (HasStackTraceHiddenAttribute(method)) - { - return false; - } - - - var type = method.DeclaringType; - if (type == null) - { - return true; - } - - if (HasStackTraceHiddenAttribute(type)) - { - return false; - } - - // Fallbacks for runtime pre-StackTraceHiddenAttribute - if (type == typeof(ExceptionDispatchInfo) && method.Name == "Throw") - { - return false; - } - else if (type == typeof(TaskAwaiter) || - type == typeof(TaskAwaiter<>) || - type == typeof(ConfiguredTaskAwaitable.ConfiguredTaskAwaiter) || - type == typeof(ConfiguredTaskAwaitable<>.ConfiguredTaskAwaiter)) - { - switch (method.Name) - { - case "HandleNonSuccessAndDebuggerNotification": - case "ThrowForNonSuccess": - case "ValidateEnd": - case "GetResult": - return false; - } - } - - return true; - } - - private static bool TryResolveStateMachineMethod(ref MethodBase method, out Type declaringType) - { - Debug.Assert(method != null); - Debug.Assert(method.DeclaringType != null); - - declaringType = method.DeclaringType; - - var parentType = declaringType.DeclaringType; - if (parentType == null) - { - return false; - } - - var methods = parentType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly); - if (methods == null) - { - return false; - } - - foreach (var candidateMethod in methods) - { - var attributes = candidateMethod.GetCustomAttributes(); - if (attributes == null) - { - continue; - } - - foreach (var asma in attributes) - { - if (asma.StateMachineType == declaringType) - { - method = candidateMethod; - declaringType = candidateMethod.DeclaringType; - // Mark the iterator as changed; so it gets the + annotation of the original method - // async statemachines resolve directly to their builder methods so aren't marked as changed - return asma is IteratorStateMachineAttribute; - } - } - } - - return false; - } - - private static bool HasStackTraceHiddenAttribute(MemberInfo memberInfo) - { - IList attributes; - try - { - // Accessing MembmerInfo.GetCustomAttributesData throws for some types (such as types in dynamically generated assemblies). - // We'll skip looking up StackTraceHiddenAttributes on such types. - attributes = memberInfo.GetCustomAttributesData(); - } - catch - { - return false; - } - - for (var i = 0; i < attributes.Count; i++) - { - if (attributes[i].AttributeType.Name == "StackTraceHiddenAttribute") - { - return true; - } - } - - return false; - } - } -} diff --git a/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj b/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj index 014c17aba3c0..1873e485a974 100644 --- a/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj +++ b/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj @@ -2,7 +2,6 @@ $(StandardTestTfms) - portable true @@ -12,15 +11,9 @@ ..\src\BenchmarkRunner\**\*.cs; ..\src\StackTrace\ExceptionDetails\**\*.cs; " /> -
- - - - - diff --git a/src/Shared/test/Shared.Tests/Readme.txt b/src/Shared/test/Shared.Tests/Readme.txt deleted file mode 100644 index b818bd814800..000000000000 --- a/src/Shared/test/Shared.Tests/Readme.txt +++ /dev/null @@ -1,4 +0,0 @@ -NOTE: -1. The tests for 'ExceptionDetailProvider' and 'StackTraceHelper' in project 'Microsoft.Extensions.StackTrace.Sources' are located in Diagnostics - repo. This is because they refer to some packages from FileSystem repo which causes a circular reference and breaks the - build. \ No newline at end of file diff --git a/src/Shared/test/Shared.Tests/StackTraceHelperTest.cs b/src/Shared/test/Shared.Tests/StackTraceHelperTest.cs deleted file mode 100644 index 657a310b6e24..000000000000 --- a/src/Shared/test/Shared.Tests/StackTraceHelperTest.cs +++ /dev/null @@ -1,345 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; -using Microsoft.Extensions.StackTrace.Sources; -using ThrowingLibrary; -using Xunit; - -namespace Microsoft.Extensions.Internal -{ - public class StackTraceHelperTest - { - [Fact] - public void StackTraceHelper_IncludesLineNumbersForFiles() - { - // Arrange - Exception exception = null; - try - { - // Throwing an exception in the current assembly always seems to populate the full stack - // trace regardless of symbol type. Crossing assembly boundaries ensures PortablePdbReader gets used - // on desktop. - Thrower.Throw(); - } - catch (Exception ex) - { - exception = ex; - } - - // Act - var stackFrames = StackTraceHelper.GetFrames(exception); - - // Assert - Assert.Collection(stackFrames, - frame => - { - Assert.Contains("Thrower.cs", frame.FilePath); - Assert.Equal(17, frame.LineNumber); - }, - frame => - { - Assert.Contains("StackTraceHelperTest.cs", frame.FilePath); - }); - } - - [Fact] - public void StackTraceHelper_PrettyPrintsStackTraceForGenericMethods() - { - // Arrange - var exception = Record.Exception(() => GenericMethod(null)); - - // Act - var stackFrames = StackTraceHelper.GetFrames(exception); - - // Assert - var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray(); - Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.GenericMethod(T val)", methods[0]); - } - - [Fact] - public void StackTraceHelper_PrettyPrintsStackTraceForMethodsWithOutParameters() - { - // Arrange - var exception = Record.Exception(() => MethodWithOutParameter(out var value)); - - // Act - var stackFrames = StackTraceHelper.GetFrames(exception); - - // Assert - var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray(); - Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.MethodWithOutParameter(out int value)", methods[0]); - } - - [Fact] - public void StackTraceHelper_PrettyPrintsStackTraceForMethodsWithGenericOutParameters() - { - // Arrange - var exception = Record.Exception(() => MethodWithGenericOutParameter("Test", out int value)); - - // Act - var stackFrames = StackTraceHelper.GetFrames(exception); - - // Assert - var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray(); - Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.MethodWithGenericOutParameter(string a, out TVal value)", methods[0]); - } - - [Fact] - public void StackTraceHelper_PrettyPrintsStackTraceForMethodsWithRefParameters() - { - // Arrange - var value = 0; - var exception = Record.Exception(() => MethodWithRefParameter(ref value)); - - // Act - var stackFrames = StackTraceHelper.GetFrames(exception); - - // Assert - var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray(); - Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.MethodWithRefParameter(ref int value)", methods[0]); - } - - [Fact] - public void StackTraceHelper_PrettyPrintsStackTraceForMethodsWithGenericRefParameters() - { - // Arrange - var value = 0; - var exception = Record.Exception(() => MethodWithGenericRefParameter(ref value)); - - // Act - var stackFrames = StackTraceHelper.GetFrames(exception); - - // Assert - var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray(); - Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.MethodWithGenericRefParameter(ref TVal value)", methods[0]); - } - - [Fact] - public void StackTraceHelper_PrettyPrintsStackTraceForMethodsWithNullableParameters() - { - // Arrange - var value = 0; - var exception = Record.Exception(() => MethodWithNullableParameter(value)); - - // Act - var stackFrames = StackTraceHelper.GetFrames(exception); - - // Assert - var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray(); - Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.MethodWithNullableParameter(Nullable value)", methods[0]); - } - - [Fact] - public void StackTraceHelper_PrettyPrintsStackTraceForMethodsOnGenericTypes() - { - // Arrange - var exception = Record.Exception(() => new GenericClass().Throw(0)); - - // Act - var stackFrames = StackTraceHelper.GetFrames(exception); - - // Assert - var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray(); - Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest+GenericClass.Throw(T parameter)", methods[0]); - } - - [Fact] - public void StackTraceHelper_ProducesReadableOutput() - { - // Arrange - var expectedCallStack = new List() - { - "Microsoft.Extensions.Internal.StackTraceHelperTest.Iterator()+MoveNext()", - "string.Join(string separator, IEnumerable values)", - "Microsoft.Extensions.Internal.StackTraceHelperTest+GenericClass.GenericMethod(ref V value)", - "Microsoft.Extensions.Internal.StackTraceHelperTest.MethodAsync(int value)", - "Microsoft.Extensions.Internal.StackTraceHelperTest.MethodAsync(TValue value)", - "Microsoft.Extensions.Internal.StackTraceHelperTest.Method(string value)", - "Microsoft.Extensions.Internal.StackTraceHelperTest.StackTraceHelper_ProducesReadableOutput()", - }; - - Exception exception = null; - try - { - Method("test"); - } - catch (Exception ex) - { - exception = ex; - } - - // Act - var stackFrames = StackTraceHelper.GetFrames(exception); - var methodNames = stackFrames.Select(stackFrame => stackFrame.MethodDisplayInfo.ToString()).ToArray(); - - // Assert - Assert.Equal(expectedCallStack, methodNames); - } - - [Fact] - public void StackTraceHelper_DoesNotIncludeInstanceMethodsOnTypesWithStackTraceHiddenAttribute() - { - // Arrange - var exception = Record.Exception(() => InvokeMethodOnTypeWithStackTraceHiddenAttribute()); - - // Act - var stackFrames = StackTraceHelper.GetFrames(exception); - - // Assert - var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray(); - Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.ThrowCore()", methods[0]); - Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.InvokeMethodOnTypeWithStackTraceHiddenAttribute()", methods[1]); - } - - [Fact] - public void StackTraceHelper_DoesNotIncludeStaticMethodsOnTypesWithStackTraceHiddenAttribute() - { - // Arrange - var exception = Record.Exception(() => InvokeStaticMethodOnTypeWithStackTraceHiddenAttribute()); - - // Act - var stackFrames = StackTraceHelper.GetFrames(exception); - - // Assert - var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray(); - Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.ThrowCore()", methods[0]); - Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.InvokeStaticMethodOnTypeWithStackTraceHiddenAttribute()", methods[1]); - } - - [Fact] - public void StackTraceHelper_DoesNotIncludeMethodsWithStackTraceHiddenAttribute() - { - // Arrange - var exception = Record.Exception(() => new TypeWithMethodWithStackTraceHiddenAttribute().Throw()); - - // Act - var stackFrames = StackTraceHelper.GetFrames(exception); - - // Assert - var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray(); - Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.ThrowCore()", methods[0]); - Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest+TypeWithMethodWithStackTraceHiddenAttribute.Throw()", methods[1]); - } - - [Fact] - public void GetFrames_DoesNotFailForDynamicallyGeneratedAssemblies() - { - // Arrange - var action = (Action)Expression.Lambda( - Expression.Throw( - Expression.New(typeof(Exception)))).Compile(); - var exception = Record.Exception(action); - - // Act - var frames = StackTraceHelper.GetFrames(exception).ToArray(); - - // Assert - var frame = frames[0]; - Assert.Null(frame.FilePath); - Assert.Equal($"lambda_method(Closure )", frame.MethodDisplayInfo.ToString()); - } - - [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] - async Task MethodAsync(int value) - { - await Task.Delay(0); - return GenericClass.GenericMethod(ref value); - } - - [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] - async Task MethodAsync(TValue value) - { - return await MethodAsync(1); - } - - [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] - string Method(string value) - { - return MethodAsync(value).GetAwaiter().GetResult(); - } - - [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] - static IEnumerable Iterator() - { - yield return "Success"; - throw new Exception(); - } - - [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] - void MethodWithOutParameter(out int value) => throw new Exception(); - - [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] - void MethodWithGenericOutParameter(string a, out TVal value) => throw new Exception(); - - [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] - void MethodWithRefParameter(ref int value) => throw new Exception(); - - [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] - void MethodWithGenericRefParameter(ref TVal value) => throw new Exception(); - - [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] - void MethodWithNullableParameter(int? value) => throw new Exception(); - - [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] - void InvokeMethodOnTypeWithStackTraceHiddenAttribute() => new TypeWithStackTraceHiddenAttribute().Throw(); - - [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] - void InvokeStaticMethodOnTypeWithStackTraceHiddenAttribute() => TypeWithStackTraceHiddenAttribute.ThrowStatic(); - - class GenericClass - { - [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] - public static string GenericMethod(ref V value) - { - var returnVal = ""; - for (var i = 0; i < 10; i++) - { - returnVal += string.Join(", ", Iterator()); - } - return returnVal; - } - - [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] - public void Throw(T parameter) => throw new Exception(); - } - - [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] - private void GenericMethod(T val) where T : class => throw new Exception(); - - private class StackTraceHiddenAttribute : Attribute - { - } - - [StackTraceHidden] - private class TypeWithStackTraceHiddenAttribute - { - [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] - public void Throw() => ThrowCore(); - - [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] - public static void ThrowStatic() => ThrowCore(); - } - - private class TypeWithMethodWithStackTraceHiddenAttribute - { - [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] - [StackTraceHidden] - public void MethodWithStackTraceHiddenAttribute() - { - ThrowCore(); - } - - [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] - public void Throw() => MethodWithStackTraceHiddenAttribute(); - } - - [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] - private static void ThrowCore() => throw new Exception(); - } -} diff --git a/src/Shared/test/testassets/ThrowingLibrary/Thrower.cs b/src/Shared/test/testassets/ThrowingLibrary/Thrower.cs deleted file mode 100644 index babe2387c6ab..000000000000 --- a/src/Shared/test/testassets/ThrowingLibrary/Thrower.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Runtime.CompilerServices; - -namespace ThrowingLibrary -{ - // Throwing an exception in the current assembly always seems to populate the full stack - // trace regardless of symbol type. This type exists to simulate an exception thrown - // across assemblies which is the typical use case for StackTraceHelper. - public static class Thrower - { - [MethodImpl(MethodImplOptions.NoInlining)] - public static void Throw() - { - throw new DivideByZeroException(); - } - } -} diff --git a/src/Shared/test/testassets/ThrowingLibrary/ThrowingLibrary.csproj b/src/Shared/test/testassets/ThrowingLibrary/ThrowingLibrary.csproj deleted file mode 100644 index d77d392873d7..000000000000 --- a/src/Shared/test/testassets/ThrowingLibrary/ThrowingLibrary.csproj +++ /dev/null @@ -1,8 +0,0 @@ - - - - netstandard2.0 - portable - - - From 4fcd97e4d289f6d798878e960c51f6f59641a4a9 Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Thu, 13 Dec 2018 11:41:24 -0800 Subject: [PATCH 033/183] Remove Microsoft.Extensions.WebEncoders.Sources \n\nCommit migrated from https://github.com/dotnet/extensions/commit/e7bb41b9bfaba1219dfba3dfd80f3a1452311a72 --- .../Properties/EncoderResources.cs | 38 -- src/Shared/WebEncoders/WebEncoders.cs | 388 ------------------ .../test/Shared.Tests/WebEncodersTests.cs | 113 ----- 3 files changed, 539 deletions(-) delete mode 100644 src/Shared/WebEncoders/Properties/EncoderResources.cs delete mode 100644 src/Shared/WebEncoders/WebEncoders.cs delete mode 100644 src/Shared/test/Shared.Tests/WebEncodersTests.cs diff --git a/src/Shared/WebEncoders/Properties/EncoderResources.cs b/src/Shared/WebEncoders/Properties/EncoderResources.cs deleted file mode 100644 index 3474ae82c5b7..000000000000 --- a/src/Shared/WebEncoders/Properties/EncoderResources.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Globalization; - -namespace Microsoft.Extensions.WebEncoders.Sources -{ - // TODO using a resx file. project.json, unfortunately, fails to embed resx files when there are also compile items - // in the contentFiles section. Revisit once we convert repos to MSBuild - internal static class EncoderResources - { - /// - /// Invalid {0}, {1} or {2} length. - /// - internal static readonly string WebEncoders_InvalidCountOffsetOrLength = "Invalid {0}, {1} or {2} length."; - - /// - /// Malformed input: {0} is an invalid input length. - /// - internal static readonly string WebEncoders_MalformedInput = "Malformed input: {0} is an invalid input length."; - - /// - /// Invalid {0}, {1} or {2} length. - /// - internal static string FormatWebEncoders_InvalidCountOffsetOrLength(object p0, object p1, object p2) - { - return string.Format(CultureInfo.CurrentCulture, WebEncoders_InvalidCountOffsetOrLength, p0, p1, p2); - } - - /// - /// Malformed input: {0} is an invalid input length. - /// - internal static string FormatWebEncoders_MalformedInput(object p0) - { - return string.Format(CultureInfo.CurrentCulture, WebEncoders_MalformedInput, p0); - } - } -} diff --git a/src/Shared/WebEncoders/WebEncoders.cs b/src/Shared/WebEncoders/WebEncoders.cs deleted file mode 100644 index 17068ae67a55..000000000000 --- a/src/Shared/WebEncoders/WebEncoders.cs +++ /dev/null @@ -1,388 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Diagnostics; -using System.Globalization; -using Microsoft.Extensions.WebEncoders.Sources; - -#if WebEncoders_In_WebUtilities -namespace Microsoft.AspNetCore.WebUtilities -#else -namespace Microsoft.Extensions.Internal -#endif -{ - /// - /// Contains utility APIs to assist with common encoding and decoding operations. - /// -#if WebEncoders_In_WebUtilities - public -#else - internal -#endif - static class WebEncoders - { - private static readonly byte[] EmptyBytes = new byte[0]; - - /// - /// Decodes a base64url-encoded string. - /// - /// The base64url-encoded input to decode. - /// The base64url-decoded form of the input. - /// - /// The input must not contain any whitespace or padding characters. - /// Throws if the input is malformed. - /// - public static byte[] Base64UrlDecode(string input) - { - if (input == null) - { - throw new ArgumentNullException(nameof(input)); - } - - return Base64UrlDecode(input, offset: 0, count: input.Length); - } - - /// - /// Decodes a base64url-encoded substring of a given string. - /// - /// A string containing the base64url-encoded input to decode. - /// The position in at which decoding should begin. - /// The number of characters in to decode. - /// The base64url-decoded form of the input. - /// - /// The input must not contain any whitespace or padding characters. - /// Throws if the input is malformed. - /// - public static byte[] Base64UrlDecode(string input, int offset, int count) - { - if (input == null) - { - throw new ArgumentNullException(nameof(input)); - } - - ValidateParameters(input.Length, nameof(input), offset, count); - - // Special-case empty input - if (count == 0) - { - return EmptyBytes; - } - - // Create array large enough for the Base64 characters, not just shorter Base64-URL-encoded form. - var buffer = new char[GetArraySizeRequiredToDecode(count)]; - - return Base64UrlDecode(input, offset, buffer, bufferOffset: 0, count: count); - } - - /// - /// Decodes a base64url-encoded into a byte[]. - /// - /// A string containing the base64url-encoded input to decode. - /// The position in at which decoding should begin. - /// - /// Scratch buffer to hold the s to decode. Array must be large enough to hold - /// and characters as well as Base64 padding - /// characters. Content is not preserved. - /// - /// - /// The offset into at which to begin writing the s to decode. - /// - /// The number of characters in to decode. - /// The base64url-decoded form of the . - /// - /// The input must not contain any whitespace or padding characters. - /// Throws if the input is malformed. - /// - public static byte[] Base64UrlDecode(string input, int offset, char[] buffer, int bufferOffset, int count) - { - if (input == null) - { - throw new ArgumentNullException(nameof(input)); - } - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - - ValidateParameters(input.Length, nameof(input), offset, count); - if (bufferOffset < 0) - { - throw new ArgumentOutOfRangeException(nameof(bufferOffset)); - } - - if (count == 0) - { - return EmptyBytes; - } - - // Assumption: input is base64url encoded without padding and contains no whitespace. - - var paddingCharsToAdd = GetNumBase64PaddingCharsToAddForDecode(count); - var arraySizeRequired = checked(count + paddingCharsToAdd); - Debug.Assert(arraySizeRequired % 4 == 0, "Invariant: Array length must be a multiple of 4."); - - if (buffer.Length - bufferOffset < arraySizeRequired) - { - throw new ArgumentException( - string.Format( - CultureInfo.CurrentCulture, - EncoderResources.WebEncoders_InvalidCountOffsetOrLength, - nameof(count), - nameof(bufferOffset), - nameof(input)), - nameof(count)); - } - - // Copy input into buffer, fixing up '-' -> '+' and '_' -> '/'. - var i = bufferOffset; - for (var j = offset; i - bufferOffset < count; i++, j++) - { - var ch = input[j]; - if (ch == '-') - { - buffer[i] = '+'; - } - else if (ch == '_') - { - buffer[i] = '/'; - } - else - { - buffer[i] = ch; - } - } - - // Add the padding characters back. - for (; paddingCharsToAdd > 0; i++, paddingCharsToAdd--) - { - buffer[i] = '='; - } - - // Decode. - // If the caller provided invalid base64 chars, they'll be caught here. - return Convert.FromBase64CharArray(buffer, bufferOffset, arraySizeRequired); - } - - /// - /// Gets the minimum char[] size required for decoding of characters - /// with the method. - /// - /// The number of characters to decode. - /// - /// The minimum char[] size required for decoding of characters. - /// - public static int GetArraySizeRequiredToDecode(int count) - { - if (count < 0) - { - throw new ArgumentOutOfRangeException(nameof(count)); - } - - if (count == 0) - { - return 0; - } - - var numPaddingCharsToAdd = GetNumBase64PaddingCharsToAddForDecode(count); - - return checked(count + numPaddingCharsToAdd); - } - - /// - /// Encodes using base64url encoding. - /// - /// The binary input to encode. - /// The base64url-encoded form of . - public static string Base64UrlEncode(byte[] input) - { - if (input == null) - { - throw new ArgumentNullException(nameof(input)); - } - - return Base64UrlEncode(input, offset: 0, count: input.Length); - } - - /// - /// Encodes using base64url encoding. - /// - /// The binary input to encode. - /// The offset into at which to begin encoding. - /// The number of bytes from to encode. - /// The base64url-encoded form of . - public static string Base64UrlEncode(byte[] input, int offset, int count) - { - if (input == null) - { - throw new ArgumentNullException(nameof(input)); - } - - ValidateParameters(input.Length, nameof(input), offset, count); - - // Special-case empty input - if (count == 0) - { - return string.Empty; - } - - var buffer = new char[GetArraySizeRequiredToEncode(count)]; - var numBase64Chars = Base64UrlEncode(input, offset, buffer, outputOffset: 0, count: count); - - return new String(buffer, startIndex: 0, length: numBase64Chars); - } - - /// - /// Encodes using base64url encoding. - /// - /// The binary input to encode. - /// The offset into at which to begin encoding. - /// - /// Buffer to receive the base64url-encoded form of . Array must be large enough to - /// hold characters and the full base64-encoded form of - /// , including padding characters. - /// - /// - /// The offset into at which to begin writing the base64url-encoded form of - /// . - /// - /// The number of bytes from to encode. - /// - /// The number of characters written to , less any padding characters. - /// - public static int Base64UrlEncode(byte[] input, int offset, char[] output, int outputOffset, int count) - { - if (input == null) - { - throw new ArgumentNullException(nameof(input)); - } - if (output == null) - { - throw new ArgumentNullException(nameof(output)); - } - - ValidateParameters(input.Length, nameof(input), offset, count); - if (outputOffset < 0) - { - throw new ArgumentOutOfRangeException(nameof(outputOffset)); - } - - var arraySizeRequired = GetArraySizeRequiredToEncode(count); - if (output.Length - outputOffset < arraySizeRequired) - { - throw new ArgumentException( - string.Format( - CultureInfo.CurrentCulture, - EncoderResources.WebEncoders_InvalidCountOffsetOrLength, - nameof(count), - nameof(outputOffset), - nameof(output)), - nameof(count)); - } - - // Special-case empty input. - if (count == 0) - { - return 0; - } - - // Use base64url encoding with no padding characters. See RFC 4648, Sec. 5. - - // Start with default Base64 encoding. - var numBase64Chars = Convert.ToBase64CharArray(input, offset, count, output, outputOffset); - - // Fix up '+' -> '-' and '/' -> '_'. Drop padding characters. - for (var i = outputOffset; i - outputOffset < numBase64Chars; i++) - { - var ch = output[i]; - if (ch == '+') - { - output[i] = '-'; - } - else if (ch == '/') - { - output[i] = '_'; - } - else if (ch == '=') - { - // We've reached a padding character; truncate the remainder. - return i - outputOffset; - } - } - - return numBase64Chars; - } - - /// - /// Get the minimum output char[] size required for encoding - /// s with the method. - /// - /// The number of characters to encode. - /// - /// The minimum output char[] size required for encoding s. - /// - public static int GetArraySizeRequiredToEncode(int count) - { - var numWholeOrPartialInputBlocks = checked(count + 2) / 3; - return checked(numWholeOrPartialInputBlocks * 4); - } - - private static int GetNumBase64PaddingCharsInString(string str) - { - // Assumption: input contains a well-formed base64 string with no whitespace. - - // base64 guaranteed have 0 - 2 padding characters. - if (str[str.Length - 1] == '=') - { - if (str[str.Length - 2] == '=') - { - return 2; - } - return 1; - } - return 0; - } - - private static int GetNumBase64PaddingCharsToAddForDecode(int inputLength) - { - switch (inputLength % 4) - { - case 0: - return 0; - case 2: - return 2; - case 3: - return 1; - default: - throw new FormatException( - string.Format( - CultureInfo.CurrentCulture, - EncoderResources.WebEncoders_MalformedInput, - inputLength)); - } - } - - private static void ValidateParameters(int bufferLength, string inputName, int offset, int count) - { - if (offset < 0) - { - throw new ArgumentOutOfRangeException(nameof(offset)); - } - if (count < 0) - { - throw new ArgumentOutOfRangeException(nameof(count)); - } - if (bufferLength - offset < count) - { - throw new ArgumentException( - string.Format( - CultureInfo.CurrentCulture, - EncoderResources.WebEncoders_InvalidCountOffsetOrLength, - nameof(count), - nameof(offset), - inputName), - nameof(count)); - } - } - } -} diff --git a/src/Shared/test/Shared.Tests/WebEncodersTests.cs b/src/Shared/test/Shared.Tests/WebEncodersTests.cs deleted file mode 100644 index 5c71403fd653..000000000000 --- a/src/Shared/test/Shared.Tests/WebEncodersTests.cs +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Linq; -using Xunit; - -namespace Microsoft.Extensions.Internal -{ - public class WebEncodersTests - { - [Theory] - [InlineData("", 1, 0)] - [InlineData("", 0, 1)] - [InlineData("0123456789", 9, 2)] - [InlineData("0123456789", Int32.MaxValue, 2)] - [InlineData("0123456789", 9, -1)] - public void Base64UrlDecode_BadOffsets(string input, int offset, int count) - { - // Act & assert - Assert.ThrowsAny(() => - { - var retVal = WebEncoders.Base64UrlDecode(input, offset, count); - }); - } - - [Theory] - [InlineData("x")] - [InlineData("(x)")] - public void Base64UrlDecode_MalformedInput(string input) - { - // Act & assert - Assert.Throws(() => - { - var retVal = WebEncoders.Base64UrlDecode(input); - }); - } - - [Theory] - [InlineData("", "")] - [InlineData("123456qwerty++//X+/x", "123456qwerty--__X-_x")] - [InlineData("123456qwerty++//X+/xxw==", "123456qwerty--__X-_xxw")] - [InlineData("123456qwerty++//X+/xxw0=", "123456qwerty--__X-_xxw0")] - public void Base64UrlEncode_And_Decode(string base64Input, string expectedBase64Url) - { - // Arrange - byte[] input = new byte[3].Concat(Convert.FromBase64String(base64Input)).Concat(new byte[2]).ToArray(); - - // Act & assert - 1 - string actualBase64Url = WebEncoders.Base64UrlEncode(input, 3, input.Length - 5); // also helps test offsets - Assert.Equal(expectedBase64Url, actualBase64Url); - - // Act & assert - 2 - // Verify that values round-trip - byte[] roundTripped = WebEncoders.Base64UrlDecode("xx" + actualBase64Url + "yyy", 2, actualBase64Url.Length); // also helps test offsets - string roundTrippedAsBase64 = Convert.ToBase64String(roundTripped); - Assert.Equal(roundTrippedAsBase64, base64Input); - } - - [Theory] - [InlineData("", "")] - [InlineData("123456qwerty++//X+/x", "123456qwerty--__X-_x")] - [InlineData("123456qwerty++//X+/xxw==", "123456qwerty--__X-_xxw")] - [InlineData("123456qwerty++//X+/xxw0=", "123456qwerty--__X-_xxw0")] - public void Base64UrlEncode_And_Decode_WithBufferOffsets(string base64Input, string expectedBase64Url) - { - // Arrange - var input = new byte[3].Concat(Convert.FromBase64String(base64Input)).Concat(new byte[2]).ToArray(); - var buffer = new char[30]; - var output = new char[30]; - for (var i = 0; i < buffer.Length; i++) - { - buffer[i] = '^'; - output[i] = '^'; - } - - // Act 1 - var numEncodedChars = - WebEncoders.Base64UrlEncode(input, offset: 3, output: output, outputOffset: 4, count: input.Length - 5); - - // Assert 1 - var encodedString = new string(output, startIndex: 4, length: numEncodedChars); - Assert.Equal(expectedBase64Url, encodedString); - - // Act 2 - var roundTripInput = new string(output); - var roundTripped = - WebEncoders.Base64UrlDecode(roundTripInput, offset: 4, buffer: buffer, bufferOffset: 5, count: numEncodedChars); - - // Assert 2, verify that values round-trip - var roundTrippedAsBase64 = Convert.ToBase64String(roundTripped); - Assert.Equal(roundTrippedAsBase64, base64Input); - } - - [Theory] - [InlineData(0, 1, 0)] - [InlineData(0, 0, 1)] - [InlineData(10, 9, 2)] - [InlineData(10, Int32.MaxValue, 2)] - [InlineData(10, 9, -1)] - public void Base64UrlEncode_BadOffsets(int inputLength, int offset, int count) - { - // Arrange - byte[] input = new byte[inputLength]; - - // Act & assert - Assert.ThrowsAny(() => - { - var retVal = WebEncoders.Base64UrlEncode(input, offset, count); - }); - } - } -} From 6d45c068073e7826fafe89e4b6d447763e41d14f Mon Sep 17 00:00:00 2001 From: Arthur Vickers Date: Fri, 7 Dec 2018 11:58:10 -0800 Subject: [PATCH 034/183] Method to get source for each configuration element Fixes dotnet/extensions#609 by allowing the configuration paths to be associated with values to help with debugging. \n\nCommit migrated from https://github.com/dotnet/extensions/commit/d7f8e253d414ce6053ad59b6f974621d5620c0da --- .../src/KeyPerFileConfigurationProvider.cs | 16 +- .../test/ConfigurationProviderTestBase.cs | 325 ++++++++++++++---- .../test/KeyPerFileTests.cs | 81 +---- 3 files changed, 295 insertions(+), 127 deletions(-) diff --git a/src/Configuration.KeyPerFile/src/KeyPerFileConfigurationProvider.cs b/src/Configuration.KeyPerFile/src/KeyPerFileConfigurationProvider.cs index 474889574411..13541110e637 100644 --- a/src/Configuration.KeyPerFile/src/KeyPerFileConfigurationProvider.cs +++ b/src/Configuration.KeyPerFile/src/KeyPerFileConfigurationProvider.cs @@ -39,10 +39,8 @@ public override void Load() { return; } - else - { - throw new DirectoryNotFoundException("A non-null file provider for the directory is required when this source is not optional."); - } + + throw new DirectoryNotFoundException("A non-null file provider for the directory is required when this source is not optional."); } var directory = Source.FileProvider.GetDirectoryContents("/"); @@ -68,5 +66,15 @@ public override void Load() } } } + + private string GetDirectoryName() + => Source.FileProvider?.GetFileInfo("/")?.PhysicalPath ?? ""; + + /// + /// Generates a string representing this provider name and relevant details. + /// + /// The configuration name. + public override string ToString() + => $"{GetType().Name} for files in '{GetDirectoryName()}' ({(Source.Optional ? "Optional" : "Required")})"; } } diff --git a/src/Configuration.KeyPerFile/test/ConfigurationProviderTestBase.cs b/src/Configuration.KeyPerFile/test/ConfigurationProviderTestBase.cs index c18bea8d2851..4609ee2560ff 100644 --- a/src/Configuration.KeyPerFile/test/ConfigurationProviderTestBase.cs +++ b/src/Configuration.KeyPerFile/test/ConfigurationProviderTestBase.cs @@ -14,7 +14,39 @@ public abstract class ConfigurationProviderTestBase [Fact] public virtual void Load_from_single_provider() { - AssertConfig(BuildConfigRoot(LoadThroughProvider(TestSection.TestConfig))); + var configRoot = BuildConfigRoot(LoadThroughProvider(TestSection.TestConfig)); + + AssertConfig(configRoot); + } + + [Fact] + public virtual void Has_debug_view() + { + var configRoot = BuildConfigRoot(LoadThroughProvider(TestSection.TestConfig)); + var providerTag = configRoot.Providers.Single().ToString(); + + var expected = + $@"Key1=Value1 ({providerTag}) +Section1: + Key2=Value12 ({providerTag}) + Section2: + Key3=Value123 ({providerTag}) + Key3a: + 0=ArrayValue0 ({providerTag}) + 1=ArrayValue1 ({providerTag}) + 2=ArrayValue2 ({providerTag}) +Section3: + Section4: + Key4=Value344 ({providerTag}) +"; + + AssertDebugView(configRoot, expected); + } + + [Fact] + public virtual void Null_values_are_included_in_the_config() + { + AssertConfig(BuildConfigRoot(LoadThroughProvider(TestSection.NullsTestConfig)), expectNulls: true, nullValue: ""); } [Fact] @@ -22,8 +54,13 @@ public virtual void Combine_after_other_provider() { AssertConfig( BuildConfigRoot( - LoadUsingMemoryProvider(TestSection.MissingSection2Config), + LoadUsingMemoryProvider(TestSection.MissingSection2ValuesConfig), LoadThroughProvider(TestSection.MissingSection4Config))); + + AssertConfig( + BuildConfigRoot( + LoadUsingMemoryProvider(TestSection.MissingSection4Config), + LoadThroughProvider(TestSection.MissingSection2ValuesConfig))); } [Fact] @@ -31,8 +68,13 @@ public virtual void Combine_before_other_provider() { AssertConfig( BuildConfigRoot( - LoadThroughProvider(TestSection.MissingSection2Config), + LoadThroughProvider(TestSection.MissingSection2ValuesConfig), LoadUsingMemoryProvider(TestSection.MissingSection4Config))); + + AssertConfig( + BuildConfigRoot( + LoadThroughProvider(TestSection.MissingSection4Config), + LoadUsingMemoryProvider(TestSection.MissingSection2ValuesConfig))); } [Fact] @@ -128,48 +170,83 @@ public class Section4AsOptions public string Key4 { get; set; } } - protected virtual void AssertConfig(IConfigurationRoot config) + protected virtual void AssertDebugView( + IConfigurationRoot config, + string expected) + { + string RemoveLineEnds(string source) => source.Replace("\n", "").Replace("\r", ""); + + var actual = config.GetDebugView(); + + Assert.Equal( + RemoveLineEnds(expected), + RemoveLineEnds(actual)); + } + + protected virtual void AssertConfig( + IConfigurationRoot config, + bool expectNulls = false, + string nullValue = null) { - Assert.Equal("Value1", config["Key1"], StringComparer.InvariantCultureIgnoreCase); - Assert.Equal("Value12", config["Section1:Key2"], StringComparer.InvariantCultureIgnoreCase); - Assert.Equal("Value123", config["Section1:Section2:Key3"], StringComparer.InvariantCultureIgnoreCase); - Assert.Equal("ArrayValue0", config["Section1:Section2:Key3a:0"], StringComparer.InvariantCultureIgnoreCase); - Assert.Equal("ArrayValue1", config["Section1:Section2:Key3a:1"], StringComparer.InvariantCultureIgnoreCase); - Assert.Equal("ArrayValue2", config["Section1:Section2:Key3a:2"], StringComparer.InvariantCultureIgnoreCase); - Assert.Equal("Value344", config["Section3:Section4:Key4"], StringComparer.InvariantCultureIgnoreCase); + var value1 = expectNulls ? nullValue : "Value1"; + var value12 = expectNulls ? nullValue : "Value12"; + var value123 = expectNulls ? nullValue : "Value123"; + var arrayvalue0 = expectNulls ? nullValue : "ArrayValue0"; + var arrayvalue1 = expectNulls ? nullValue : "ArrayValue1"; + var arrayvalue2 = expectNulls ? nullValue : "ArrayValue2"; + var value344 = expectNulls ? nullValue : "Value344"; + + Assert.Equal(value1, config["Key1"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal(value12, config["Section1:Key2"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal(value123, config["Section1:Section2:Key3"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal(arrayvalue0, config["Section1:Section2:Key3a:0"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal(arrayvalue1, config["Section1:Section2:Key3a:1"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal(arrayvalue2, config["Section1:Section2:Key3a:2"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal(value344, config["Section3:Section4:Key4"], StringComparer.InvariantCultureIgnoreCase); var section1 = config.GetSection("Section1"); - Assert.Equal("Value12", section1["Key2"], StringComparer.InvariantCultureIgnoreCase); - Assert.Equal("Value123", section1["Section2:Key3"], StringComparer.InvariantCultureIgnoreCase); - Assert.Equal("ArrayValue0", section1["Section2:Key3a:0"], StringComparer.InvariantCultureIgnoreCase); - Assert.Equal("ArrayValue1", section1["Section2:Key3a:1"], StringComparer.InvariantCultureIgnoreCase); - Assert.Equal("ArrayValue2", section1["Section2:Key3a:2"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal(value12, section1["Key2"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal(value123, section1["Section2:Key3"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal(arrayvalue0, section1["Section2:Key3a:0"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal(arrayvalue1, section1["Section2:Key3a:1"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal(arrayvalue2, section1["Section2:Key3a:2"], StringComparer.InvariantCultureIgnoreCase); Assert.Equal("Section1", section1.Path, StringComparer.InvariantCultureIgnoreCase); Assert.Null(section1.Value); var section2 = config.GetSection("Section1:Section2"); - Assert.Equal("Value123", section2["Key3"], StringComparer.InvariantCultureIgnoreCase); - Assert.Equal("ArrayValue0", section2["Key3a:0"], StringComparer.InvariantCultureIgnoreCase); - Assert.Equal("ArrayValue1", section2["Key3a:1"], StringComparer.InvariantCultureIgnoreCase); - Assert.Equal("ArrayValue2", section2["Key3a:2"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal(value123, section2["Key3"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal(arrayvalue0, section2["Key3a:0"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal(arrayvalue1, section2["Key3a:1"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal(arrayvalue2, section2["Key3a:2"], StringComparer.InvariantCultureIgnoreCase); Assert.Equal("Section1:Section2", section2.Path, StringComparer.InvariantCultureIgnoreCase); Assert.Null(section2.Value); section2 = section1.GetSection("Section2"); - Assert.Equal("Value123", section2["Key3"], StringComparer.InvariantCultureIgnoreCase); - Assert.Equal("ArrayValue0", section2["Key3a:0"], StringComparer.InvariantCultureIgnoreCase); - Assert.Equal("ArrayValue1", section2["Key3a:1"], StringComparer.InvariantCultureIgnoreCase); - Assert.Equal("ArrayValue2", section2["Key3a:2"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal(value123, section2["Key3"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal(arrayvalue0, section2["Key3a:0"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal(arrayvalue1, section2["Key3a:1"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal(arrayvalue2, section2["Key3a:2"], StringComparer.InvariantCultureIgnoreCase); Assert.Equal("Section1:Section2", section2.Path, StringComparer.InvariantCultureIgnoreCase); Assert.Null(section2.Value); + var section3a = section2.GetSection("Key3a"); + Assert.Equal(arrayvalue0, section3a["0"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal(arrayvalue1, section3a["1"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal(arrayvalue2, section3a["2"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal("Section1:Section2:Key3a", section3a.Path, StringComparer.InvariantCultureIgnoreCase); + Assert.Null(section3a.Value); + + var section3 = config.GetSection("Section3"); + Assert.Equal("Section3", section3.Path, StringComparer.InvariantCultureIgnoreCase); + Assert.Null(section3.Value); + var section4 = config.GetSection("Section3:Section4"); - Assert.Equal("Value344", section4["Key4"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal(value344, section4["Key4"], StringComparer.InvariantCultureIgnoreCase); Assert.Equal("Section3:Section4", section4.Path, StringComparer.InvariantCultureIgnoreCase); Assert.Null(section4.Value); section4 = config.GetSection("Section3").GetSection("Section4"); - Assert.Equal("Value344", section4["Key4"], StringComparer.InvariantCultureIgnoreCase); + Assert.Equal(value344, section4["Key4"], StringComparer.InvariantCultureIgnoreCase); Assert.Equal("Section3:Section4", section4.Path, StringComparer.InvariantCultureIgnoreCase); Assert.Null(section4.Value); @@ -179,7 +256,7 @@ protected virtual void AssertConfig(IConfigurationRoot config) Assert.Equal("Key1", sections[0].Key, StringComparer.InvariantCultureIgnoreCase); Assert.Equal("Key1", sections[0].Path, StringComparer.InvariantCultureIgnoreCase); - Assert.Equal("Value1", sections[0].Value, StringComparer.InvariantCultureIgnoreCase); + Assert.Equal(value1, sections[0].Value, StringComparer.InvariantCultureIgnoreCase); Assert.Equal("Section1", sections[1].Key, StringComparer.InvariantCultureIgnoreCase); Assert.Equal("Section1", sections[1].Path, StringComparer.InvariantCultureIgnoreCase); @@ -195,11 +272,55 @@ protected virtual void AssertConfig(IConfigurationRoot config) Assert.Equal("Key2", sections[0].Key, StringComparer.InvariantCultureIgnoreCase); Assert.Equal("Section1:Key2", sections[0].Path, StringComparer.InvariantCultureIgnoreCase); - Assert.Equal("Value12", sections[0].Value, StringComparer.InvariantCultureIgnoreCase); + Assert.Equal(value12, sections[0].Value, StringComparer.InvariantCultureIgnoreCase); Assert.Equal("Section2", sections[1].Key, StringComparer.InvariantCultureIgnoreCase); Assert.Equal("Section1:Section2", sections[1].Path, StringComparer.InvariantCultureIgnoreCase); Assert.Null(sections[1].Value); + + sections = section2.GetChildren().ToList(); + + Assert.Equal(2, sections.Count); + + Assert.Equal("Key3", sections[0].Key, StringComparer.InvariantCultureIgnoreCase); + Assert.Equal("Section1:Section2:Key3", sections[0].Path, StringComparer.InvariantCultureIgnoreCase); + Assert.Equal(value123, sections[0].Value, StringComparer.InvariantCultureIgnoreCase); + + Assert.Equal("Key3a", sections[1].Key, StringComparer.InvariantCultureIgnoreCase); + Assert.Equal("Section1:Section2:Key3a", sections[1].Path, StringComparer.InvariantCultureIgnoreCase); + Assert.Null(sections[1].Value); + + sections = section3a.GetChildren().ToList(); + + Assert.Equal(3, sections.Count); + + Assert.Equal("0", sections[0].Key, StringComparer.InvariantCultureIgnoreCase); + Assert.Equal("Section1:Section2:Key3a:0", sections[0].Path, StringComparer.InvariantCultureIgnoreCase); + Assert.Equal(arrayvalue0, sections[0].Value, StringComparer.InvariantCultureIgnoreCase); + + Assert.Equal("1", sections[1].Key, StringComparer.InvariantCultureIgnoreCase); + Assert.Equal("Section1:Section2:Key3a:1", sections[1].Path, StringComparer.InvariantCultureIgnoreCase); + Assert.Equal(arrayvalue1, sections[1].Value, StringComparer.InvariantCultureIgnoreCase); + + Assert.Equal("2", sections[2].Key, StringComparer.InvariantCultureIgnoreCase); + Assert.Equal("Section1:Section2:Key3a:2", sections[2].Path, StringComparer.InvariantCultureIgnoreCase); + Assert.Equal(arrayvalue2, sections[2].Value, StringComparer.InvariantCultureIgnoreCase); + + sections = section3.GetChildren().ToList(); + + Assert.Single(sections); + + Assert.Equal("Section4", sections[0].Key, StringComparer.InvariantCultureIgnoreCase); + Assert.Equal("Section3:Section4", sections[0].Path, StringComparer.InvariantCultureIgnoreCase); + Assert.Null(sections[0].Value); + + sections = section4.GetChildren().ToList(); + + Assert.Single(sections); + + Assert.Equal("Key4", sections[0].Key, StringComparer.InvariantCultureIgnoreCase); + Assert.Equal("Section3:Section4:Key4", sections[0].Path, StringComparer.InvariantCultureIgnoreCase); + Assert.Equal(value344, sections[0].Value, StringComparer.InvariantCultureIgnoreCase); } protected abstract (IConfigurationProvider Provider, Action Initializer) LoadThroughProvider(TestSection testConfig); @@ -272,7 +393,7 @@ public TestKeyValue(string[] values) public IEnumerable<(string Key, string Value)> Expand(string key) { - if (AsString != null) + if (AsArray == null) { yield return (key, AsString); } @@ -310,7 +431,7 @@ protected class TestSection Values = new[] { ("Key3", (TestKeyValue)"Value123"), - ("Key3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"}), + ("Key3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"}) }, }) } @@ -321,11 +442,10 @@ protected class TestSection { ("Section4", new TestSection { - Values = new[] {("Key4", (TestKeyValue)"Value344")}, + Values = new[] {("Key4", (TestKeyValue)"Value344")} }) } - }), - + }) } }; @@ -345,7 +465,7 @@ protected class TestSection Values = new[] { ("Key3", (TestKeyValue)"-----"), - ("Key3a", (TestKeyValue)new[] {"-----------", "-----------", "-----------"}), + ("Key3a", (TestKeyValue)new[] {"-----------", "-----------", "-----------"}) }, }) } @@ -356,15 +476,14 @@ protected class TestSection { ("Section4", new TestSection { - Values = new[] {("Key4", (TestKeyValue)"--------")}, + Values = new[] {("Key4", (TestKeyValue)"--------")} }) } - }), - + }) } }; - public static TestSection MissingSection2Config { get; } + public static TestSection MissingSection2ValuesConfig { get; } = new TestSection { Values = new[] { ("Key1", (TestKeyValue)"Value1") }, @@ -373,6 +492,16 @@ protected class TestSection ("Section1", new TestSection { Values = new[] {("Key2", (TestKeyValue)"Value12")}, + Sections = new[] + { + ("Section2", new TestSection + { + Values = new[] + { + ("Key3a", (TestKeyValue)new[] {"ArrayValue0"}) + }, + }) + } }), ("Section3", new TestSection { @@ -380,11 +509,10 @@ protected class TestSection { ("Section4", new TestSection { - Values = new[] {("Key4", (TestKeyValue)"Value344")}, + Values = new[] {("Key4", (TestKeyValue)"Value344")} }) } - }), - + }) } }; @@ -405,15 +533,12 @@ protected class TestSection Values = new[] { ("Key3", (TestKeyValue)"Value123"), - ("Key3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"}), + ("Key3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"}) }, }) } }), - ("Section3", new TestSection - { - }), - + ("Section3", new TestSection()) } }; @@ -433,7 +558,7 @@ protected class TestSection Values = new[] { ("KeY3", (TestKeyValue)"Value123"), - ("KeY3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"}), + ("KeY3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"}) }, }) } @@ -444,11 +569,10 @@ protected class TestSection { ("SectioN4", new TestSection { - Values = new[] {("KeY4", (TestKeyValue)"Value344")}, + Values = new[] {("KeY4", (TestKeyValue)"Value344")} }) } - }), - + }) } }; @@ -472,7 +596,7 @@ protected class TestSection Values = new[] { ("Key3", (TestKeyValue)"Value123"), - ("Key3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"}), + ("Key3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"}) }, }), ("Section2", new TestSection @@ -480,7 +604,7 @@ protected class TestSection Values = new[] { ("Key3", (TestKeyValue)"Value123"), - ("Key3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"}), + ("Key3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"}) }, }) @@ -492,11 +616,10 @@ protected class TestSection { ("Section4", new TestSection { - Values = new[] {("Key4", (TestKeyValue)"Value344")}, + Values = new[] {("Key4", (TestKeyValue)"Value344")} }) } - }), - + }) } }; @@ -520,7 +643,7 @@ protected class TestSection Values = new[] { ("Key3", (TestKeyValue)"Value123"), - ("Key3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"}), + ("Key3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"}) }, }), ("SectioN2", new TestSection @@ -528,7 +651,7 @@ protected class TestSection Values = new[] { ("KeY3", (TestKeyValue)"Value123"), - ("KeY3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"}), + ("KeY3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"}) }, }) @@ -540,11 +663,97 @@ protected class TestSection { ("Section4", new TestSection { - Values = new[] {("Key4", (TestKeyValue)"Value344")}, + Values = new[] {("Key4", (TestKeyValue)"Value344")} + }) + } + }) + } + }; + + public static TestSection NullsTestConfig { get; } + = new TestSection + { + Values = new[] { ("Key1", new TestKeyValue((string)null)) }, + Sections = new[] + { + ("Section1", new TestSection + { + Values = new[] {("Key2", new TestKeyValue((string)null))}, + Sections = new[] + { + ("Section2", new TestSection + { + Values = new[] + { + ("Key3", new TestKeyValue((string)null)), + ("Key3a", (TestKeyValue)new string[] {null, null, null}) + }, }) } }), + ("Section3", new TestSection + { + Sections = new[] + { + ("Section4", new TestSection + { + Values = new[] {("Key4", new TestKeyValue((string)null))} + }) + } + }) + } + }; + public static TestSection ExtraValuesTestConfig { get; } + = new TestSection + { + Values = new[] + { + ("Key1", (TestKeyValue)"Value1"), + ("Key1r", (TestKeyValue)"Value1r") + }, + Sections = new[] + { + ("Section1", new TestSection + { + Values = new[] + { + ("Key2", (TestKeyValue)"Value12"), + ("Key2r", (TestKeyValue)"Value12r") + }, + Sections = new[] + { + ("Section2", new TestSection + { + Values = new[] + { + ("Key3", (TestKeyValue)"Value123"), + ("Key3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2", "ArrayValue2r"}), + ("Key3ar", (TestKeyValue)new[] {"ArrayValue0r"}) + }, + }) + } + }), + ("Section3", new TestSection + { + Sections = new[] + { + ("Section4", new TestSection + { + Values = new[] {("Key4", (TestKeyValue)"Value344")} + }) + } + }), + ("Section5r", new TestSection + { + Sections = new[] + { + ("Section6r", new TestSection + { + Values = new[] {("Key5r", (TestKeyValue)"Value565r")} + }) + } + }) } }; } diff --git a/src/Configuration.KeyPerFile/test/KeyPerFileTests.cs b/src/Configuration.KeyPerFile/test/KeyPerFileTests.cs index fac0e839e8b5..4de528c01162 100644 --- a/src/Configuration.KeyPerFile/test/KeyPerFileTests.cs +++ b/src/Configuration.KeyPerFile/test/KeyPerFileTests.cs @@ -190,20 +190,11 @@ public TestFileProvider(params IFileInfo[] files) _contents = new TestDirectoryContents(files); } - public IDirectoryContents GetDirectoryContents(string subpath) - { - return _contents; - } + public IDirectoryContents GetDirectoryContents(string subpath) => _contents; - public IFileInfo GetFileInfo(string subpath) - { - throw new NotImplementedException(); - } + public IFileInfo GetFileInfo(string subpath) => new TestFile("TestDirectory"); - public IChangeToken Watch(string filter) - { - throw new NotImplementedException(); - } + public IChangeToken Watch(string filter) => throw new NotImplementedException(); } class TestDirectoryContents : IDirectoryContents @@ -215,75 +206,33 @@ public TestDirectoryContents(params IFileInfo[] files) _list = new List(files); } - public bool Exists - { - get - { - return true; - } - } + public bool Exists => true; - public IEnumerator GetEnumerator() - { - return _list.GetEnumerator(); - } + public IEnumerator GetEnumerator() => _list.GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } //TODO: Probably need a directory and file type. class TestFile : IFileInfo { - private string _name; - private string _contents; + private readonly string _name; + private readonly string _contents; - public bool Exists - { - get - { - return true; - } - } + public bool Exists => true; public bool IsDirectory { get; } - public DateTimeOffset LastModified - { - get - { - throw new NotImplementedException(); - } - } + public DateTimeOffset LastModified => throw new NotImplementedException(); - public long Length - { - get - { - throw new NotImplementedException(); - } - } + public long Length => throw new NotImplementedException(); - public string Name - { - get - { - return _name; - } - } + public string Name => _name; - public string PhysicalPath - { - get - { - throw new NotImplementedException(); - } - } + public string PhysicalPath => "Root/" + Name; public TestFile(string name) { @@ -304,7 +253,9 @@ public Stream CreateReadStream() throw new InvalidOperationException("Cannot create stream from directory"); } - return new MemoryStream(Encoding.UTF8.GetBytes(_contents)); + return _contents == null + ? new MemoryStream() + : new MemoryStream(Encoding.UTF8.GetBytes(_contents)); } } } From 5f7ab102cfe885af5972449a71aeffb4bfdd9946 Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Wed, 19 Dec 2018 13:43:43 -0800 Subject: [PATCH 035/183] Fix dotnet/extensions#815 - fix path to embedded manifest task file \n\nCommit migrated from https://github.com/dotnet/extensions/commit/cab86d2e3601db232849e4fab078ff6d5dc4df8d --- .../src/Microsoft.Extensions.FileProviders.Embedded.nuspec | 4 +--- .../Microsoft.Extensions.FileProviders.Embedded.props | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.nuspec b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.nuspec index af7feca8fdc0..ff6d385add60 100644 --- a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.nuspec +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.nuspec @@ -27,7 +27,5 @@ - - - \ No newline at end of file + diff --git a/src/FileProviders/Embedded/src/build/netstandard2.0/Microsoft.Extensions.FileProviders.Embedded.props b/src/FileProviders/Embedded/src/build/netstandard2.0/Microsoft.Extensions.FileProviders.Embedded.props index 66ea7b5c22cd..aabbabc92fe1 100644 --- a/src/FileProviders/Embedded/src/build/netstandard2.0/Microsoft.Extensions.FileProviders.Embedded.props +++ b/src/FileProviders/Embedded/src/build/netstandard2.0/Microsoft.Extensions.FileProviders.Embedded.props @@ -5,9 +5,7 @@ - <_FileProviderTaskFolder Condition="'$(MSBuildRuntimeType)' == 'Core'">netstandard2.0 - <_FileProviderTaskFolder Condition="'$(MSBuildRuntimeType)' != 'Core'">net461 - <_FileProviderTaskAssembly>$(MSBuildThisFileDirectory)..\..\tasks\$(_FileProviderTaskFolder)\Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.dll + <_FileProviderTaskAssembly>$(MSBuildThisFileDirectory)..\..\tasks\netstandard2.0\Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.dll Date: Sun, 23 Dec 2018 18:28:10 -0800 Subject: [PATCH 036/183] Fix aspnet/Extensionsdotnet/extensions#639 The issue is that the hosted service would not be registered if other hosted services are present. The fix is to use TryAddEnumble so that we allow multipled `IHostedService`s but not multiple instances of the same type. \n\nCommit migrated from https://github.com/dotnet/extensions/commit/a2f28147c21d5272fb66b75a47325cd9e63cde58 --- .../HealthCheckServiceCollectionExtensions.cs | 2 +- .../ServiceCollectionExtensionsTest.cs | 53 +++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthCheckServiceCollectionExtensions.cs b/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthCheckServiceCollectionExtensions.cs index d6df03d2aecb..91ffa59449e3 100644 --- a/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthCheckServiceCollectionExtensions.cs +++ b/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthCheckServiceCollectionExtensions.cs @@ -26,7 +26,7 @@ public static class HealthCheckServiceCollectionExtensions public static IHealthChecksBuilder AddHealthChecks(this IServiceCollection services) { services.TryAddSingleton(); - services.TryAddSingleton(); + services.TryAddEnumerable(ServiceDescriptor.Singleton()); return new HealthChecksBuilder(services); } } diff --git a/src/HealthChecks/HealthChecks/test/DependencyInjection/ServiceCollectionExtensionsTest.cs b/src/HealthChecks/HealthChecks/test/DependencyInjection/ServiceCollectionExtensionsTest.cs index 694a97628d67..98371e9f139e 100644 --- a/src/HealthChecks/HealthChecks/test/DependencyInjection/ServiceCollectionExtensionsTest.cs +++ b/src/HealthChecks/HealthChecks/test/DependencyInjection/ServiceCollectionExtensionsTest.cs @@ -2,6 +2,8 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Linq; +using System.Threading; +using System.Threading.Tasks; using Microsoft.Extensions.Diagnostics.HealthChecks; using Microsoft.Extensions.Hosting; using Xunit; @@ -39,5 +41,56 @@ public void AddHealthChecks_RegistersSingletonHealthCheckServiceIdempotently() Assert.Null(actual.ImplementationFactory); }); } + + [Fact] // see: https://github.com/aspnet/Extensions/issues/639 + public void AddHealthChecks_RegistersPublisherService_WhenOtherHostedServicesRegistered() + { + // Arrange + var services = new ServiceCollection(); + + // Act + services.AddSingleton(); + services.AddHealthChecks(); + + // Assert + Assert.Collection(services.OrderBy(s => s.ServiceType.FullName).ThenBy(s => s.ImplementationType.FullName), + actual => + { + Assert.Equal(ServiceLifetime.Singleton, actual.Lifetime); + Assert.Equal(typeof(HealthCheckService), actual.ServiceType); + Assert.Equal(typeof(DefaultHealthCheckService), actual.ImplementationType); + Assert.Null(actual.ImplementationInstance); + Assert.Null(actual.ImplementationFactory); + }, + actual => + { + Assert.Equal(ServiceLifetime.Singleton, actual.Lifetime); + Assert.Equal(typeof(IHostedService), actual.ServiceType); + Assert.Equal(typeof(DummyHostedService), actual.ImplementationType); + Assert.Null(actual.ImplementationInstance); + Assert.Null(actual.ImplementationFactory); + }, + actual => + { + Assert.Equal(ServiceLifetime.Singleton, actual.Lifetime); + Assert.Equal(typeof(IHostedService), actual.ServiceType); + Assert.Equal(typeof(HealthCheckPublisherHostedService), actual.ImplementationType); + Assert.Null(actual.ImplementationInstance); + Assert.Null(actual.ImplementationFactory); + }); + } + + private class DummyHostedService : IHostedService + { + public Task StartAsync(CancellationToken cancellationToken) + { + throw new System.NotImplementedException(); + } + + public Task StopAsync(CancellationToken cancellationToken) + { + throw new System.NotImplementedException(); + } + } } } From bdc2ea81c0da8ca6e0c7633c4b37769de216e995 Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Mon, 31 Dec 2018 12:54:54 -0800 Subject: [PATCH 037/183] Unify TypeNameHelpers (dotnet/extensions#876) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/2f53c3619543bb2b7a166c1926ea462b8521d2fc --- src/Shared/TypeNameHelper/TypeNameHelper.cs | 61 ++++++++++++------- .../test/Shared.Tests/TypeNameHelperTest.cs | 41 +++++++++++++ 2 files changed, 81 insertions(+), 21 deletions(-) diff --git a/src/Shared/TypeNameHelper/TypeNameHelper.cs b/src/Shared/TypeNameHelper/TypeNameHelper.cs index 1cc746864658..3994a074b6d4 100644 --- a/src/Shared/TypeNameHelper/TypeNameHelper.cs +++ b/src/Shared/TypeNameHelper/TypeNameHelper.cs @@ -9,6 +9,8 @@ namespace Microsoft.Extensions.Internal { internal class TypeNameHelper { + private const char DefaultNestedTypeDelimiter = '+'; + private static readonly Dictionary _builtInTypeNames = new Dictionary { { typeof(void), "void" }, @@ -40,15 +42,17 @@ public static string GetTypeDisplayName(object item, bool fullName = true) /// The . /// true to print a fully qualified name. /// true to include generic parameter names. + /// true to include generic parameters. + /// Character to use as a delimiter in nested type names /// The pretty printed type name. - public static string GetTypeDisplayName(Type type, bool fullName = true, bool includeGenericParameterNames = false) + public static string GetTypeDisplayName(Type type, bool fullName = true, bool includeGenericParameterNames = false, bool includeGenericParameters = true, char nestedTypeDelimiter = DefaultNestedTypeDelimiter) { var builder = new StringBuilder(); - ProcessType(builder, type, new DisplayNameOptions(fullName, includeGenericParameterNames)); + ProcessType(builder, type, new DisplayNameOptions(fullName, includeGenericParameterNames, includeGenericParameters, nestedTypeDelimiter)); return builder.ToString(); } - private static void ProcessType(StringBuilder builder, Type type, DisplayNameOptions options) + private static void ProcessType(StringBuilder builder, Type type, in DisplayNameOptions options) { if (type.IsGenericType) { @@ -72,11 +76,17 @@ private static void ProcessType(StringBuilder builder, Type type, DisplayNameOpt } else { - builder.Append(options.FullName ? type.FullName : type.Name); + var name = options.FullName ? type.FullName : type.Name; + builder.Append(name); + + if (options.NestedTypeDelimiter != DefaultNestedTypeDelimiter) + { + builder.Replace(DefaultNestedTypeDelimiter, options.NestedTypeDelimiter, builder.Length - name.Length, name.Length); + } } } - private static void ProcessArrayType(StringBuilder builder, Type type, DisplayNameOptions options) + private static void ProcessArrayType(StringBuilder builder, Type type, in DisplayNameOptions options) { var innerType = type; while (innerType.IsArray) @@ -95,7 +105,7 @@ private static void ProcessArrayType(StringBuilder builder, Type type, DisplayNa } } - private static void ProcessGenericType(StringBuilder builder, Type type, Type[] genericArguments, int length, DisplayNameOptions options) + private static void ProcessGenericType(StringBuilder builder, Type type, Type[] genericArguments, int length, in DisplayNameOptions options) { var offset = 0; if (type.IsNested) @@ -108,7 +118,7 @@ private static void ProcessGenericType(StringBuilder builder, Type type, Type[] if (type.IsNested) { ProcessGenericType(builder, type.DeclaringType, genericArguments, offset, options); - builder.Append('+'); + builder.Append(options.NestedTypeDelimiter); } else if (!string.IsNullOrEmpty(type.Namespace)) { @@ -126,35 +136,44 @@ private static void ProcessGenericType(StringBuilder builder, Type type, Type[] builder.Append(type.Name, 0, genericPartIndex); - builder.Append('<'); - for (var i = offset; i < length; i++) + if (options.IncludeGenericParameters) { - ProcessType(builder, genericArguments[i], options); - if (i + 1 == length) + builder.Append('<'); + for (var i = offset; i < length; i++) { - continue; - } - - builder.Append(','); - if (options.IncludeGenericParameterNames || !genericArguments[i + 1].IsGenericParameter) - { - builder.Append(' '); + ProcessType(builder, genericArguments[i], options); + if (i + 1 == length) + { + continue; + } + + builder.Append(','); + if (options.IncludeGenericParameterNames || !genericArguments[i + 1].IsGenericParameter) + { + builder.Append(' '); + } } + builder.Append('>'); } - builder.Append('>'); } - private struct DisplayNameOptions + private readonly struct DisplayNameOptions { - public DisplayNameOptions(bool fullName, bool includeGenericParameterNames) + public DisplayNameOptions(bool fullName, bool includeGenericParameterNames, bool includeGenericParameters, char nestedTypeDelimiter) { FullName = fullName; + IncludeGenericParameters = includeGenericParameters; IncludeGenericParameterNames = includeGenericParameterNames; + NestedTypeDelimiter = nestedTypeDelimiter; } public bool FullName { get; } + public bool IncludeGenericParameters { get; } + public bool IncludeGenericParameterNames { get; } + + public char NestedTypeDelimiter { get; } } } } diff --git a/src/Shared/test/Shared.Tests/TypeNameHelperTest.cs b/src/Shared/test/Shared.Tests/TypeNameHelperTest.cs index b7f4285bdc4f..bd29f647d10b 100644 --- a/src/Shared/test/Shared.Tests/TypeNameHelperTest.cs +++ b/src/Shared/test/Shared.Tests/TypeNameHelperTest.cs @@ -216,6 +216,47 @@ public void GetTypeDisplayName_WithoutFullName_IncludesGenericParameterNamesWhen Assert.Equal(expected, actual); } + public static TheoryData FullTypeNameData + { + get + { + return new TheoryData + { + // Predefined Types + { typeof(int), "int" }, + { typeof(List), "System.Collections.Generic.List" }, + { typeof(Dictionary), "System.Collections.Generic.Dictionary" }, + { typeof(Dictionary>), "System.Collections.Generic.Dictionary" }, + { typeof(List>), "System.Collections.Generic.List" }, + + // Classes inside NonGeneric class + { typeof(A), "Microsoft.Extensions.Internal.TypeNameHelperTest.A" }, + { typeof(B), "Microsoft.Extensions.Internal.TypeNameHelperTest.B" }, + { typeof(C), "Microsoft.Extensions.Internal.TypeNameHelperTest.C" }, + { typeof(C>), "Microsoft.Extensions.Internal.TypeNameHelperTest.C" }, + { typeof(B>), "Microsoft.Extensions.Internal.TypeNameHelperTest.B" }, + + // Classes inside Generic class + { typeof(Outer.D), "Microsoft.Extensions.Internal.TypeNameHelperTest.Outer.D" }, + { typeof(Outer.E), "Microsoft.Extensions.Internal.TypeNameHelperTest.Outer.E" }, + { typeof(Outer.F), "Microsoft.Extensions.Internal.TypeNameHelperTest.Outer.F" }, + { typeof(Outer.F.E>),"Microsoft.Extensions.Internal.TypeNameHelperTest.Outer.F" }, + { typeof(Outer.E.E>), "Microsoft.Extensions.Internal.TypeNameHelperTest.Outer.E" } + }; + } + } + + [Theory] + [MemberData(nameof(FullTypeNameData))] + public void Can_PrettyPrint_FullTypeName_WithoutGenericParametersAndNestedTypeDelimiter(Type type, string expectedTypeName) + { + // Arrange & Act + var displayName = TypeNameHelper.GetTypeDisplayName(type, fullName: true, includeGenericParameters: false, nestedTypeDelimiter: '.'); + + // Assert + Assert.Equal(expectedTypeName, displayName); + } + private class A { } private class B { } From 3287fba28f61d9167bcb342a5447aec0ad45cacd Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Mon, 7 Jan 2019 15:37:53 +1300 Subject: [PATCH 038/183] Don't timeout in testing task extensions when debugger is attached (dotnet/extensions#903) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/24c84a5b1db0fca3cfcc54eed3f4f94c6670a709 --- src/Testing/src/TaskExtensions.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Testing/src/TaskExtensions.cs b/src/Testing/src/TaskExtensions.cs index 83130aeae405..f99bf7361aca 100644 --- a/src/Testing/src/TaskExtensions.cs +++ b/src/Testing/src/TaskExtensions.cs @@ -1,7 +1,8 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Diagnostics; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; @@ -12,10 +13,11 @@ public static class TaskExtensions { public static async Task TimeoutAfter(this Task task, TimeSpan timeout, [CallerFilePath] string filePath = null, - [CallerLineNumber] int lineNumber = default(int)) + [CallerLineNumber] int lineNumber = default) { // Don't create a timer if the task is already completed - if (task.IsCompleted) + // or the debugger is attached + if (task.IsCompleted || Debugger.IsAttached) { return await task; } @@ -28,17 +30,17 @@ public static async Task TimeoutAfter(this Task task, TimeSpan timeout, } else { - throw new TimeoutException( - CreateMessage(timeout, filePath, lineNumber)); + throw new TimeoutException(CreateMessage(timeout, filePath, lineNumber)); } } public static async Task TimeoutAfter(this Task task, TimeSpan timeout, [CallerFilePath] string filePath = null, - [CallerLineNumber] int lineNumber = default(int)) + [CallerLineNumber] int lineNumber = default) { // Don't create a timer if the task is already completed - if (task.IsCompleted) + // or the debugger is attached + if (task.IsCompleted || Debugger.IsAttached) { await task; return; From 3db4f50eace48876c30c9293363c7c83fcf2645b Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Mon, 7 Jan 2019 15:52:44 -0800 Subject: [PATCH 039/183] Remove netcoreapp2.0 test TFMs (dotnet/extensions#907) .NET Core 2.0 reached EOL last year. This removes multi-targeting our test projects and test assets to only use .NET Core 2.1 and .NET Framework 4.6.1.\n\nCommit migrated from https://github.com/dotnet/extensions/commit/084494d21dc5b9df06e4c3771d1232f0a1d0b3a6 --- .../Microsoft.Extensions.Configuration.KeyPerFile.Tests.csproj | 2 +- .../Microsoft.Extensions.FileProviders.Embedded.Tests.csproj | 2 +- ....Extensions.FileProviders.Embedded.Manifest.Task.Test.csproj | 2 +- .../test/Microsoft.Extensions.Localization.Tests.csproj | 2 +- .../test/Microsoft.Extensions.ObjectPool.Tests.csproj | 2 +- .../test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj | 2 +- .../test/Microsoft.Extensions.WebEncoders.Tests.csproj | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Configuration.KeyPerFile/test/Microsoft.Extensions.Configuration.KeyPerFile.Tests.csproj b/src/Configuration.KeyPerFile/test/Microsoft.Extensions.Configuration.KeyPerFile.Tests.csproj index 634056a345bd..71ed14dff417 100644 --- a/src/Configuration.KeyPerFile/test/Microsoft.Extensions.Configuration.KeyPerFile.Tests.csproj +++ b/src/Configuration.KeyPerFile/test/Microsoft.Extensions.Configuration.KeyPerFile.Tests.csproj @@ -1,7 +1,7 @@  - $(StandardTestTfms) + netcoreapp2.1;net461 diff --git a/src/FileProviders/Embedded/test/Microsoft.Extensions.FileProviders.Embedded.Tests.csproj b/src/FileProviders/Embedded/test/Microsoft.Extensions.FileProviders.Embedded.Tests.csproj index 8703c9c508ed..b4c7a3987a9f 100644 --- a/src/FileProviders/Embedded/test/Microsoft.Extensions.FileProviders.Embedded.Tests.csproj +++ b/src/FileProviders/Embedded/test/Microsoft.Extensions.FileProviders.Embedded.Tests.csproj @@ -1,7 +1,7 @@  - $(StandardTestTfms) + netcoreapp2.1;net461 diff --git a/src/FileProviders/Manifest.MSBuildTask/test/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Test.csproj b/src/FileProviders/Manifest.MSBuildTask/test/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Test.csproj index fe338976031f..9659b189c84d 100644 --- a/src/FileProviders/Manifest.MSBuildTask/test/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Test.csproj +++ b/src/FileProviders/Manifest.MSBuildTask/test/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Test.csproj @@ -1,7 +1,7 @@  - $(StandardTestTfms) + netcoreapp2.1;net461 diff --git a/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests.csproj b/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests.csproj index e7a47a1a6e7e..6834de2bd5a0 100644 --- a/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests.csproj +++ b/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests.csproj @@ -1,7 +1,7 @@  - $(StandardTestTfms) + netcoreapp2.1;net461 diff --git a/src/ObjectPool/test/Microsoft.Extensions.ObjectPool.Tests.csproj b/src/ObjectPool/test/Microsoft.Extensions.ObjectPool.Tests.csproj index b922cc227e33..c8ba4cf4df10 100644 --- a/src/ObjectPool/test/Microsoft.Extensions.ObjectPool.Tests.csproj +++ b/src/ObjectPool/test/Microsoft.Extensions.ObjectPool.Tests.csproj @@ -1,7 +1,7 @@ - $(StandardTestTfms) + netcoreapp2.1;net461 diff --git a/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj b/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj index 1873e485a974..e3194d66c57e 100644 --- a/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj +++ b/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj @@ -1,7 +1,7 @@ - $(StandardTestTfms) + netcoreapp2.1;net461 true diff --git a/src/WebEncoders/test/Microsoft.Extensions.WebEncoders.Tests.csproj b/src/WebEncoders/test/Microsoft.Extensions.WebEncoders.Tests.csproj index 729dc5c61a92..af02c91cdf8e 100755 --- a/src/WebEncoders/test/Microsoft.Extensions.WebEncoders.Tests.csproj +++ b/src/WebEncoders/test/Microsoft.Extensions.WebEncoders.Tests.csproj @@ -1,7 +1,7 @@  - $(StandardTestTfms) + netcoreapp2.1;net461 From 97bb8897cf92da0a371526d72625fcaf93348ae5 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Wed, 9 Jan 2019 11:10:09 -0800 Subject: [PATCH 040/183] Reorganize code \n\nCommit migrated from https://github.com/dotnet/extensions/commit/a24412cc03f496b928da34f8b24d092d3d64d4f5 --- src/JSInterop/.editorconfig | 27 + src/JSInterop/Microsoft.JSInterop.sln | 58 + src/JSInterop/README.md | 8 + .../src/Microsoft.JSInterop.JS/.gitignore | 1 + .../Microsoft.JSInterop.JS.csproj | 13 + .../Microsoft.JSInterop.JS/package-lock.json | 105 + .../src/Microsoft.JSInterop.JS/package.json | 25 + .../src/Microsoft.JSInterop.ts | 287 +++ .../src/Microsoft.JSInterop.JS/tsconfig.json | 19 + .../Microsoft.JSInterop/DotNetDispatcher.cs | 299 +++ .../Microsoft.JSInterop/DotNetObjectRef.cs | 66 + .../ICustomArgSerializer.cs | 22 + .../IJSInProcessRuntime.cs | 20 + .../src/Microsoft.JSInterop/IJSRuntime.cs | 32 + .../InteropArgSerializerStrategy.cs | 121 + .../Microsoft.JSInterop/JSAsyncCallResult.cs | 36 + .../src/Microsoft.JSInterop/JSException.cs | 21 + .../JSInProcessRuntimeBase.cs | 32 + .../JSInvokableAttribute.cs | 48 + .../src/Microsoft.JSInterop/JSRuntime.cs | 34 + .../src/Microsoft.JSInterop/JSRuntimeBase.cs | 116 + .../src/Microsoft.JSInterop/Json/CamelCase.cs | 59 + .../src/Microsoft.JSInterop/Json/Json.cs | 39 + .../Json/SimpleJson/README.txt | 24 + .../Json/SimpleJson/SimpleJson.cs | 2201 +++++++++++++++++ .../Microsoft.JSInterop.csproj | 7 + .../Properties/AssemblyInfo.cs | 3 + .../Microsoft.JSInterop/TaskGenericsUtil.cs | 116 + .../Mono.WebAssembly.Interop/InternalCalls.cs | 25 + .../Mono.WebAssembly.Interop.csproj | 11 + .../MonoWebAssemblyJSRuntime.cs | 114 + .../DotNetDispatcherTest.cs | 443 ++++ .../DotNetObjectRefTest.cs | 68 + .../JSInProcessRuntimeBaseTest.cs | 117 + .../JSRuntimeBaseTest.cs | 191 ++ .../Microsoft.JSInterop.Test/JSRuntimeTest.cs | 37 + .../Microsoft.JSInterop.Test/JsonUtilTest.cs | 349 +++ .../Microsoft.JSInterop.Test.csproj | 20 + 38 files changed, 5214 insertions(+) create mode 100644 src/JSInterop/.editorconfig create mode 100644 src/JSInterop/Microsoft.JSInterop.sln create mode 100644 src/JSInterop/README.md create mode 100644 src/JSInterop/src/Microsoft.JSInterop.JS/.gitignore create mode 100644 src/JSInterop/src/Microsoft.JSInterop.JS/Microsoft.JSInterop.JS.csproj create mode 100644 src/JSInterop/src/Microsoft.JSInterop.JS/package-lock.json create mode 100644 src/JSInterop/src/Microsoft.JSInterop.JS/package.json create mode 100644 src/JSInterop/src/Microsoft.JSInterop.JS/src/Microsoft.JSInterop.ts create mode 100644 src/JSInterop/src/Microsoft.JSInterop.JS/tsconfig.json create mode 100644 src/JSInterop/src/Microsoft.JSInterop/DotNetDispatcher.cs create mode 100644 src/JSInterop/src/Microsoft.JSInterop/DotNetObjectRef.cs create mode 100644 src/JSInterop/src/Microsoft.JSInterop/ICustomArgSerializer.cs create mode 100644 src/JSInterop/src/Microsoft.JSInterop/IJSInProcessRuntime.cs create mode 100644 src/JSInterop/src/Microsoft.JSInterop/IJSRuntime.cs create mode 100644 src/JSInterop/src/Microsoft.JSInterop/InteropArgSerializerStrategy.cs create mode 100644 src/JSInterop/src/Microsoft.JSInterop/JSAsyncCallResult.cs create mode 100644 src/JSInterop/src/Microsoft.JSInterop/JSException.cs create mode 100644 src/JSInterop/src/Microsoft.JSInterop/JSInProcessRuntimeBase.cs create mode 100644 src/JSInterop/src/Microsoft.JSInterop/JSInvokableAttribute.cs create mode 100644 src/JSInterop/src/Microsoft.JSInterop/JSRuntime.cs create mode 100644 src/JSInterop/src/Microsoft.JSInterop/JSRuntimeBase.cs create mode 100644 src/JSInterop/src/Microsoft.JSInterop/Json/CamelCase.cs create mode 100644 src/JSInterop/src/Microsoft.JSInterop/Json/Json.cs create mode 100644 src/JSInterop/src/Microsoft.JSInterop/Json/SimpleJson/README.txt create mode 100644 src/JSInterop/src/Microsoft.JSInterop/Json/SimpleJson/SimpleJson.cs create mode 100644 src/JSInterop/src/Microsoft.JSInterop/Microsoft.JSInterop.csproj create mode 100644 src/JSInterop/src/Microsoft.JSInterop/Properties/AssemblyInfo.cs create mode 100644 src/JSInterop/src/Microsoft.JSInterop/TaskGenericsUtil.cs create mode 100644 src/JSInterop/src/Mono.WebAssembly.Interop/InternalCalls.cs create mode 100644 src/JSInterop/src/Mono.WebAssembly.Interop/Mono.WebAssembly.Interop.csproj create mode 100644 src/JSInterop/src/Mono.WebAssembly.Interop/MonoWebAssemblyJSRuntime.cs create mode 100644 src/JSInterop/test/Microsoft.JSInterop.Test/DotNetDispatcherTest.cs create mode 100644 src/JSInterop/test/Microsoft.JSInterop.Test/DotNetObjectRefTest.cs create mode 100644 src/JSInterop/test/Microsoft.JSInterop.Test/JSInProcessRuntimeBaseTest.cs create mode 100644 src/JSInterop/test/Microsoft.JSInterop.Test/JSRuntimeBaseTest.cs create mode 100644 src/JSInterop/test/Microsoft.JSInterop.Test/JSRuntimeTest.cs create mode 100644 src/JSInterop/test/Microsoft.JSInterop.Test/JsonUtilTest.cs create mode 100644 src/JSInterop/test/Microsoft.JSInterop.Test/Microsoft.JSInterop.Test.csproj diff --git a/src/JSInterop/.editorconfig b/src/JSInterop/.editorconfig new file mode 100644 index 000000000000..0d238f8e41d1 --- /dev/null +++ b/src/JSInterop/.editorconfig @@ -0,0 +1,27 @@ +# All Files +[*] +charset = utf-8 +end_of_line = crlf +indent_style = space +indent_size = 4 +insert_final_newline = false +trim_trailing_whitespace = true + +# Solution Files +[*.sln] +indent_style = tab + +# Markdown Files +[*.md] +trim_trailing_whitespace = false + +# Web Files +[*.{htm,html,js,ts,css,scss,less}] +insert_final_newline = true +indent_size = 2 + +[*.{yml,json}] +indent_size = 2 + +[*.{xml,csproj,config,*proj,targets,props}] +indent_size = 2 diff --git a/src/JSInterop/Microsoft.JSInterop.sln b/src/JSInterop/Microsoft.JSInterop.sln new file mode 100644 index 000000000000..d646a6b0681c --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop.sln @@ -0,0 +1,58 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27703.2042 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{1290437E-A890-419E-A317-D0F7FEE185A5}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{B98D4F51-88FB-471C-B56F-752E8EE502E7}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.JSInterop", "src\Microsoft.JSInterop\Microsoft.JSInterop.csproj", "{CB4CD4A6-9BAA-46D1-944F-CE56DEC2663C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.JSInterop.Test", "test\Microsoft.JSInterop.Test\Microsoft.JSInterop.Test.csproj", "{7FF8B199-52C0-4DFE-A73B-0C9E18220C0E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.JSInterop.JS", "src\Microsoft.JSInterop.JS\Microsoft.JSInterop.JS.csproj", "{60BA5AAD-264A-437E-8319-577841C66CC6}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{BE4CBB33-5C40-4A07-B6FC-1D7C3AE13024}" + ProjectSection(SolutionItems) = preProject + README.md = README.md + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mono.WebAssembly.Interop", "src\Mono.WebAssembly.Interop\Mono.WebAssembly.Interop.csproj", "{10145E99-1B2D-40C5-9595-582BDAF3E024}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CB4CD4A6-9BAA-46D1-944F-CE56DEC2663C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CB4CD4A6-9BAA-46D1-944F-CE56DEC2663C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CB4CD4A6-9BAA-46D1-944F-CE56DEC2663C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CB4CD4A6-9BAA-46D1-944F-CE56DEC2663C}.Release|Any CPU.Build.0 = Release|Any CPU + {7FF8B199-52C0-4DFE-A73B-0C9E18220C0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7FF8B199-52C0-4DFE-A73B-0C9E18220C0E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7FF8B199-52C0-4DFE-A73B-0C9E18220C0E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7FF8B199-52C0-4DFE-A73B-0C9E18220C0E}.Release|Any CPU.Build.0 = Release|Any CPU + {60BA5AAD-264A-437E-8319-577841C66CC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {60BA5AAD-264A-437E-8319-577841C66CC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {60BA5AAD-264A-437E-8319-577841C66CC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {60BA5AAD-264A-437E-8319-577841C66CC6}.Release|Any CPU.Build.0 = Release|Any CPU + {10145E99-1B2D-40C5-9595-582BDAF3E024}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {10145E99-1B2D-40C5-9595-582BDAF3E024}.Debug|Any CPU.Build.0 = Debug|Any CPU + {10145E99-1B2D-40C5-9595-582BDAF3E024}.Release|Any CPU.ActiveCfg = Release|Any CPU + {10145E99-1B2D-40C5-9595-582BDAF3E024}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {CB4CD4A6-9BAA-46D1-944F-CE56DEC2663C} = {1290437E-A890-419E-A317-D0F7FEE185A5} + {7FF8B199-52C0-4DFE-A73B-0C9E18220C0E} = {B98D4F51-88FB-471C-B56F-752E8EE502E7} + {60BA5AAD-264A-437E-8319-577841C66CC6} = {1290437E-A890-419E-A317-D0F7FEE185A5} + {10145E99-1B2D-40C5-9595-582BDAF3E024} = {1290437E-A890-419E-A317-D0F7FEE185A5} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {7E07ABF2-427A-43FA-A6A4-82B21B96ACAF} + EndGlobalSection +EndGlobal diff --git a/src/JSInterop/README.md b/src/JSInterop/README.md new file mode 100644 index 000000000000..dcdaf7618e74 --- /dev/null +++ b/src/JSInterop/README.md @@ -0,0 +1,8 @@ +# jsinterop + +This repo is for `Microsoft.JSInterop`, a package that provides abstractions and features for interop between .NET and JavaScript code. + +## Usage + +The primary use case is for applications built with Mono WebAssembly or Blazor. It's not expected that developers will typically use these libraries separately from Mono WebAssembly, Blazor, or a similar technology. + diff --git a/src/JSInterop/src/Microsoft.JSInterop.JS/.gitignore b/src/JSInterop/src/Microsoft.JSInterop.JS/.gitignore new file mode 100644 index 000000000000..849ddff3b7ec --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop.JS/.gitignore @@ -0,0 +1 @@ +dist/ diff --git a/src/JSInterop/src/Microsoft.JSInterop.JS/Microsoft.JSInterop.JS.csproj b/src/JSInterop/src/Microsoft.JSInterop.JS/Microsoft.JSInterop.JS.csproj new file mode 100644 index 000000000000..4a6ba93ebbfc --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop.JS/Microsoft.JSInterop.JS.csproj @@ -0,0 +1,13 @@ + + + + netstandard2.0 + Latest + false + + + + + + + diff --git a/src/JSInterop/src/Microsoft.JSInterop.JS/package-lock.json b/src/JSInterop/src/Microsoft.JSInterop.JS/package-lock.json new file mode 100644 index 000000000000..a7f31ead4fb0 --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop.JS/package-lock.json @@ -0,0 +1,105 @@ +{ + "name": "@dotnet/jsinterop", + "version": "0.1.1", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "1.1.11" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "dev": true, + "requires": { + "glob": "7.1.3" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + } + } +} diff --git a/src/JSInterop/src/Microsoft.JSInterop.JS/package.json b/src/JSInterop/src/Microsoft.JSInterop.JS/package.json new file mode 100644 index 000000000000..a84734bc464b --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop.JS/package.json @@ -0,0 +1,25 @@ +{ + "name": "@dotnet/jsinterop", + "version": "0.1.1", + "description": "Provides abstractions and features for interop between .NET and JavaScript code.", + "main": "dist/Microsoft.JSInterop.js", + "types": "dist/Microsoft.JSInterop.d.js", + "scripts": { + "prepublish": "rimraf dist && dotnet build && echo 'Finished building NPM package \"@dotnet/jsinterop\"'" + }, + "files": [ + "dist/**" + ], + "author": "Microsoft", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/dotnet/jsinterop/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/dotnet/jsinterop.git" + }, + "devDependencies": { + "rimraf": "^2.5.4" + } +} diff --git a/src/JSInterop/src/Microsoft.JSInterop.JS/src/Microsoft.JSInterop.ts b/src/JSInterop/src/Microsoft.JSInterop.JS/src/Microsoft.JSInterop.ts new file mode 100644 index 000000000000..4b5d409d0f85 --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop.JS/src/Microsoft.JSInterop.ts @@ -0,0 +1,287 @@ +// This is a single-file self-contained module to avoid the need for a Webpack build + +module DotNet { + (window as any).DotNet = DotNet; // Ensure reachable from anywhere + + export type JsonReviver = ((key: any, value: any) => any); + const jsonRevivers: JsonReviver[] = []; + + const pendingAsyncCalls: { [id: number]: PendingAsyncCall } = {}; + const cachedJSFunctions: { [identifier: string]: Function } = {}; + let nextAsyncCallId = 1; // Start at 1 because zero signals "no response needed" + + let dotNetDispatcher: DotNetCallDispatcher | null = null; + + /** + * Sets the specified .NET call dispatcher as the current instance so that it will be used + * for future invocations. + * + * @param dispatcher An object that can dispatch calls from JavaScript to a .NET runtime. + */ + export function attachDispatcher(dispatcher: DotNetCallDispatcher) { + dotNetDispatcher = dispatcher; + } + + /** + * Adds a JSON reviver callback that will be used when parsing arguments received from .NET. + * @param reviver The reviver to add. + */ + export function attachReviver(reviver: JsonReviver) { + jsonRevivers.push(reviver); + } + + /** + * Invokes the specified .NET public method synchronously. Not all hosting scenarios support + * synchronous invocation, so if possible use invokeMethodAsync instead. + * + * @param assemblyName The short name (without key/version or .dll extension) of the .NET assembly containing the method. + * @param methodIdentifier The identifier of the method to invoke. The method must have a [JSInvokable] attribute specifying this identifier. + * @param args Arguments to pass to the method, each of which must be JSON-serializable. + * @returns The result of the operation. + */ + export function invokeMethod(assemblyName: string, methodIdentifier: string, ...args: any[]): T { + return invokePossibleInstanceMethod(assemblyName, methodIdentifier, null, args); + } + + /** + * Invokes the specified .NET public method asynchronously. + * + * @param assemblyName The short name (without key/version or .dll extension) of the .NET assembly containing the method. + * @param methodIdentifier The identifier of the method to invoke. The method must have a [JSInvokable] attribute specifying this identifier. + * @param args Arguments to pass to the method, each of which must be JSON-serializable. + * @returns A promise representing the result of the operation. + */ + export function invokeMethodAsync(assemblyName: string, methodIdentifier: string, ...args: any[]): Promise { + return invokePossibleInstanceMethodAsync(assemblyName, methodIdentifier, null, args); + } + + function invokePossibleInstanceMethod(assemblyName: string | null, methodIdentifier: string, dotNetObjectId: number | null, args: any[]): T { + const dispatcher = getRequiredDispatcher(); + if (dispatcher.invokeDotNetFromJS) { + const argsJson = JSON.stringify(args, argReplacer); + const resultJson = dispatcher.invokeDotNetFromJS(assemblyName, methodIdentifier, dotNetObjectId, argsJson); + return resultJson ? parseJsonWithRevivers(resultJson) : null; + } else { + throw new Error('The current dispatcher does not support synchronous calls from JS to .NET. Use invokeMethodAsync instead.'); + } + } + + function invokePossibleInstanceMethodAsync(assemblyName: string | null, methodIdentifier: string, dotNetObjectId: number | null, args: any[]): Promise { + const asyncCallId = nextAsyncCallId++; + const resultPromise = new Promise((resolve, reject) => { + pendingAsyncCalls[asyncCallId] = { resolve, reject }; + }); + + try { + const argsJson = JSON.stringify(args, argReplacer); + getRequiredDispatcher().beginInvokeDotNetFromJS(asyncCallId, assemblyName, methodIdentifier, dotNetObjectId, argsJson); + } catch(ex) { + // Synchronous failure + completePendingCall(asyncCallId, false, ex); + } + + return resultPromise; + } + + function getRequiredDispatcher(): DotNetCallDispatcher { + if (dotNetDispatcher !== null) { + return dotNetDispatcher; + } + + throw new Error('No .NET call dispatcher has been set.'); + } + + function completePendingCall(asyncCallId: number, success: boolean, resultOrError: any) { + if (!pendingAsyncCalls.hasOwnProperty(asyncCallId)) { + throw new Error(`There is no pending async call with ID ${asyncCallId}.`); + } + + const asyncCall = pendingAsyncCalls[asyncCallId]; + delete pendingAsyncCalls[asyncCallId]; + if (success) { + asyncCall.resolve(resultOrError); + } else { + asyncCall.reject(resultOrError); + } + } + + interface PendingAsyncCall { + resolve: (value?: T | PromiseLike) => void; + reject: (reason?: any) => void; + } + + /** + * Represents the ability to dispatch calls from JavaScript to a .NET runtime. + */ + export interface DotNetCallDispatcher { + /** + * Optional. If implemented, invoked by the runtime to perform a synchronous call to a .NET method. + * + * @param assemblyName The short name (without key/version or .dll extension) of the .NET assembly holding the method to invoke. The value may be null when invoking instance methods. + * @param methodIdentifier The identifier of the method to invoke. The method must have a [JSInvokable] attribute specifying this identifier. + * @param dotNetObjectId If given, the call will be to an instance method on the specified DotNetObject. Pass null or undefined to call static methods. + * @param argsJson JSON representation of arguments to pass to the method. + * @returns JSON representation of the result of the invocation. + */ + invokeDotNetFromJS?(assemblyName: string | null, methodIdentifier: string, dotNetObjectId: number | null, argsJson: string): string | null; + + /** + * Invoked by the runtime to begin an asynchronous call to a .NET method. + * + * @param callId A value identifying the asynchronous operation. This value should be passed back in a later call from .NET to JS. + * @param assemblyName The short name (without key/version or .dll extension) of the .NET assembly holding the method to invoke. The value may be null when invoking instance methods. + * @param methodIdentifier The identifier of the method to invoke. The method must have a [JSInvokable] attribute specifying this identifier. + * @param dotNetObjectId If given, the call will be to an instance method on the specified DotNetObject. Pass null to call static methods. + * @param argsJson JSON representation of arguments to pass to the method. + */ + beginInvokeDotNetFromJS(callId: number, assemblyName: string | null, methodIdentifier: string, dotNetObjectId: number | null, argsJson: string): void; + } + + /** + * Receives incoming calls from .NET and dispatches them to JavaScript. + */ + export const jsCallDispatcher = { + /** + * Finds the JavaScript function matching the specified identifier. + * + * @param identifier Identifies the globally-reachable function to be returned. + * @returns A Function instance. + */ + findJSFunction, + + /** + * Invokes the specified synchronous JavaScript function. + * + * @param identifier Identifies the globally-reachable function to invoke. + * @param argsJson JSON representation of arguments to be passed to the function. + * @returns JSON representation of the invocation result. + */ + invokeJSFromDotNet: (identifier: string, argsJson: string) => { + const result = findJSFunction(identifier).apply(null, parseJsonWithRevivers(argsJson)); + return result === null || result === undefined + ? null + : JSON.stringify(result, argReplacer); + }, + + /** + * Invokes the specified synchronous or asynchronous JavaScript function. + * + * @param asyncHandle A value identifying the asynchronous operation. This value will be passed back in a later call to endInvokeJSFromDotNet. + * @param identifier Identifies the globally-reachable function to invoke. + * @param argsJson JSON representation of arguments to be passed to the function. + */ + beginInvokeJSFromDotNet: (asyncHandle: number, identifier: string, argsJson: string): void => { + // Coerce synchronous functions into async ones, plus treat + // synchronous exceptions the same as async ones + const promise = new Promise(resolve => { + const synchronousResultOrPromise = findJSFunction(identifier).apply(null, parseJsonWithRevivers(argsJson)); + resolve(synchronousResultOrPromise); + }); + + // We only listen for a result if the caller wants to be notified about it + if (asyncHandle) { + // On completion, dispatch result back to .NET + // Not using "await" because it codegens a lot of boilerplate + promise.then( + result => getRequiredDispatcher().beginInvokeDotNetFromJS(0, 'Microsoft.JSInterop', 'DotNetDispatcher.EndInvoke', null, JSON.stringify([asyncHandle, true, result], argReplacer)), + error => getRequiredDispatcher().beginInvokeDotNetFromJS(0, 'Microsoft.JSInterop', 'DotNetDispatcher.EndInvoke', null, JSON.stringify([asyncHandle, false, formatError(error)])) + ); + } + }, + + /** + * Receives notification that an async call from JS to .NET has completed. + * @param asyncCallId The identifier supplied in an earlier call to beginInvokeDotNetFromJS. + * @param success A flag to indicate whether the operation completed successfully. + * @param resultOrExceptionMessage Either the operation result or an error message. + */ + endInvokeDotNetFromJS: (asyncCallId: string, success: boolean, resultOrExceptionMessage: any): void => { + const resultOrError = success ? resultOrExceptionMessage : new Error(resultOrExceptionMessage); + completePendingCall(parseInt(asyncCallId), success, resultOrError); + } + } + + function parseJsonWithRevivers(json: string): any { + return json ? JSON.parse(json, (key, initialValue) => { + // Invoke each reviver in order, passing the output from the previous reviver, + // so that each one gets a chance to transform the value + return jsonRevivers.reduce( + (latestValue, reviver) => reviver(key, latestValue), + initialValue + ); + }) : null; + } + + function formatError(error: any): string { + if (error instanceof Error) { + return `${error.message}\n${error.stack}`; + } else { + return error ? error.toString() : 'null'; + } + } + + function findJSFunction(identifier: string): Function { + if (cachedJSFunctions.hasOwnProperty(identifier)) { + return cachedJSFunctions[identifier]; + } + + let result: any = window; + let resultIdentifier = 'window'; + identifier.split('.').forEach(segment => { + if (segment in result) { + result = result[segment]; + resultIdentifier += '.' + segment; + } else { + throw new Error(`Could not find '${segment}' in '${resultIdentifier}'.`); + } + }); + + if (result instanceof Function) { + return result; + } else { + throw new Error(`The value '${resultIdentifier}' is not a function.`); + } + } + + class DotNetObject { + constructor(private _id: number) { + } + + public invokeMethod(methodIdentifier: string, ...args: any[]): T { + return invokePossibleInstanceMethod(null, methodIdentifier, this._id, args); + } + + public invokeMethodAsync(methodIdentifier: string, ...args: any[]): Promise { + return invokePossibleInstanceMethodAsync(null, methodIdentifier, this._id, args); + } + + public dispose() { + const promise = invokeMethodAsync( + 'Microsoft.JSInterop', + 'DotNetDispatcher.ReleaseDotNetObject', + this._id); + promise.catch(error => console.error(error)); + } + + public serializeAsArg() { + return `__dotNetObject:${this._id}`; + } + } + + const dotNetObjectValueFormat = /^__dotNetObject\:(\d+)$/; + attachReviver(function reviveDotNetObject(key: any, value: any) { + if (typeof value === 'string') { + const match = value.match(dotNetObjectValueFormat); + if (match) { + return new DotNetObject(parseInt(match[1])); + } + } + + // Unrecognized - let another reviver handle it + return value; + }); + + function argReplacer(key: string, value: any) { + return value instanceof DotNetObject ? value.serializeAsArg() : value; + } +} diff --git a/src/JSInterop/src/Microsoft.JSInterop.JS/tsconfig.json b/src/JSInterop/src/Microsoft.JSInterop.JS/tsconfig.json new file mode 100644 index 000000000000..f5a2b0e31a92 --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop.JS/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "noEmitOnError": true, + "removeComments": false, + "sourceMap": true, + "target": "es5", + "lib": ["es2015", "dom", "es2015.promise"], + "strict": true, + "declaration": true, + "outDir": "dist" + }, + "include": [ + "src/**/*.ts" + ], + "exclude": [ + "dist/**" + ] +} diff --git a/src/JSInterop/src/Microsoft.JSInterop/DotNetDispatcher.cs b/src/JSInterop/src/Microsoft.JSInterop/DotNetDispatcher.cs new file mode 100644 index 000000000000..0f346bbfdd21 --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop/DotNetDispatcher.cs @@ -0,0 +1,299 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.JSInterop.Internal; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; + +namespace Microsoft.JSInterop +{ + /// + /// Provides methods that receive incoming calls from JS to .NET. + /// + public static class DotNetDispatcher + { + private static ConcurrentDictionary> _cachedMethodsByAssembly + = new ConcurrentDictionary>(); + + /// + /// Receives a call from JS to .NET, locating and invoking the specified method. + /// + /// The assembly containing the method to be invoked. + /// The identifier of the method to be invoked. The method must be annotated with a matching this identifier string. + /// For instance method calls, identifies the target object. + /// A JSON representation of the parameters. + /// A JSON representation of the return value, or null. + public static string Invoke(string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) + { + // This method doesn't need [JSInvokable] because the platform is responsible for having + // some way to dispatch calls here. The logic inside here is the thing that checks whether + // the targeted method has [JSInvokable]. It is not itself subject to that restriction, + // because there would be nobody to police that. This method *is* the police. + + // DotNetDispatcher only works with JSRuntimeBase instances. + var jsRuntime = (JSRuntimeBase)JSRuntime.Current; + + var targetInstance = (object)null; + if (dotNetObjectId != default) + { + targetInstance = jsRuntime.ArgSerializerStrategy.FindDotNetObject(dotNetObjectId); + } + + var syncResult = InvokeSynchronously(assemblyName, methodIdentifier, targetInstance, argsJson); + return syncResult == null ? null : Json.Serialize(syncResult, jsRuntime.ArgSerializerStrategy); + } + + /// + /// Receives a call from JS to .NET, locating and invoking the specified method asynchronously. + /// + /// A value identifying the asynchronous call that should be passed back with the result, or null if no result notification is required. + /// The assembly containing the method to be invoked. + /// The identifier of the method to be invoked. The method must be annotated with a matching this identifier string. + /// For instance method calls, identifies the target object. + /// A JSON representation of the parameters. + /// A JSON representation of the return value, or null. + public static void BeginInvoke(string callId, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) + { + // This method doesn't need [JSInvokable] because the platform is responsible for having + // some way to dispatch calls here. The logic inside here is the thing that checks whether + // the targeted method has [JSInvokable]. It is not itself subject to that restriction, + // because there would be nobody to police that. This method *is* the police. + + // DotNetDispatcher only works with JSRuntimeBase instances. + // If the developer wants to use a totally custom IJSRuntime, then their JS-side + // code has to implement its own way of returning async results. + var jsRuntimeBaseInstance = (JSRuntimeBase)JSRuntime.Current; + + var targetInstance = dotNetObjectId == default + ? null + : jsRuntimeBaseInstance.ArgSerializerStrategy.FindDotNetObject(dotNetObjectId); + + object syncResult = null; + Exception syncException = null; + + try + { + syncResult = InvokeSynchronously(assemblyName, methodIdentifier, targetInstance, argsJson); + } + catch (Exception ex) + { + syncException = ex; + } + + // If there was no callId, the caller does not want to be notified about the result + if (callId != null) + { + // Invoke and coerce the result to a Task so the caller can use the same async API + // for both synchronous and asynchronous methods + var task = CoerceToTask(syncResult, syncException); + + task.ContinueWith(completedTask => + { + try + { + var result = TaskGenericsUtil.GetTaskResult(completedTask); + jsRuntimeBaseInstance.EndInvokeDotNet(callId, true, result); + } + catch (Exception ex) + { + ex = UnwrapException(ex); + jsRuntimeBaseInstance.EndInvokeDotNet(callId, false, ex); + } + }); + } + } + + private static Task CoerceToTask(object syncResult, Exception syncException) + { + if (syncException != null) + { + return Task.FromException(syncException); + } + else if (syncResult is Task syncResultTask) + { + return syncResultTask; + } + else + { + return Task.FromResult(syncResult); + } + } + + private static object InvokeSynchronously(string assemblyName, string methodIdentifier, object targetInstance, string argsJson) + { + if (targetInstance != null) + { + if (assemblyName != null) + { + throw new ArgumentException($"For instance method calls, '{nameof(assemblyName)}' should be null. Value received: '{assemblyName}'."); + } + + assemblyName = targetInstance.GetType().Assembly.GetName().Name; + } + + var (methodInfo, parameterTypes) = GetCachedMethodInfo(assemblyName, methodIdentifier); + + // There's no direct way to say we want to deserialize as an array with heterogenous + // entry types (e.g., [string, int, bool]), so we need to deserialize in two phases. + // First we deserialize as object[], for which SimpleJson will supply JsonObject + // instances for nonprimitive values. + var suppliedArgs = (object[])null; + var suppliedArgsLength = 0; + if (argsJson != null) + { + suppliedArgs = Json.Deserialize(argsJson).ToArray(); + suppliedArgsLength = suppliedArgs.Length; + } + if (suppliedArgsLength != parameterTypes.Length) + { + throw new ArgumentException($"In call to '{methodIdentifier}', expected {parameterTypes.Length} parameters but received {suppliedArgsLength}."); + } + + // Second, convert each supplied value to the type expected by the method + var runtime = (JSRuntimeBase)JSRuntime.Current; + var serializerStrategy = runtime.ArgSerializerStrategy; + for (var i = 0; i < suppliedArgsLength; i++) + { + if (parameterTypes[i] == typeof(JSAsyncCallResult)) + { + // For JS async call results, we have to defer the deserialization until + // later when we know what type it's meant to be deserialized as + suppliedArgs[i] = new JSAsyncCallResult(suppliedArgs[i]); + } + else + { + suppliedArgs[i] = serializerStrategy.DeserializeObject( + suppliedArgs[i], parameterTypes[i]); + } + } + + try + { + return methodInfo.Invoke(targetInstance, suppliedArgs); + } + catch (Exception ex) + { + throw UnwrapException(ex); + } + } + + /// + /// Receives notification that a call from .NET to JS has finished, marking the + /// associated as completed. + /// + /// The identifier for the function invocation. + /// A flag to indicate whether the invocation succeeded. + /// If is true, specifies the invocation result. If is false, gives the corresponding to the invocation failure. + [JSInvokable(nameof(DotNetDispatcher) + "." + nameof(EndInvoke))] + public static void EndInvoke(long asyncHandle, bool succeeded, JSAsyncCallResult result) + => ((JSRuntimeBase)JSRuntime.Current).EndInvokeJS(asyncHandle, succeeded, result.ResultOrException); + + /// + /// Releases the reference to the specified .NET object. This allows the .NET runtime + /// to garbage collect that object if there are no other references to it. + /// + /// To avoid leaking memory, the JavaScript side code must call this for every .NET + /// object it obtains a reference to. The exception is if that object is used for + /// the entire lifetime of a given user's session, in which case it is released + /// automatically when the JavaScript runtime is disposed. + /// + /// The identifier previously passed to JavaScript code. + [JSInvokable(nameof(DotNetDispatcher) + "." + nameof(ReleaseDotNetObject))] + public static void ReleaseDotNetObject(long dotNetObjectId) + { + // DotNetDispatcher only works with JSRuntimeBase instances. + var jsRuntime = (JSRuntimeBase)JSRuntime.Current; + jsRuntime.ArgSerializerStrategy.ReleaseDotNetObject(dotNetObjectId); + } + + private static (MethodInfo, Type[]) GetCachedMethodInfo(string assemblyName, string methodIdentifier) + { + if (string.IsNullOrWhiteSpace(assemblyName)) + { + throw new ArgumentException("Cannot be null, empty, or whitespace.", nameof(assemblyName)); + } + + if (string.IsNullOrWhiteSpace(methodIdentifier)) + { + throw new ArgumentException("Cannot be null, empty, or whitespace.", nameof(methodIdentifier)); + } + + var assemblyMethods = _cachedMethodsByAssembly.GetOrAdd(assemblyName, ScanAssemblyForCallableMethods); + if (assemblyMethods.TryGetValue(methodIdentifier, out var result)) + { + return result; + } + else + { + throw new ArgumentException($"The assembly '{assemblyName}' does not contain a public method with [{nameof(JSInvokableAttribute)}(\"{methodIdentifier}\")]."); + } + } + + private static IReadOnlyDictionary ScanAssemblyForCallableMethods(string assemblyName) + { + // TODO: Consider looking first for assembly-level attributes (i.e., if there are any, + // only use those) to avoid scanning, especially for framework assemblies. + var result = new Dictionary(); + var invokableMethods = GetRequiredLoadedAssembly(assemblyName) + .GetExportedTypes() + .SelectMany(type => type.GetMethods( + BindingFlags.Public | + BindingFlags.DeclaredOnly | + BindingFlags.Instance | + BindingFlags.Static)) + .Where(method => method.IsDefined(typeof(JSInvokableAttribute), inherit: false)); + foreach (var method in invokableMethods) + { + var identifier = method.GetCustomAttribute(false).Identifier ?? method.Name; + var parameterTypes = method.GetParameters().Select(p => p.ParameterType).ToArray(); + + try + { + result.Add(identifier, (method, parameterTypes)); + } + catch (ArgumentException) + { + if (result.ContainsKey(identifier)) + { + throw new InvalidOperationException($"The assembly '{assemblyName}' contains more than one " + + $"[JSInvokable] method with identifier '{identifier}'. All [JSInvokable] methods within the same " + + $"assembly must have different identifiers. You can pass a custom identifier as a parameter to " + + $"the [JSInvokable] attribute."); + } + else + { + throw; + } + } + } + + return result; + } + + private static Assembly GetRequiredLoadedAssembly(string assemblyName) + { + // We don't want to load assemblies on demand here, because we don't necessarily trust + // "assemblyName" to be something the developer intended to load. So only pick from the + // set of already-loaded assemblies. + // In some edge cases this might force developers to explicitly call something on the + // target assembly (from .NET) before they can invoke its allowed methods from JS. + var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies(); + return loadedAssemblies.FirstOrDefault(a => a.GetName().Name.Equals(assemblyName, StringComparison.Ordinal)) + ?? throw new ArgumentException($"There is no loaded assembly with the name '{assemblyName}'."); + } + + private static Exception UnwrapException(Exception ex) + { + while ((ex is AggregateException || ex is TargetInvocationException) && ex.InnerException != null) + { + ex = ex.InnerException; + } + + return ex; + } + } +} diff --git a/src/JSInterop/src/Microsoft.JSInterop/DotNetObjectRef.cs b/src/JSInterop/src/Microsoft.JSInterop/DotNetObjectRef.cs new file mode 100644 index 000000000000..aa62bee34124 --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop/DotNetObjectRef.cs @@ -0,0 +1,66 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Threading; + +namespace Microsoft.JSInterop +{ + /// + /// Wraps a JS interop argument, indicating that the value should not be serialized as JSON + /// but instead should be passed as a reference. + /// + /// To avoid leaking memory, the reference must later be disposed by JS code or by .NET code. + /// + public class DotNetObjectRef : IDisposable + { + /// + /// Gets the object instance represented by this wrapper. + /// + public object Value { get; } + + // We track an associated IJSRuntime purely so that this class can be IDisposable + // in the normal way. Developers are more likely to use objectRef.Dispose() than + // some less familiar API such as JSRuntime.Current.UntrackObjectRef(objectRef). + private IJSRuntime _attachedToRuntime; + + /// + /// Constructs an instance of . + /// + /// The value being wrapped. + public DotNetObjectRef(object value) + { + Value = value; + } + + /// + /// Ensures the is associated with the specified . + /// Developers do not normally need to invoke this manually, since it is called automatically by + /// framework code. + /// + /// The . + public void EnsureAttachedToJsRuntime(IJSRuntime runtime) + { + // The reason we populate _attachedToRuntime here rather than in the constructor + // is to ensure developers can't accidentally try to reuse DotNetObjectRef across + // different IJSRuntime instances. This method gets called as part of serializing + // the DotNetObjectRef during an interop call. + + var existingRuntime = Interlocked.CompareExchange(ref _attachedToRuntime, runtime, null); + if (existingRuntime != null && existingRuntime != runtime) + { + throw new InvalidOperationException($"The {nameof(DotNetObjectRef)} is already associated with a different {nameof(IJSRuntime)}. Do not attempt to re-use {nameof(DotNetObjectRef)} instances with multiple {nameof(IJSRuntime)} instances."); + } + } + + /// + /// Stops tracking this object reference, allowing it to be garbage collected + /// (if there are no other references to it). Once the instance is disposed, it + /// can no longer be used in interop calls from JavaScript code. + /// + public void Dispose() + { + _attachedToRuntime?.UntrackObjectRef(this); + } + } +} diff --git a/src/JSInterop/src/Microsoft.JSInterop/ICustomArgSerializer.cs b/src/JSInterop/src/Microsoft.JSInterop/ICustomArgSerializer.cs new file mode 100644 index 000000000000..f4012af8e9e2 --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop/ICustomArgSerializer.cs @@ -0,0 +1,22 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.JSInterop.Internal +{ + // This is "soft" internal because we're trying to avoid expanding JsonUtil into a sophisticated + // API. Developers who want that would be better served by using a different JSON package + // instead. Also the perf implications of the ICustomArgSerializer approach aren't ideal + // (it forces structs to be boxed, and returning a dictionary means lots more allocations + // and boxing of any value-typed properties). + + /// + /// Internal. Intended for framework use only. + /// + public interface ICustomArgSerializer + { + /// + /// Internal. Intended for framework use only. + /// + object ToJsonPrimitive(); + } +} diff --git a/src/JSInterop/src/Microsoft.JSInterop/IJSInProcessRuntime.cs b/src/JSInterop/src/Microsoft.JSInterop/IJSInProcessRuntime.cs new file mode 100644 index 000000000000..cae5126db22f --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop/IJSInProcessRuntime.cs @@ -0,0 +1,20 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.JSInterop +{ + /// + /// Represents an instance of a JavaScript runtime to which calls may be dispatched. + /// + public interface IJSInProcessRuntime : IJSRuntime + { + /// + /// Invokes the specified JavaScript function synchronously. + /// + /// The JSON-serializable return type. + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. + /// JSON-serializable arguments. + /// An instance of obtained by JSON-deserializing the return value. + T Invoke(string identifier, params object[] args); + } +} diff --git a/src/JSInterop/src/Microsoft.JSInterop/IJSRuntime.cs b/src/JSInterop/src/Microsoft.JSInterop/IJSRuntime.cs new file mode 100644 index 000000000000..b56d1f0089fa --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop/IJSRuntime.cs @@ -0,0 +1,32 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Threading.Tasks; + +namespace Microsoft.JSInterop +{ + /// + /// Represents an instance of a JavaScript runtime to which calls may be dispatched. + /// + public interface IJSRuntime + { + /// + /// Invokes the specified JavaScript function asynchronously. + /// + /// The JSON-serializable return type. + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. + /// JSON-serializable arguments. + /// An instance of obtained by JSON-deserializing the return value. + Task InvokeAsync(string identifier, params object[] args); + + /// + /// Stops tracking the .NET object represented by the . + /// This allows it to be garbage collected (if nothing else holds a reference to it) + /// and means the JS-side code can no longer invoke methods on the instance or pass + /// it as an argument to subsequent calls. + /// + /// The reference to stop tracking. + /// This method is called automatically by . + void UntrackObjectRef(DotNetObjectRef dotNetObjectRef); + } +} diff --git a/src/JSInterop/src/Microsoft.JSInterop/InteropArgSerializerStrategy.cs b/src/JSInterop/src/Microsoft.JSInterop/InteropArgSerializerStrategy.cs new file mode 100644 index 000000000000..663c1cf85aba --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop/InteropArgSerializerStrategy.cs @@ -0,0 +1,121 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.JSInterop.Internal; +using SimpleJson; +using System; +using System.Collections.Generic; + +namespace Microsoft.JSInterop +{ + internal class InteropArgSerializerStrategy : PocoJsonSerializerStrategy + { + private readonly JSRuntimeBase _jsRuntime; + private const string _dotNetObjectPrefix = "__dotNetObject:"; + private object _storageLock = new object(); + private long _nextId = 1; // Start at 1, because 0 signals "no object" + private Dictionary _trackedRefsById = new Dictionary(); + private Dictionary _trackedIdsByRef = new Dictionary(); + + public InteropArgSerializerStrategy(JSRuntimeBase jsRuntime) + { + _jsRuntime = jsRuntime ?? throw new ArgumentNullException(nameof(jsRuntime)); + } + + protected override bool TrySerializeKnownTypes(object input, out object output) + { + switch (input) + { + case DotNetObjectRef marshalByRefValue: + EnsureDotNetObjectTracked(marshalByRefValue, out var id); + + // Special value format recognized by the code in Microsoft.JSInterop.js + // If we have to make it more clash-resistant, we can do + output = _dotNetObjectPrefix + id; + + return true; + + case ICustomArgSerializer customArgSerializer: + output = customArgSerializer.ToJsonPrimitive(); + return true; + + default: + return base.TrySerializeKnownTypes(input, out output); + } + } + + public override object DeserializeObject(object value, Type type) + { + if (value is string valueString) + { + if (valueString.StartsWith(_dotNetObjectPrefix)) + { + var dotNetObjectId = long.Parse(valueString.Substring(_dotNetObjectPrefix.Length)); + return FindDotNetObject(dotNetObjectId); + } + } + + return base.DeserializeObject(value, type); + } + + public object FindDotNetObject(long dotNetObjectId) + { + lock (_storageLock) + { + return _trackedRefsById.TryGetValue(dotNetObjectId, out var dotNetObjectRef) + ? dotNetObjectRef.Value + : throw new ArgumentException($"There is no tracked object with id '{dotNetObjectId}'. Perhaps the reference was already released.", nameof(dotNetObjectId)); + } + } + + /// + /// Stops tracking the specified .NET object reference. + /// This overload is typically invoked from JS code via JS interop. + /// + /// The ID of the . + public void ReleaseDotNetObject(long dotNetObjectId) + { + lock (_storageLock) + { + if (_trackedRefsById.TryGetValue(dotNetObjectId, out var dotNetObjectRef)) + { + _trackedRefsById.Remove(dotNetObjectId); + _trackedIdsByRef.Remove(dotNetObjectRef); + } + } + } + + /// + /// Stops tracking the specified .NET object reference. + /// This overload is typically invoked from .NET code by . + /// + /// The . + public void ReleaseDotNetObject(DotNetObjectRef dotNetObjectRef) + { + lock (_storageLock) + { + if (_trackedIdsByRef.TryGetValue(dotNetObjectRef, out var dotNetObjectId)) + { + _trackedRefsById.Remove(dotNetObjectId); + _trackedIdsByRef.Remove(dotNetObjectRef); + } + } + } + + private void EnsureDotNetObjectTracked(DotNetObjectRef dotNetObjectRef, out long dotNetObjectId) + { + dotNetObjectRef.EnsureAttachedToJsRuntime(_jsRuntime); + + lock (_storageLock) + { + // Assign an ID only if it doesn't already have one + if (!_trackedIdsByRef.TryGetValue(dotNetObjectRef, out dotNetObjectId)) + { + dotNetObjectId = _nextId++; + _trackedRefsById.Add(dotNetObjectId, dotNetObjectRef); + _trackedIdsByRef.Add(dotNetObjectRef, dotNetObjectId); + } + } + } + } +} diff --git a/src/JSInterop/src/Microsoft.JSInterop/JSAsyncCallResult.cs b/src/JSInterop/src/Microsoft.JSInterop/JSAsyncCallResult.cs new file mode 100644 index 000000000000..d46517eddc69 --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop/JSAsyncCallResult.cs @@ -0,0 +1,36 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.JSInterop.Internal +{ + // This type takes care of a special case in handling the result of an async call from + // .NET to JS. The information about what type the result should be exists only on the + // corresponding TaskCompletionSource. We don't have that information at the time + // that we deserialize the incoming argsJson before calling DotNetDispatcher.EndInvoke. + // Declaring the EndInvoke parameter type as JSAsyncCallResult defers the deserialization + // until later when we have access to the TaskCompletionSource. + // + // There's no reason why developers would need anything similar to this in user code, + // because this is the mechanism by which we resolve the incoming argsJson to the correct + // user types before completing calls. + // + // It's marked as 'public' only because it has to be for use as an argument on a + // [JSInvokable] method. + + /// + /// Intended for framework use only. + /// + public class JSAsyncCallResult + { + internal object ResultOrException { get; } + + /// + /// Constructs an instance of . + /// + /// The result of the call. + internal JSAsyncCallResult(object resultOrException) + { + ResultOrException = resultOrException; + } + } +} diff --git a/src/JSInterop/src/Microsoft.JSInterop/JSException.cs b/src/JSInterop/src/Microsoft.JSInterop/JSException.cs new file mode 100644 index 000000000000..2929f693114e --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop/JSException.cs @@ -0,0 +1,21 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.JSInterop +{ + /// + /// Represents errors that occur during an interop call from .NET to JavaScript. + /// + public class JSException : Exception + { + /// + /// Constructs an instance of . + /// + /// The exception message. + public JSException(string message) : base(message) + { + } + } +} diff --git a/src/JSInterop/src/Microsoft.JSInterop/JSInProcessRuntimeBase.cs b/src/JSInterop/src/Microsoft.JSInterop/JSInProcessRuntimeBase.cs new file mode 100644 index 000000000000..49a47d0595a3 --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop/JSInProcessRuntimeBase.cs @@ -0,0 +1,32 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.JSInterop +{ + /// + /// Abstract base class for an in-process JavaScript runtime. + /// + public abstract class JSInProcessRuntimeBase : JSRuntimeBase, IJSInProcessRuntime + { + /// + /// Invokes the specified JavaScript function synchronously. + /// + /// The JSON-serializable return type. + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. + /// JSON-serializable arguments. + /// An instance of obtained by JSON-deserializing the return value. + public T Invoke(string identifier, params object[] args) + { + var resultJson = InvokeJS(identifier, Json.Serialize(args, ArgSerializerStrategy)); + return Json.Deserialize(resultJson, ArgSerializerStrategy); + } + + /// + /// Performs a synchronous function invocation. + /// + /// The identifier for the function to invoke. + /// A JSON representation of the arguments. + /// A JSON representation of the result. + protected abstract string InvokeJS(string identifier, string argsJson); + } +} diff --git a/src/JSInterop/src/Microsoft.JSInterop/JSInvokableAttribute.cs b/src/JSInterop/src/Microsoft.JSInterop/JSInvokableAttribute.cs new file mode 100644 index 000000000000..e037078cba46 --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop/JSInvokableAttribute.cs @@ -0,0 +1,48 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.JSInterop +{ + /// + /// Identifies a .NET method as allowing invocation from JavaScript code. + /// Any method marked with this attribute may receive arbitrary parameter values + /// from untrusted callers. All inputs should be validated carefully. + /// + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + public class JSInvokableAttribute : Attribute + { + /// + /// Gets the identifier for the method. The identifier must be unique within the scope + /// of an assembly. + /// + /// If not set, the identifier is taken from the name of the method. In this case the + /// method name must be unique within the assembly. + /// + public string Identifier { get; } + + /// + /// Constructs an instance of without setting + /// an identifier for the method. + /// + public JSInvokableAttribute() + { + } + + /// + /// Constructs an instance of using the specified + /// identifier. + /// + /// An identifier for the method, which must be unique within the scope of the assembly. + public JSInvokableAttribute(string identifier) + { + if (string.IsNullOrEmpty(identifier)) + { + throw new ArgumentException("Cannot be null or empty", nameof(identifier)); + } + + Identifier = identifier; + } + } +} diff --git a/src/JSInterop/src/Microsoft.JSInterop/JSRuntime.cs b/src/JSInterop/src/Microsoft.JSInterop/JSRuntime.cs new file mode 100644 index 000000000000..5a9830fa3526 --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop/JSRuntime.cs @@ -0,0 +1,34 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Threading; + +namespace Microsoft.JSInterop +{ + /// + /// Provides mechanisms for accessing the current . + /// + public static class JSRuntime + { + private static AsyncLocal _currentJSRuntime + = new AsyncLocal(); + + /// + /// Gets the current , if any. + /// + public static IJSRuntime Current => _currentJSRuntime.Value; + + /// + /// Sets the current JS runtime to the supplied instance. + /// + /// This is intended for framework use. Developers should not normally need to call this method. + /// + /// The new current . + public static void SetCurrentJSRuntime(IJSRuntime instance) + { + _currentJSRuntime.Value = instance + ?? throw new ArgumentNullException(nameof(instance)); + } + } +} diff --git a/src/JSInterop/src/Microsoft.JSInterop/JSRuntimeBase.cs b/src/JSInterop/src/Microsoft.JSInterop/JSRuntimeBase.cs new file mode 100644 index 000000000000..09379396cf5e --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop/JSRuntimeBase.cs @@ -0,0 +1,116 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Concurrent; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.JSInterop +{ + /// + /// Abstract base class for a JavaScript runtime. + /// + public abstract class JSRuntimeBase : IJSRuntime + { + private long _nextPendingTaskId = 1; // Start at 1 because zero signals "no response needed" + private readonly ConcurrentDictionary _pendingTasks + = new ConcurrentDictionary(); + + internal InteropArgSerializerStrategy ArgSerializerStrategy { get; } + + /// + /// Constructs an instance of . + /// + public JSRuntimeBase() + { + ArgSerializerStrategy = new InteropArgSerializerStrategy(this); + } + + /// + public void UntrackObjectRef(DotNetObjectRef dotNetObjectRef) + => ArgSerializerStrategy.ReleaseDotNetObject(dotNetObjectRef); + + /// + /// Invokes the specified JavaScript function asynchronously. + /// + /// The JSON-serializable return type. + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. + /// JSON-serializable arguments. + /// An instance of obtained by JSON-deserializing the return value. + public Task InvokeAsync(string identifier, params object[] args) + { + // We might consider also adding a default timeout here in case we don't want to + // risk a memory leak in the scenario where the JS-side code is failing to complete + // the operation. + + var taskId = Interlocked.Increment(ref _nextPendingTaskId); + var tcs = new TaskCompletionSource(); + _pendingTasks[taskId] = tcs; + + try + { + var argsJson = args?.Length > 0 + ? Json.Serialize(args, ArgSerializerStrategy) + : null; + BeginInvokeJS(taskId, identifier, argsJson); + return tcs.Task; + } + catch + { + _pendingTasks.TryRemove(taskId, out _); + throw; + } + } + + /// + /// Begins an asynchronous function invocation. + /// + /// The identifier for the function invocation, or zero if no async callback is required. + /// The identifier for the function to invoke. + /// A JSON representation of the arguments. + protected abstract void BeginInvokeJS(long asyncHandle, string identifier, string argsJson); + + internal void EndInvokeDotNet(string callId, bool success, object resultOrException) + { + // For failures, the common case is to call EndInvokeDotNet with the Exception object. + // For these we'll serialize as something that's useful to receive on the JS side. + // If the value is not an Exception, we'll just rely on it being directly JSON-serializable. + if (!success && resultOrException is Exception) + { + resultOrException = resultOrException.ToString(); + } + + // We pass 0 as the async handle because we don't want the JS-side code to + // send back any notification (we're just providing a result for an existing async call) + BeginInvokeJS(0, "DotNet.jsCallDispatcher.endInvokeDotNetFromJS", Json.Serialize(new[] { + callId, + success, + resultOrException + }, ArgSerializerStrategy)); + } + + internal void EndInvokeJS(long asyncHandle, bool succeeded, object resultOrException) + { + if (!_pendingTasks.TryRemove(asyncHandle, out var tcs)) + { + throw new ArgumentException($"There is no pending task with handle '{asyncHandle}'."); + } + + if (succeeded) + { + var resultType = TaskGenericsUtil.GetTaskCompletionSourceResultType(tcs); + if (resultOrException is SimpleJson.JsonObject || resultOrException is SimpleJson.JsonArray) + { + resultOrException = ArgSerializerStrategy.DeserializeObject(resultOrException, resultType); + } + + TaskGenericsUtil.SetTaskCompletionSourceResult(tcs, resultOrException); + } + else + { + TaskGenericsUtil.SetTaskCompletionSourceException(tcs, new JSException(resultOrException.ToString())); + } + } + } +} diff --git a/src/JSInterop/src/Microsoft.JSInterop/Json/CamelCase.cs b/src/JSInterop/src/Microsoft.JSInterop/Json/CamelCase.cs new file mode 100644 index 000000000000..8caae1387b08 --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop/Json/CamelCase.cs @@ -0,0 +1,59 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.JSInterop +{ + internal static class CamelCase + { + public static string MemberNameToCamelCase(string value) + { + if (string.IsNullOrEmpty(value)) + { + throw new ArgumentException( + $"The value '{value ?? "null"}' is not a valid member name.", + nameof(value)); + } + + // If we don't need to modify the value, bail out without creating a char array + if (!char.IsUpper(value[0])) + { + return value; + } + + // We have to modify at least one character + var chars = value.ToCharArray(); + + var length = chars.Length; + if (length < 2 || !char.IsUpper(chars[1])) + { + // Only the first character needs to be modified + // Note that this branch is functionally necessary, because the 'else' branch below + // never looks at char[1]. It's always looking at the n+2 character. + chars[0] = char.ToLowerInvariant(chars[0]); + } + else + { + // If chars[0] and chars[1] are both upper, then we'll lowercase the first char plus + // any consecutive uppercase ones, stopping if we find any char that is followed by a + // non-uppercase one + var i = 0; + while (i < length) + { + chars[i] = char.ToLowerInvariant(chars[i]); + + i++; + + // If the next-plus-one char isn't also uppercase, then we're now on the last uppercase, so stop + if (i < length - 1 && !char.IsUpper(chars[i + 1])) + { + break; + } + } + } + + return new string(chars); + } + } +} diff --git a/src/JSInterop/src/Microsoft.JSInterop/Json/Json.cs b/src/JSInterop/src/Microsoft.JSInterop/Json/Json.cs new file mode 100644 index 000000000000..7275dfe42720 --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop/Json/Json.cs @@ -0,0 +1,39 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.JSInterop +{ + /// + /// Provides mechanisms for converting between .NET objects and JSON strings for use + /// when making calls to JavaScript functions via . + /// + /// Warning: This is not intended as a general-purpose JSON library. It is only intended + /// for use when making calls via . Eventually its implementation + /// will be replaced by something more general-purpose. + /// + public static class Json + { + /// + /// Serializes the value as a JSON string. + /// + /// The value to serialize. + /// The JSON string. + public static string Serialize(object value) + => SimpleJson.SimpleJson.SerializeObject(value); + + internal static string Serialize(object value, SimpleJson.IJsonSerializerStrategy serializerStrategy) + => SimpleJson.SimpleJson.SerializeObject(value, serializerStrategy); + + /// + /// Deserializes the JSON string, creating an object of the specified generic type. + /// + /// The type of object to create. + /// The JSON string. + /// An object of the specified type. + public static T Deserialize(string json) + => SimpleJson.SimpleJson.DeserializeObject(json); + + internal static T Deserialize(string json, SimpleJson.IJsonSerializerStrategy serializerStrategy) + => SimpleJson.SimpleJson.DeserializeObject(json, serializerStrategy); + } +} diff --git a/src/JSInterop/src/Microsoft.JSInterop/Json/SimpleJson/README.txt b/src/JSInterop/src/Microsoft.JSInterop/Json/SimpleJson/README.txt new file mode 100644 index 000000000000..5e58eb710615 --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop/Json/SimpleJson/README.txt @@ -0,0 +1,24 @@ +SimpleJson is from https://github.com/facebook-csharp-sdk/simple-json + +LICENSE (from https://github.com/facebook-csharp-sdk/simple-json/blob/08b6871e8f63e866810d25e7a03c48502c9a234b/LICENSE.txt): +===== +Copyright (c) 2011, The Outercurve Foundation + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/src/JSInterop/src/Microsoft.JSInterop/Json/SimpleJson/SimpleJson.cs b/src/JSInterop/src/Microsoft.JSInterop/Json/SimpleJson/SimpleJson.cs new file mode 100644 index 000000000000..d12c6fae30c7 --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop/Json/SimpleJson/SimpleJson.cs @@ -0,0 +1,2201 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) 2011, The Outercurve Foundation. +// +// Licensed under the MIT License (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.opensource.org/licenses/mit-license.php +// +// 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. +// +// Nathan Totten (ntotten.com), Jim Zimmerman (jimzimmerman.com) and Prabir Shrestha (prabir.me) +// https://github.com/facebook-csharp-sdk/simple-json +//----------------------------------------------------------------------- + +// VERSION: + +// NOTE: uncomment the following line to make SimpleJson class internal. +#define SIMPLE_JSON_INTERNAL + +// NOTE: uncomment the following line to make JsonArray and JsonObject class internal. +#define SIMPLE_JSON_OBJARRAYINTERNAL + +// NOTE: uncomment the following line to enable dynamic support. +//#define SIMPLE_JSON_DYNAMIC + +// NOTE: uncomment the following line to enable DataContract support. +//#define SIMPLE_JSON_DATACONTRACT + +// NOTE: uncomment the following line to enable IReadOnlyCollection and IReadOnlyList support. +//#define SIMPLE_JSON_READONLY_COLLECTIONS + +// NOTE: uncomment the following line to disable linq expressions/compiled lambda (better performance) instead of method.invoke(). +// define if you are using .net framework <= 3.0 or < WP7.5 +#define SIMPLE_JSON_NO_LINQ_EXPRESSION + +// NOTE: uncomment the following line if you are compiling under Window Metro style application/library. +// usually already defined in properties +//#define NETFX_CORE; + +// If you are targetting WinStore, WP8 and NET4.5+ PCL make sure to #define SIMPLE_JSON_TYPEINFO; + +// original json parsing code from http://techblog.procurios.nl/k/618/news/view/14605/14863/How-do-I-write-my-own-parser-for-JSON.html + +#if NETFX_CORE +#define SIMPLE_JSON_TYPEINFO +#endif + +using System; +using System.CodeDom.Compiler; +using System.Collections; +using System.Collections.Generic; +#if !SIMPLE_JSON_NO_LINQ_EXPRESSION +using System.Linq.Expressions; +#endif +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +#if SIMPLE_JSON_DYNAMIC +using System.Dynamic; +#endif +using System.Globalization; +using System.Reflection; +using System.Runtime.Serialization; +using System.Text; +using Microsoft.JSInterop; +using SimpleJson.Reflection; + +// ReSharper disable LoopCanBeConvertedToQuery +// ReSharper disable RedundantExplicitArrayCreation +// ReSharper disable SuggestUseVarKeywordEvident +namespace SimpleJson +{ + /// + /// Represents the json array. + /// + [GeneratedCode("simple-json", "1.0.0")] + [EditorBrowsable(EditorBrowsableState.Never)] + [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] +#if SIMPLE_JSON_OBJARRAYINTERNAL + internal +#else + public +#endif + class JsonArray : List + { + /// + /// Initializes a new instance of the class. + /// + public JsonArray() { } + + /// + /// Initializes a new instance of the class. + /// + /// The capacity of the json array. + public JsonArray(int capacity) : base(capacity) { } + + /// + /// The json representation of the array. + /// + /// The json representation of the array. + public override string ToString() + { + return SimpleJson.SerializeObject(this) ?? string.Empty; + } + } + + /// + /// Represents the json object. + /// + [GeneratedCode("simple-json", "1.0.0")] + [EditorBrowsable(EditorBrowsableState.Never)] + [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] +#if SIMPLE_JSON_OBJARRAYINTERNAL + internal +#else + public +#endif + class JsonObject : +#if SIMPLE_JSON_DYNAMIC + DynamicObject, +#endif + IDictionary + { + /// + /// The internal member dictionary. + /// + private readonly Dictionary _members; + + /// + /// Initializes a new instance of . + /// + public JsonObject() + { + _members = new Dictionary(); + } + + /// + /// Initializes a new instance of . + /// + /// The implementation to use when comparing keys, or null to use the default for the type of the key. + public JsonObject(IEqualityComparer comparer) + { + _members = new Dictionary(comparer); + } + + /// + /// Gets the at the specified index. + /// + /// + public object this[int index] + { + get { return GetAtIndex(_members, index); } + } + + internal static object GetAtIndex(IDictionary obj, int index) + { + if (obj == null) + throw new ArgumentNullException("obj"); + if (index >= obj.Count) + throw new ArgumentOutOfRangeException("index"); + int i = 0; + foreach (KeyValuePair o in obj) + if (i++ == index) return o.Value; + return null; + } + + /// + /// Adds the specified key. + /// + /// The key. + /// The value. + public void Add(string key, object value) + { + _members.Add(key, value); + } + + /// + /// Determines whether the specified key contains key. + /// + /// The key. + /// + /// true if the specified key contains key; otherwise, false. + /// + public bool ContainsKey(string key) + { + return _members.ContainsKey(key); + } + + /// + /// Gets the keys. + /// + /// The keys. + public ICollection Keys + { + get { return _members.Keys; } + } + + /// + /// Removes the specified key. + /// + /// The key. + /// + public bool Remove(string key) + { + return _members.Remove(key); + } + + /// + /// Tries the get value. + /// + /// The key. + /// The value. + /// + public bool TryGetValue(string key, out object value) + { + return _members.TryGetValue(key, out value); + } + + /// + /// Gets the values. + /// + /// The values. + public ICollection Values + { + get { return _members.Values; } + } + + /// + /// Gets or sets the with the specified key. + /// + /// + public object this[string key] + { + get { return _members[key]; } + set { _members[key] = value; } + } + + /// + /// Adds the specified item. + /// + /// The item. + public void Add(KeyValuePair item) + { + _members.Add(item.Key, item.Value); + } + + /// + /// Clears this instance. + /// + public void Clear() + { + _members.Clear(); + } + + /// + /// Determines whether [contains] [the specified item]. + /// + /// The item. + /// + /// true if [contains] [the specified item]; otherwise, false. + /// + public bool Contains(KeyValuePair item) + { + return _members.ContainsKey(item.Key) && _members[item.Key] == item.Value; + } + + /// + /// Copies to. + /// + /// The array. + /// Index of the array. + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + if (array == null) throw new ArgumentNullException("array"); + int num = Count; + foreach (KeyValuePair kvp in this) + { + array[arrayIndex++] = kvp; + if (--num <= 0) + return; + } + } + + /// + /// Gets the count. + /// + /// The count. + public int Count + { + get { return _members.Count; } + } + + /// + /// Gets a value indicating whether this instance is read only. + /// + /// + /// true if this instance is read only; otherwise, false. + /// + public bool IsReadOnly + { + get { return false; } + } + + /// + /// Removes the specified item. + /// + /// The item. + /// + public bool Remove(KeyValuePair item) + { + return _members.Remove(item.Key); + } + + /// + /// Gets the enumerator. + /// + /// + public IEnumerator> GetEnumerator() + { + return _members.GetEnumerator(); + } + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// + IEnumerator IEnumerable.GetEnumerator() + { + return _members.GetEnumerator(); + } + + /// + /// Returns a json that represents the current . + /// + /// + /// A json that represents the current . + /// + public override string ToString() + { + return SimpleJson.SerializeObject(this); + } + +#if SIMPLE_JSON_DYNAMIC + /// + /// Provides implementation for type conversion operations. Classes derived from the class can override this method to specify dynamic behavior for operations that convert an object from one type to another. + /// + /// Provides information about the conversion operation. The binder.Type property provides the type to which the object must be converted. For example, for the statement (String)sampleObject in C# (CType(sampleObject, Type) in Visual Basic), where sampleObject is an instance of the class derived from the class, binder.Type returns the type. The binder.Explicit property provides information about the kind of conversion that occurs. It returns true for explicit conversion and false for implicit conversion. + /// The result of the type conversion operation. + /// + /// Alwasy returns true. + /// + public override bool TryConvert(ConvertBinder binder, out object result) + { + // + if (binder == null) + throw new ArgumentNullException("binder"); + // + Type targetType = binder.Type; + + if ((targetType == typeof(IEnumerable)) || + (targetType == typeof(IEnumerable>)) || + (targetType == typeof(IDictionary)) || + (targetType == typeof(IDictionary))) + { + result = this; + return true; + } + + return base.TryConvert(binder, out result); + } + + /// + /// Provides the implementation for operations that delete an object member. This method is not intended for use in C# or Visual Basic. + /// + /// Provides information about the deletion. + /// + /// Alwasy returns true. + /// + public override bool TryDeleteMember(DeleteMemberBinder binder) + { + // + if (binder == null) + throw new ArgumentNullException("binder"); + // + return _members.Remove(binder.Name); + } + + /// + /// Provides the implementation for operations that get a value by index. Classes derived from the class can override this method to specify dynamic behavior for indexing operations. + /// + /// Provides information about the operation. + /// The indexes that are used in the operation. For example, for the sampleObject[3] operation in C# (sampleObject(3) in Visual Basic), where sampleObject is derived from the DynamicObject class, is equal to 3. + /// The result of the index operation. + /// + /// Alwasy returns true. + /// + public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) + { + if (indexes == null) throw new ArgumentNullException("indexes"); + if (indexes.Length == 1) + { + result = ((IDictionary)this)[(string)indexes[0]]; + return true; + } + result = null; + return true; + } + + /// + /// Provides the implementation for operations that get member values. Classes derived from the class can override this method to specify dynamic behavior for operations such as getting a value for a property. + /// + /// Provides information about the object that called the dynamic operation. The binder.Name property provides the name of the member on which the dynamic operation is performed. For example, for the Console.WriteLine(sampleObject.SampleProperty) statement, where sampleObject is an instance of the class derived from the class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies whether the member name is case-sensitive. + /// The result of the get operation. For example, if the method is called for a property, you can assign the property value to . + /// + /// Alwasy returns true. + /// + public override bool TryGetMember(GetMemberBinder binder, out object result) + { + object value; + if (_members.TryGetValue(binder.Name, out value)) + { + result = value; + return true; + } + result = null; + return true; + } + + /// + /// Provides the implementation for operations that set a value by index. Classes derived from the class can override this method to specify dynamic behavior for operations that access objects by a specified index. + /// + /// Provides information about the operation. + /// The indexes that are used in the operation. For example, for the sampleObject[3] = 10 operation in C# (sampleObject(3) = 10 in Visual Basic), where sampleObject is derived from the class, is equal to 3. + /// The value to set to the object that has the specified index. For example, for the sampleObject[3] = 10 operation in C# (sampleObject(3) = 10 in Visual Basic), where sampleObject is derived from the class, is equal to 10. + /// + /// true if the operation is successful; otherwise, false. If this method returns false, the run-time binder of the language determines the behavior. (In most cases, a language-specific run-time exception is thrown. + /// + public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value) + { + if (indexes == null) throw new ArgumentNullException("indexes"); + if (indexes.Length == 1) + { + ((IDictionary)this)[(string)indexes[0]] = value; + return true; + } + return base.TrySetIndex(binder, indexes, value); + } + + /// + /// Provides the implementation for operations that set member values. Classes derived from the class can override this method to specify dynamic behavior for operations such as setting a value for a property. + /// + /// Provides information about the object that called the dynamic operation. The binder.Name property provides the name of the member to which the value is being assigned. For example, for the statement sampleObject.SampleProperty = "Test", where sampleObject is an instance of the class derived from the class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies whether the member name is case-sensitive. + /// The value to set to the member. For example, for sampleObject.SampleProperty = "Test", where sampleObject is an instance of the class derived from the class, the is "Test". + /// + /// true if the operation is successful; otherwise, false. If this method returns false, the run-time binder of the language determines the behavior. (In most cases, a language-specific run-time exception is thrown.) + /// + public override bool TrySetMember(SetMemberBinder binder, object value) + { + // + if (binder == null) + throw new ArgumentNullException("binder"); + // + _members[binder.Name] = value; + return true; + } + + /// + /// Returns the enumeration of all dynamic member names. + /// + /// + /// A sequence that contains dynamic member names. + /// + public override IEnumerable GetDynamicMemberNames() + { + foreach (var key in Keys) + yield return key; + } +#endif + } +} + +namespace SimpleJson +{ + /// + /// This class encodes and decodes JSON strings. + /// Spec. details, see http://www.json.org/ + /// + /// JSON uses Arrays and Objects. These correspond here to the datatypes JsonArray(IList<object>) and JsonObject(IDictionary<string,object>). + /// All numbers are parsed to doubles. + /// + [GeneratedCode("simple-json", "1.0.0")] +#if SIMPLE_JSON_INTERNAL + internal +#else + public +#endif + static class SimpleJson + { + private const int TOKEN_NONE = 0; + private const int TOKEN_CURLY_OPEN = 1; + private const int TOKEN_CURLY_CLOSE = 2; + private const int TOKEN_SQUARED_OPEN = 3; + private const int TOKEN_SQUARED_CLOSE = 4; + private const int TOKEN_COLON = 5; + private const int TOKEN_COMMA = 6; + private const int TOKEN_STRING = 7; + private const int TOKEN_NUMBER = 8; + private const int TOKEN_TRUE = 9; + private const int TOKEN_FALSE = 10; + private const int TOKEN_NULL = 11; + private const int BUILDER_CAPACITY = 2000; + + private static readonly char[] EscapeTable; + private static readonly char[] EscapeCharacters = new char[] { '"', '\\', '\b', '\f', '\n', '\r', '\t' }; + private static readonly string EscapeCharactersString = new string(EscapeCharacters); + + static SimpleJson() + { + EscapeTable = new char[93]; + EscapeTable['"'] = '"'; + EscapeTable['\\'] = '\\'; + EscapeTable['\b'] = 'b'; + EscapeTable['\f'] = 'f'; + EscapeTable['\n'] = 'n'; + EscapeTable['\r'] = 'r'; + EscapeTable['\t'] = 't'; + } + + /// + /// Parses the string json into a value + /// + /// A JSON string. + /// An IList<object>, a IDictionary<string,object>, a double, a string, null, true, or false + public static object DeserializeObject(string json) + { + object obj; + if (TryDeserializeObject(json, out obj)) + return obj; + throw new SerializationException("Invalid JSON string"); + } + + /// + /// Try parsing the json string into a value. + /// + /// + /// A JSON string. + /// + /// + /// The object. + /// + /// + /// Returns true if successful otherwise false. + /// + [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification="Need to support .NET 2")] + public static bool TryDeserializeObject(string json, out object obj) + { + bool success = true; + if (json != null) + { + char[] charArray = json.ToCharArray(); + int index = 0; + obj = ParseValue(charArray, ref index, ref success); + } + else + obj = null; + + return success; + } + + public static object DeserializeObject(string json, Type type, IJsonSerializerStrategy jsonSerializerStrategy) + { + object jsonObject = DeserializeObject(json); + return type == null || jsonObject != null && ReflectionUtils.IsAssignableFrom(jsonObject.GetType(), type) + ? jsonObject + : (jsonSerializerStrategy ?? CurrentJsonSerializerStrategy).DeserializeObject(jsonObject, type); + } + + public static object DeserializeObject(string json, Type type) + { + return DeserializeObject(json, type, null); + } + + public static T DeserializeObject(string json, IJsonSerializerStrategy jsonSerializerStrategy) + { + return (T)DeserializeObject(json, typeof(T), jsonSerializerStrategy); + } + + public static T DeserializeObject(string json) + { + return (T)DeserializeObject(json, typeof(T), null); + } + + /// + /// Converts a IDictionary<string,object> / IList<object> object into a JSON string + /// + /// A IDictionary<string,object> / IList<object> + /// Serializer strategy to use + /// A JSON encoded string, or null if object 'json' is not serializable + public static string SerializeObject(object json, IJsonSerializerStrategy jsonSerializerStrategy) + { + StringBuilder builder = new StringBuilder(BUILDER_CAPACITY); + bool success = SerializeValue(jsonSerializerStrategy, json, builder); + return (success ? builder.ToString() : null); + } + + public static string SerializeObject(object json) + { + return SerializeObject(json, CurrentJsonSerializerStrategy); + } + + public static string EscapeToJavascriptString(string jsonString) + { + if (string.IsNullOrEmpty(jsonString)) + return jsonString; + + StringBuilder sb = new StringBuilder(); + char c; + + for (int i = 0; i < jsonString.Length; ) + { + c = jsonString[i++]; + + if (c == '\\') + { + int remainingLength = jsonString.Length - i; + if (remainingLength >= 2) + { + char lookahead = jsonString[i]; + if (lookahead == '\\') + { + sb.Append('\\'); + ++i; + } + else if (lookahead == '"') + { + sb.Append("\""); + ++i; + } + else if (lookahead == 't') + { + sb.Append('\t'); + ++i; + } + else if (lookahead == 'b') + { + sb.Append('\b'); + ++i; + } + else if (lookahead == 'n') + { + sb.Append('\n'); + ++i; + } + else if (lookahead == 'r') + { + sb.Append('\r'); + ++i; + } + } + } + else + { + sb.Append(c); + } + } + return sb.ToString(); + } + + static IDictionary ParseObject(char[] json, ref int index, ref bool success) + { + IDictionary table = new JsonObject(); + int token; + + // { + NextToken(json, ref index); + + bool done = false; + while (!done) + { + token = LookAhead(json, index); + if (token == TOKEN_NONE) + { + success = false; + return null; + } + else if (token == TOKEN_COMMA) + NextToken(json, ref index); + else if (token == TOKEN_CURLY_CLOSE) + { + NextToken(json, ref index); + return table; + } + else + { + // name + string name = ParseString(json, ref index, ref success); + if (!success) + { + success = false; + return null; + } + // : + token = NextToken(json, ref index); + if (token != TOKEN_COLON) + { + success = false; + return null; + } + // value + object value = ParseValue(json, ref index, ref success); + if (!success) + { + success = false; + return null; + } + table[name] = value; + } + } + return table; + } + + static JsonArray ParseArray(char[] json, ref int index, ref bool success) + { + JsonArray array = new JsonArray(); + + // [ + NextToken(json, ref index); + + bool done = false; + while (!done) + { + int token = LookAhead(json, index); + if (token == TOKEN_NONE) + { + success = false; + return null; + } + else if (token == TOKEN_COMMA) + NextToken(json, ref index); + else if (token == TOKEN_SQUARED_CLOSE) + { + NextToken(json, ref index); + break; + } + else + { + object value = ParseValue(json, ref index, ref success); + if (!success) + return null; + array.Add(value); + } + } + return array; + } + + static object ParseValue(char[] json, ref int index, ref bool success) + { + switch (LookAhead(json, index)) + { + case TOKEN_STRING: + return ParseString(json, ref index, ref success); + case TOKEN_NUMBER: + return ParseNumber(json, ref index, ref success); + case TOKEN_CURLY_OPEN: + return ParseObject(json, ref index, ref success); + case TOKEN_SQUARED_OPEN: + return ParseArray(json, ref index, ref success); + case TOKEN_TRUE: + NextToken(json, ref index); + return true; + case TOKEN_FALSE: + NextToken(json, ref index); + return false; + case TOKEN_NULL: + NextToken(json, ref index); + return null; + case TOKEN_NONE: + break; + } + success = false; + return null; + } + + static string ParseString(char[] json, ref int index, ref bool success) + { + StringBuilder s = new StringBuilder(BUILDER_CAPACITY); + char c; + + EatWhitespace(json, ref index); + + // " + c = json[index++]; + bool complete = false; + while (!complete) + { + if (index == json.Length) + break; + + c = json[index++]; + if (c == '"') + { + complete = true; + break; + } + else if (c == '\\') + { + if (index == json.Length) + break; + c = json[index++]; + if (c == '"') + s.Append('"'); + else if (c == '\\') + s.Append('\\'); + else if (c == '/') + s.Append('/'); + else if (c == 'b') + s.Append('\b'); + else if (c == 'f') + s.Append('\f'); + else if (c == 'n') + s.Append('\n'); + else if (c == 'r') + s.Append('\r'); + else if (c == 't') + s.Append('\t'); + else if (c == 'u') + { + int remainingLength = json.Length - index; + if (remainingLength >= 4) + { + // parse the 32 bit hex into an integer codepoint + uint codePoint; + if (!(success = UInt32.TryParse(new string(json, index, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out codePoint))) + return ""; + + // convert the integer codepoint to a unicode char and add to string + if (0xD800 <= codePoint && codePoint <= 0xDBFF) // if high surrogate + { + index += 4; // skip 4 chars + remainingLength = json.Length - index; + if (remainingLength >= 6) + { + uint lowCodePoint; + if (new string(json, index, 2) == "\\u" && UInt32.TryParse(new string(json, index + 2, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out lowCodePoint)) + { + if (0xDC00 <= lowCodePoint && lowCodePoint <= 0xDFFF) // if low surrogate + { + s.Append((char)codePoint); + s.Append((char)lowCodePoint); + index += 6; // skip 6 chars + continue; + } + } + } + success = false; // invalid surrogate pair + return ""; + } + s.Append(ConvertFromUtf32((int)codePoint)); + // skip 4 chars + index += 4; + } + else + break; + } + } + else + s.Append(c); + } + if (!complete) + { + success = false; + return null; + } + return s.ToString(); + } + + private static string ConvertFromUtf32(int utf32) + { + // http://www.java2s.com/Open-Source/CSharp/2.6.4-mono-.net-core/System/System/Char.cs.htm + if (utf32 < 0 || utf32 > 0x10FFFF) + throw new ArgumentOutOfRangeException("utf32", "The argument must be from 0 to 0x10FFFF."); + if (0xD800 <= utf32 && utf32 <= 0xDFFF) + throw new ArgumentOutOfRangeException("utf32", "The argument must not be in surrogate pair range."); + if (utf32 < 0x10000) + return new string((char)utf32, 1); + utf32 -= 0x10000; + return new string(new char[] { (char)((utf32 >> 10) + 0xD800), (char)(utf32 % 0x0400 + 0xDC00) }); + } + + static object ParseNumber(char[] json, ref int index, ref bool success) + { + EatWhitespace(json, ref index); + int lastIndex = GetLastIndexOfNumber(json, index); + int charLength = (lastIndex - index) + 1; + object returnNumber; + string str = new string(json, index, charLength); + if (str.IndexOf(".", StringComparison.OrdinalIgnoreCase) != -1 || str.IndexOf("e", StringComparison.OrdinalIgnoreCase) != -1) + { + double number; + success = double.TryParse(new string(json, index, charLength), NumberStyles.Any, CultureInfo.InvariantCulture, out number); + returnNumber = number; + } + else + { + long number; + success = long.TryParse(new string(json, index, charLength), NumberStyles.Any, CultureInfo.InvariantCulture, out number); + returnNumber = number; + } + index = lastIndex + 1; + return returnNumber; + } + + static int GetLastIndexOfNumber(char[] json, int index) + { + int lastIndex; + for (lastIndex = index; lastIndex < json.Length; lastIndex++) + if ("0123456789+-.eE".IndexOf(json[lastIndex]) == -1) break; + return lastIndex - 1; + } + + static void EatWhitespace(char[] json, ref int index) + { + for (; index < json.Length; index++) { + switch (json[index]) { + case ' ': + case '\t': + case '\n': + case '\r': + case '\b': + case '\f': + break; + default: + return; + } + } + } + + static int LookAhead(char[] json, int index) + { + int saveIndex = index; + return NextToken(json, ref saveIndex); + } + + [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + static int NextToken(char[] json, ref int index) + { + EatWhitespace(json, ref index); + if (index == json.Length) + return TOKEN_NONE; + char c = json[index]; + index++; + switch (c) + { + case '{': + return TOKEN_CURLY_OPEN; + case '}': + return TOKEN_CURLY_CLOSE; + case '[': + return TOKEN_SQUARED_OPEN; + case ']': + return TOKEN_SQUARED_CLOSE; + case ',': + return TOKEN_COMMA; + case '"': + return TOKEN_STRING; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + return TOKEN_NUMBER; + case ':': + return TOKEN_COLON; + } + index--; + int remainingLength = json.Length - index; + // false + if (remainingLength >= 5) + { + if (json[index] == 'f' && json[index + 1] == 'a' && json[index + 2] == 'l' && json[index + 3] == 's' && json[index + 4] == 'e') + { + index += 5; + return TOKEN_FALSE; + } + } + // true + if (remainingLength >= 4) + { + if (json[index] == 't' && json[index + 1] == 'r' && json[index + 2] == 'u' && json[index + 3] == 'e') + { + index += 4; + return TOKEN_TRUE; + } + } + // null + if (remainingLength >= 4) + { + if (json[index] == 'n' && json[index + 1] == 'u' && json[index + 2] == 'l' && json[index + 3] == 'l') + { + index += 4; + return TOKEN_NULL; + } + } + return TOKEN_NONE; + } + + static bool SerializeValue(IJsonSerializerStrategy jsonSerializerStrategy, object value, StringBuilder builder) + { + bool success = true; + string stringValue = value as string; + if (stringValue != null) + success = SerializeString(stringValue, builder); + else + { + IDictionary dict = value as IDictionary; + if (dict != null) + { + success = SerializeObject(jsonSerializerStrategy, dict.Keys, dict.Values, builder); + } + else + { + IDictionary stringDictionary = value as IDictionary; + if (stringDictionary != null) + { + success = SerializeObject(jsonSerializerStrategy, stringDictionary.Keys, stringDictionary.Values, builder); + } + else + { + IEnumerable enumerableValue = value as IEnumerable; + if (enumerableValue != null) + success = SerializeArray(jsonSerializerStrategy, enumerableValue, builder); + else if (IsNumeric(value)) + success = SerializeNumber(value, builder); + else if (value is bool) + builder.Append((bool)value ? "true" : "false"); + else if (value == null) + builder.Append("null"); + else + { + object serializedObject; + success = jsonSerializerStrategy.TrySerializeNonPrimitiveObject(value, out serializedObject); + if (success) + SerializeValue(jsonSerializerStrategy, serializedObject, builder); + } + } + } + } + return success; + } + + static bool SerializeObject(IJsonSerializerStrategy jsonSerializerStrategy, IEnumerable keys, IEnumerable values, StringBuilder builder) + { + builder.Append("{"); + IEnumerator ke = keys.GetEnumerator(); + IEnumerator ve = values.GetEnumerator(); + bool first = true; + while (ke.MoveNext() && ve.MoveNext()) + { + object key = ke.Current; + object value = ve.Current; + if (!first) + builder.Append(","); + string stringKey = key as string; + if (stringKey != null) + SerializeString(stringKey, builder); + else + if (!SerializeValue(jsonSerializerStrategy, value, builder)) return false; + builder.Append(":"); + if (!SerializeValue(jsonSerializerStrategy, value, builder)) + return false; + first = false; + } + builder.Append("}"); + return true; + } + + static bool SerializeArray(IJsonSerializerStrategy jsonSerializerStrategy, IEnumerable anArray, StringBuilder builder) + { + builder.Append("["); + bool first = true; + foreach (object value in anArray) + { + if (!first) + builder.Append(","); + if (!SerializeValue(jsonSerializerStrategy, value, builder)) + return false; + first = false; + } + builder.Append("]"); + return true; + } + + static bool SerializeString(string aString, StringBuilder builder) + { + // Happy path if there's nothing to be escaped. IndexOfAny is highly optimized (and unmanaged) + if (aString.IndexOfAny(EscapeCharacters) == -1) + { + builder.Append('"'); + builder.Append(aString); + builder.Append('"'); + + return true; + } + + builder.Append('"'); + int safeCharacterCount = 0; + char[] charArray = aString.ToCharArray(); + + for (int i = 0; i < charArray.Length; i++) + { + char c = charArray[i]; + + // Non ascii characters are fine, buffer them up and send them to the builder + // in larger chunks if possible. The escape table is a 1:1 translation table + // with \0 [default(char)] denoting a safe character. + if (c >= EscapeTable.Length || EscapeTable[c] == default(char)) + { + safeCharacterCount++; + } + else + { + if (safeCharacterCount > 0) + { + builder.Append(charArray, i - safeCharacterCount, safeCharacterCount); + safeCharacterCount = 0; + } + + builder.Append('\\'); + builder.Append(EscapeTable[c]); + } + } + + if (safeCharacterCount > 0) + { + builder.Append(charArray, charArray.Length - safeCharacterCount, safeCharacterCount); + } + + builder.Append('"'); + return true; + } + + static bool SerializeNumber(object number, StringBuilder builder) + { + if (number is long) + builder.Append(((long)number).ToString(CultureInfo.InvariantCulture)); + else if (number is ulong) + builder.Append(((ulong)number).ToString(CultureInfo.InvariantCulture)); + else if (number is int) + builder.Append(((int)number).ToString(CultureInfo.InvariantCulture)); + else if (number is uint) + builder.Append(((uint)number).ToString(CultureInfo.InvariantCulture)); + else if (number is decimal) + builder.Append(((decimal)number).ToString(CultureInfo.InvariantCulture)); + else if (number is float) + builder.Append(((float)number).ToString(CultureInfo.InvariantCulture)); + else + builder.Append(Convert.ToDouble(number, CultureInfo.InvariantCulture).ToString("r", CultureInfo.InvariantCulture)); + return true; + } + + /// + /// Determines if a given object is numeric in any way + /// (can be integer, double, null, etc). + /// + static bool IsNumeric(object value) + { + if (value is sbyte) return true; + if (value is byte) return true; + if (value is short) return true; + if (value is ushort) return true; + if (value is int) return true; + if (value is uint) return true; + if (value is long) return true; + if (value is ulong) return true; + if (value is float) return true; + if (value is double) return true; + if (value is decimal) return true; + return false; + } + + private static IJsonSerializerStrategy _currentJsonSerializerStrategy; + public static IJsonSerializerStrategy CurrentJsonSerializerStrategy + { + get + { + return _currentJsonSerializerStrategy ?? + (_currentJsonSerializerStrategy = +#if SIMPLE_JSON_DATACONTRACT + DataContractJsonSerializerStrategy +#else + PocoJsonSerializerStrategy +#endif +); + } + set + { + _currentJsonSerializerStrategy = value; + } + } + + private static PocoJsonSerializerStrategy _pocoJsonSerializerStrategy; + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static PocoJsonSerializerStrategy PocoJsonSerializerStrategy + { + get + { + return _pocoJsonSerializerStrategy ?? (_pocoJsonSerializerStrategy = new PocoJsonSerializerStrategy()); + } + } + +#if SIMPLE_JSON_DATACONTRACT + + private static DataContractJsonSerializerStrategy _dataContractJsonSerializerStrategy; + [System.ComponentModel.EditorBrowsable(EditorBrowsableState.Advanced)] + public static DataContractJsonSerializerStrategy DataContractJsonSerializerStrategy + { + get + { + return _dataContractJsonSerializerStrategy ?? (_dataContractJsonSerializerStrategy = new DataContractJsonSerializerStrategy()); + } + } + +#endif + } + + [GeneratedCode("simple-json", "1.0.0")] +#if SIMPLE_JSON_INTERNAL + internal +#else + public +#endif + interface IJsonSerializerStrategy + { + [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification="Need to support .NET 2")] + bool TrySerializeNonPrimitiveObject(object input, out object output); + object DeserializeObject(object value, Type type); + } + + [GeneratedCode("simple-json", "1.0.0")] +#if SIMPLE_JSON_INTERNAL + internal +#else + public +#endif + class PocoJsonSerializerStrategy : IJsonSerializerStrategy + { + internal IDictionary ConstructorCache; + internal IDictionary> GetCache; + internal IDictionary>> SetCache; + + internal static readonly Type[] EmptyTypes = new Type[0]; + internal static readonly Type[] ArrayConstructorParameterTypes = new Type[] { typeof(int) }; + + private static readonly string[] Iso8601Format = new string[] + { + @"yyyy-MM-dd\THH:mm:ss.FFFFFFF\Z", + @"yyyy-MM-dd\THH:mm:ss\Z", + @"yyyy-MM-dd\THH:mm:ssK" + }; + + public PocoJsonSerializerStrategy() + { + ConstructorCache = new ReflectionUtils.ThreadSafeDictionary(ConstructorDelegateFactory); + GetCache = new ReflectionUtils.ThreadSafeDictionary>(GetterValueFactory); + SetCache = new ReflectionUtils.ThreadSafeDictionary>>(SetterValueFactory); + } + + protected virtual string MapClrMemberNameToJsonFieldName(string clrPropertyName) + { + return CamelCase.MemberNameToCamelCase(clrPropertyName); + } + + internal virtual ReflectionUtils.ConstructorDelegate ConstructorDelegateFactory(Type key) + { + // We need List(int) constructor so that DeserializeObject method will work for generating IList-declared values + var needsCapacityArgument = key.IsArray || key.IsConstructedGenericType && key.GetGenericTypeDefinition() == typeof(List<>); + return ReflectionUtils.GetConstructor(key, needsCapacityArgument ? ArrayConstructorParameterTypes : EmptyTypes); + } + + internal virtual IDictionary GetterValueFactory(Type type) + { + IDictionary result = new Dictionary(); + foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) + { + if (propertyInfo.CanRead) + { + MethodInfo getMethod = ReflectionUtils.GetGetterMethodInfo(propertyInfo); + if (getMethod.IsStatic || !getMethod.IsPublic) + continue; + result[MapClrMemberNameToJsonFieldName(propertyInfo.Name)] = ReflectionUtils.GetGetMethod(propertyInfo); + } + } + foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) + { + if (fieldInfo.IsStatic || !fieldInfo.IsPublic) + continue; + result[MapClrMemberNameToJsonFieldName(fieldInfo.Name)] = ReflectionUtils.GetGetMethod(fieldInfo); + } + return result; + } + + internal virtual IDictionary> SetterValueFactory(Type type) + { + // BLAZOR-SPECIFIC MODIFICATION FROM STOCK SIMPLEJSON: + // + // For incoming keys we match case-insensitively. But if two .NET properties differ only by case, + // it's ambiguous which should be used: the one that matches the incoming JSON exactly, or the + // one that uses 'correct' PascalCase corresponding to the incoming camelCase? What if neither + // meets these descriptions? + // + // To resolve this: + // - If multiple public properties differ only by case, we throw + // - If multiple public fields differ only by case, we throw + // - If there's a public property and a public field that differ only by case, we prefer the property + // This unambiguously selects one member, and that's what we'll use. + + IDictionary> result = new Dictionary>(StringComparer.OrdinalIgnoreCase); + foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) + { + if (propertyInfo.CanWrite) + { + MethodInfo setMethod = ReflectionUtils.GetSetterMethodInfo(propertyInfo); + if (setMethod.IsStatic) + continue; + if (result.ContainsKey(propertyInfo.Name)) + { + throw new InvalidOperationException($"The type '{type.FullName}' contains multiple public properties with names case-insensitively matching '{propertyInfo.Name.ToLowerInvariant()}'. Such types cannot be used for JSON deserialization."); + } + result[propertyInfo.Name] = new KeyValuePair(propertyInfo.PropertyType, ReflectionUtils.GetSetMethod(propertyInfo)); + } + } + + IDictionary> fieldResult = new Dictionary>(StringComparer.OrdinalIgnoreCase); + foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) + { + if (fieldInfo.IsInitOnly || fieldInfo.IsStatic || !fieldInfo.IsPublic) + continue; + if (fieldResult.ContainsKey(fieldInfo.Name)) + { + throw new InvalidOperationException($"The type '{type.FullName}' contains multiple public fields with names case-insensitively matching '{fieldInfo.Name.ToLowerInvariant()}'. Such types cannot be used for JSON deserialization."); + } + fieldResult[fieldInfo.Name] = new KeyValuePair(fieldInfo.FieldType, ReflectionUtils.GetSetMethod(fieldInfo)); + if (!result.ContainsKey(fieldInfo.Name)) + { + result[fieldInfo.Name] = fieldResult[fieldInfo.Name]; + } + } + + return result; + } + + public virtual bool TrySerializeNonPrimitiveObject(object input, out object output) + { + return TrySerializeKnownTypes(input, out output) || TrySerializeUnknownTypes(input, out output); + } + + [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + public virtual object DeserializeObject(object value, Type type) + { + if (type == null) throw new ArgumentNullException("type"); + string str = value as string; + + if (type == typeof (Guid) && string.IsNullOrEmpty(str)) + return default(Guid); + + if (type.IsEnum) + { + type = type.GetEnumUnderlyingType(); + } + + if (value == null) + return null; + + object obj = null; + + if (str != null) + { + if (str.Length != 0) // We know it can't be null now. + { + if (type == typeof(TimeSpan) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(TimeSpan))) + return TimeSpan.ParseExact(str, "c", CultureInfo.InvariantCulture); + if (type == typeof(DateTime) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(DateTime))) + return DateTime.TryParseExact(str, Iso8601Format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out var result) + ? result : DateTime.Parse(str, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind); + if (type == typeof(DateTimeOffset) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(DateTimeOffset))) + return DateTimeOffset.TryParseExact(str, Iso8601Format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out var result) + ? result : DateTimeOffset.Parse(str, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind); + if (type == typeof(Guid) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid))) + return new Guid(str); + if (type == typeof(Uri)) + { + bool isValid = Uri.IsWellFormedUriString(str, UriKind.RelativeOrAbsolute); + + Uri result; + if (isValid && Uri.TryCreate(str, UriKind.RelativeOrAbsolute, out result)) + return result; + + return null; + } + + if (type == typeof(string)) + return str; + + return Convert.ChangeType(str, type, CultureInfo.InvariantCulture); + } + else + { + if (type == typeof(Guid)) + obj = default(Guid); + else if (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid)) + obj = null; + else + obj = str; + } + // Empty string case + if (!ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid)) + return str; + } + else if (value is bool) + return value; + + bool valueIsLong = value is long; + bool valueIsDouble = value is double; + if ((valueIsLong && type == typeof(long)) || (valueIsDouble && type == typeof(double))) + return value; + if ((valueIsDouble && type != typeof(double)) || (valueIsLong && type != typeof(long))) + { + obj = type == typeof(int) || type == typeof(long) || type == typeof(double) || type == typeof(float) || type == typeof(bool) || type == typeof(decimal) || type == typeof(byte) || type == typeof(short) + ? Convert.ChangeType(value, type, CultureInfo.InvariantCulture) + : value; + } + else + { + IDictionary objects = value as IDictionary; + if (objects != null) + { + IDictionary jsonObject = objects; + + if (ReflectionUtils.IsTypeDictionary(type)) + { + // if dictionary then + Type[] types = ReflectionUtils.GetGenericTypeArguments(type); + Type keyType = types[0]; + Type valueType = types[1]; + + Type genericType = typeof(Dictionary<,>).MakeGenericType(keyType, valueType); + + IDictionary dict = (IDictionary)ConstructorCache[genericType](); + + foreach (KeyValuePair kvp in jsonObject) + dict.Add(kvp.Key, DeserializeObject(kvp.Value, valueType)); + + obj = dict; + } + else + { + if (type == typeof(object)) + obj = value; + else + { + var constructorDelegate = ConstructorCache[type] + ?? throw new InvalidOperationException($"Cannot deserialize JSON into type '{type.FullName}' because it does not have a public parameterless constructor."); + obj = constructorDelegate(); + + var setterCache = SetCache[type]; + foreach (var jsonKeyValuePair in jsonObject) + { + if (setterCache.TryGetValue(jsonKeyValuePair.Key, out var setter)) + { + var jsonValue = DeserializeObject(jsonKeyValuePair.Value, setter.Key); + setter.Value(obj, jsonValue); + } + } + } + } + } + else + { + IList valueAsList = value as IList; + if (valueAsList != null) + { + IList jsonObject = valueAsList; + IList list = null; + + if (type.IsArray) + { + list = (IList)ConstructorCache[type](jsonObject.Count); + int i = 0; + foreach (object o in jsonObject) + list[i++] = DeserializeObject(o, type.GetElementType()); + } + else if (ReflectionUtils.IsTypeGenericCollectionInterface(type) || ReflectionUtils.IsAssignableFrom(typeof(IList), type)) + { + Type innerType = ReflectionUtils.GetGenericListElementType(type); + list = (IList)(ConstructorCache[type] ?? ConstructorCache[typeof(List<>).MakeGenericType(innerType)])(jsonObject.Count); + foreach (object o in jsonObject) + list.Add(DeserializeObject(o, innerType)); + } + obj = list; + } + } + return obj; + } + if (ReflectionUtils.IsNullableType(type)) + { + // For nullable enums serialized as numbers + if (Nullable.GetUnderlyingType(type).IsEnum) + { + return Enum.ToObject(Nullable.GetUnderlyingType(type), value); + } + + return ReflectionUtils.ToNullableType(obj, type); + } + + return obj; + } + + protected virtual object SerializeEnum(Enum p) + { + return Convert.ToDouble(p, CultureInfo.InvariantCulture); + } + + [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification="Need to support .NET 2")] + protected virtual bool TrySerializeKnownTypes(object input, out object output) + { + bool returnValue = true; + if (input is DateTime) + output = ((DateTime)input).ToUniversalTime().ToString(Iso8601Format[0], CultureInfo.InvariantCulture); + else if (input is DateTimeOffset) + output = ((DateTimeOffset)input).ToString("o"); + else if (input is Guid) + output = ((Guid)input).ToString("D"); + else if (input is Uri) + output = input.ToString(); + else if (input is TimeSpan) + output = ((TimeSpan)input).ToString("c"); + else + { + Enum inputEnum = input as Enum; + if (inputEnum != null) + output = SerializeEnum(inputEnum); + else + { + returnValue = false; + output = null; + } + } + return returnValue; + } + [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification="Need to support .NET 2")] + protected virtual bool TrySerializeUnknownTypes(object input, out object output) + { + if (input == null) throw new ArgumentNullException("input"); + output = null; + Type type = input.GetType(); + if (type.FullName == null) + return false; + IDictionary obj = new JsonObject(); + IDictionary getters = GetCache[type]; + foreach (KeyValuePair getter in getters) + { + if (getter.Value != null) + obj.Add(MapClrMemberNameToJsonFieldName(getter.Key), getter.Value(input)); + } + output = obj; + return true; + } + } + +#if SIMPLE_JSON_DATACONTRACT + [GeneratedCode("simple-json", "1.0.0")] +#if SIMPLE_JSON_INTERNAL + internal +#else + public +#endif + class DataContractJsonSerializerStrategy : PocoJsonSerializerStrategy + { + public DataContractJsonSerializerStrategy() + { + GetCache = new ReflectionUtils.ThreadSafeDictionary>(GetterValueFactory); + SetCache = new ReflectionUtils.ThreadSafeDictionary>>(SetterValueFactory); + } + + internal override IDictionary GetterValueFactory(Type type) + { + bool hasDataContract = ReflectionUtils.GetAttribute(type, typeof(DataContractAttribute)) != null; + if (!hasDataContract) + return base.GetterValueFactory(type); + string jsonKey; + IDictionary result = new Dictionary(); + foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) + { + if (propertyInfo.CanRead) + { + MethodInfo getMethod = ReflectionUtils.GetGetterMethodInfo(propertyInfo); + if (!getMethod.IsStatic && CanAdd(propertyInfo, out jsonKey)) + result[jsonKey] = ReflectionUtils.GetGetMethod(propertyInfo); + } + } + foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) + { + if (!fieldInfo.IsStatic && CanAdd(fieldInfo, out jsonKey)) + result[jsonKey] = ReflectionUtils.GetGetMethod(fieldInfo); + } + return result; + } + + internal override IDictionary> SetterValueFactory(Type type) + { + bool hasDataContract = ReflectionUtils.GetAttribute(type, typeof(DataContractAttribute)) != null; + if (!hasDataContract) + return base.SetterValueFactory(type); + string jsonKey; + IDictionary> result = new Dictionary>(); + foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) + { + if (propertyInfo.CanWrite) + { + MethodInfo setMethod = ReflectionUtils.GetSetterMethodInfo(propertyInfo); + if (!setMethod.IsStatic && CanAdd(propertyInfo, out jsonKey)) + result[jsonKey] = new KeyValuePair(propertyInfo.PropertyType, ReflectionUtils.GetSetMethod(propertyInfo)); + } + } + foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) + { + if (!fieldInfo.IsInitOnly && !fieldInfo.IsStatic && CanAdd(fieldInfo, out jsonKey)) + result[jsonKey] = new KeyValuePair(fieldInfo.FieldType, ReflectionUtils.GetSetMethod(fieldInfo)); + } + // todo implement sorting for DATACONTRACT. + return result; + } + + private static bool CanAdd(MemberInfo info, out string jsonKey) + { + jsonKey = null; + if (ReflectionUtils.GetAttribute(info, typeof(IgnoreDataMemberAttribute)) != null) + return false; + DataMemberAttribute dataMemberAttribute = (DataMemberAttribute)ReflectionUtils.GetAttribute(info, typeof(DataMemberAttribute)); + if (dataMemberAttribute == null) + return false; + jsonKey = string.IsNullOrEmpty(dataMemberAttribute.Name) ? info.Name : dataMemberAttribute.Name; + return true; + } + } + +#endif + + namespace Reflection + { + // This class is meant to be copied into other libraries. So we want to exclude it from Code Analysis rules + // that might be in place in the target project. + [GeneratedCode("reflection-utils", "1.0.0")] +#if SIMPLE_JSON_REFLECTION_UTILS_PUBLIC + public +#else + internal +#endif + class ReflectionUtils + { + private static readonly object[] EmptyObjects = new object[] { }; + + public delegate object GetDelegate(object source); + public delegate void SetDelegate(object source, object value); + public delegate object ConstructorDelegate(params object[] args); + + public delegate TValue ThreadSafeDictionaryValueFactory(TKey key); + +#if SIMPLE_JSON_TYPEINFO + public static TypeInfo GetTypeInfo(Type type) + { + return type.GetTypeInfo(); + } +#else + public static Type GetTypeInfo(Type type) + { + return type; + } +#endif + + public static Attribute GetAttribute(MemberInfo info, Type type) + { +#if SIMPLE_JSON_TYPEINFO + if (info == null || type == null || !info.IsDefined(type)) + return null; + return info.GetCustomAttribute(type); +#else + if (info == null || type == null || !Attribute.IsDefined(info, type)) + return null; + return Attribute.GetCustomAttribute(info, type); +#endif + } + + public static Type GetGenericListElementType(Type type) + { + IEnumerable interfaces; +#if SIMPLE_JSON_TYPEINFO + interfaces = type.GetTypeInfo().ImplementedInterfaces; +#else + interfaces = type.GetInterfaces(); +#endif + foreach (Type implementedInterface in interfaces) + { + if (IsTypeGeneric(implementedInterface) && + implementedInterface.GetGenericTypeDefinition() == typeof (IList<>)) + { + return GetGenericTypeArguments(implementedInterface)[0]; + } + } + return GetGenericTypeArguments(type)[0]; + } + + public static Attribute GetAttribute(Type objectType, Type attributeType) + { + +#if SIMPLE_JSON_TYPEINFO + if (objectType == null || attributeType == null || !objectType.GetTypeInfo().IsDefined(attributeType)) + return null; + return objectType.GetTypeInfo().GetCustomAttribute(attributeType); +#else + if (objectType == null || attributeType == null || !Attribute.IsDefined(objectType, attributeType)) + return null; + return Attribute.GetCustomAttribute(objectType, attributeType); +#endif + } + + public static Type[] GetGenericTypeArguments(Type type) + { +#if SIMPLE_JSON_TYPEINFO + return type.GetTypeInfo().GenericTypeArguments; +#else + return type.GetGenericArguments(); +#endif + } + + public static bool IsTypeGeneric(Type type) + { + return GetTypeInfo(type).IsGenericType; + } + + public static bool IsTypeGenericCollectionInterface(Type type) + { + if (!IsTypeGeneric(type)) + return false; + + Type genericDefinition = type.GetGenericTypeDefinition(); + + return (genericDefinition == typeof(IList<>) + || genericDefinition == typeof(ICollection<>) + || genericDefinition == typeof(IEnumerable<>) +#if SIMPLE_JSON_READONLY_COLLECTIONS + || genericDefinition == typeof(IReadOnlyCollection<>) + || genericDefinition == typeof(IReadOnlyList<>) +#endif + ); + } + + public static bool IsAssignableFrom(Type type1, Type type2) + { + return GetTypeInfo(type1).IsAssignableFrom(GetTypeInfo(type2)); + } + + public static bool IsTypeDictionary(Type type) + { +#if SIMPLE_JSON_TYPEINFO + if (typeof(IDictionary<,>).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) + return true; +#else + if (typeof(System.Collections.IDictionary).IsAssignableFrom(type)) + return true; +#endif + if (!GetTypeInfo(type).IsGenericType) + return false; + + Type genericDefinition = type.GetGenericTypeDefinition(); + return genericDefinition == typeof(IDictionary<,>); + } + + public static bool IsNullableType(Type type) + { + return GetTypeInfo(type).IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); + } + + public static object ToNullableType(object obj, Type nullableType) + { + return obj == null ? null : Convert.ChangeType(obj, Nullable.GetUnderlyingType(nullableType), CultureInfo.InvariantCulture); + } + + public static bool IsValueType(Type type) + { + return GetTypeInfo(type).IsValueType; + } + + public static IEnumerable GetConstructors(Type type) + { +#if SIMPLE_JSON_TYPEINFO + return type.GetTypeInfo().DeclaredConstructors; +#else + const BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; + return type.GetConstructors(flags); +#endif + } + + public static ConstructorInfo GetConstructorInfo(Type type, params Type[] argsType) + { + IEnumerable constructorInfos = GetConstructors(type); + int i; + bool matches; + foreach (ConstructorInfo constructorInfo in constructorInfos) + { + ParameterInfo[] parameters = constructorInfo.GetParameters(); + if (argsType.Length != parameters.Length) + continue; + + i = 0; + matches = true; + foreach (ParameterInfo parameterInfo in constructorInfo.GetParameters()) + { + if (parameterInfo.ParameterType != argsType[i]) + { + matches = false; + break; + } + } + + if (matches) + return constructorInfo; + } + + return null; + } + + public static IEnumerable GetProperties(Type type) + { +#if SIMPLE_JSON_TYPEINFO + return type.GetRuntimeProperties(); +#else + return type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); +#endif + } + + public static IEnumerable GetFields(Type type) + { +#if SIMPLE_JSON_TYPEINFO + return type.GetRuntimeFields(); +#else + return type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); +#endif + } + + public static MethodInfo GetGetterMethodInfo(PropertyInfo propertyInfo) + { +#if SIMPLE_JSON_TYPEINFO + return propertyInfo.GetMethod; +#else + return propertyInfo.GetGetMethod(true); +#endif + } + + public static MethodInfo GetSetterMethodInfo(PropertyInfo propertyInfo) + { +#if SIMPLE_JSON_TYPEINFO + return propertyInfo.SetMethod; +#else + return propertyInfo.GetSetMethod(true); +#endif + } + + public static ConstructorDelegate GetConstructor(ConstructorInfo constructorInfo) + { +#if SIMPLE_JSON_NO_LINQ_EXPRESSION + return GetConstructorByReflection(constructorInfo); +#else + return GetConstructorByExpression(constructorInfo); +#endif + } + + public static ConstructorDelegate GetConstructor(Type type, params Type[] argsType) + { +#if SIMPLE_JSON_NO_LINQ_EXPRESSION + return GetConstructorByReflection(type, argsType); +#else + return GetConstructorByExpression(type, argsType); +#endif + } + + public static ConstructorDelegate GetConstructorByReflection(ConstructorInfo constructorInfo) + { + return delegate(object[] args) { return constructorInfo.Invoke(args); }; + } + + public static ConstructorDelegate GetConstructorByReflection(Type type, params Type[] argsType) + { + ConstructorInfo constructorInfo = GetConstructorInfo(type, argsType); + + if (constructorInfo == null && argsType.Length == 0 && type.IsValueType) + { + // If it's a struct, then parameterless constructors are implicit + // We can always call Activator.CreateInstance in lieu of a zero-arg constructor + return args => Activator.CreateInstance(type); + } + + return constructorInfo == null ? null : GetConstructorByReflection(constructorInfo); + } + +#if !SIMPLE_JSON_NO_LINQ_EXPRESSION + + public static ConstructorDelegate GetConstructorByExpression(ConstructorInfo constructorInfo) + { + ParameterInfo[] paramsInfo = constructorInfo.GetParameters(); + ParameterExpression param = Expression.Parameter(typeof(object[]), "args"); + Expression[] argsExp = new Expression[paramsInfo.Length]; + for (int i = 0; i < paramsInfo.Length; i++) + { + Expression index = Expression.Constant(i); + Type paramType = paramsInfo[i].ParameterType; + Expression paramAccessorExp = Expression.ArrayIndex(param, index); + Expression paramCastExp = Expression.Convert(paramAccessorExp, paramType); + argsExp[i] = paramCastExp; + } + NewExpression newExp = Expression.New(constructorInfo, argsExp); + Expression> lambda = Expression.Lambda>(newExp, param); + Func compiledLambda = lambda.Compile(); + return delegate(object[] args) { return compiledLambda(args); }; + } + + public static ConstructorDelegate GetConstructorByExpression(Type type, params Type[] argsType) + { + ConstructorInfo constructorInfo = GetConstructorInfo(type, argsType); + return constructorInfo == null ? null : GetConstructorByExpression(constructorInfo); + } + +#endif + + public static GetDelegate GetGetMethod(PropertyInfo propertyInfo) + { +#if SIMPLE_JSON_NO_LINQ_EXPRESSION + return GetGetMethodByReflection(propertyInfo); +#else + return GetGetMethodByExpression(propertyInfo); +#endif + } + + public static GetDelegate GetGetMethod(FieldInfo fieldInfo) + { +#if SIMPLE_JSON_NO_LINQ_EXPRESSION + return GetGetMethodByReflection(fieldInfo); +#else + return GetGetMethodByExpression(fieldInfo); +#endif + } + + public static GetDelegate GetGetMethodByReflection(PropertyInfo propertyInfo) + { + MethodInfo methodInfo = GetGetterMethodInfo(propertyInfo); + return delegate(object source) { return methodInfo.Invoke(source, EmptyObjects); }; + } + + public static GetDelegate GetGetMethodByReflection(FieldInfo fieldInfo) + { + return delegate(object source) { return fieldInfo.GetValue(source); }; + } + +#if !SIMPLE_JSON_NO_LINQ_EXPRESSION + + public static GetDelegate GetGetMethodByExpression(PropertyInfo propertyInfo) + { + MethodInfo getMethodInfo = GetGetterMethodInfo(propertyInfo); + ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); + UnaryExpression instanceCast = (!IsValueType(propertyInfo.DeclaringType)) ? Expression.TypeAs(instance, propertyInfo.DeclaringType) : Expression.Convert(instance, propertyInfo.DeclaringType); + Func compiled = Expression.Lambda>(Expression.TypeAs(Expression.Call(instanceCast, getMethodInfo), typeof(object)), instance).Compile(); + return delegate(object source) { return compiled(source); }; + } + + public static GetDelegate GetGetMethodByExpression(FieldInfo fieldInfo) + { + ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); + MemberExpression member = Expression.Field(Expression.Convert(instance, fieldInfo.DeclaringType), fieldInfo); + GetDelegate compiled = Expression.Lambda(Expression.Convert(member, typeof(object)), instance).Compile(); + return delegate(object source) { return compiled(source); }; + } + +#endif + + public static SetDelegate GetSetMethod(PropertyInfo propertyInfo) + { +#if SIMPLE_JSON_NO_LINQ_EXPRESSION + return GetSetMethodByReflection(propertyInfo); +#else + return GetSetMethodByExpression(propertyInfo); +#endif + } + + public static SetDelegate GetSetMethod(FieldInfo fieldInfo) + { +#if SIMPLE_JSON_NO_LINQ_EXPRESSION + return GetSetMethodByReflection(fieldInfo); +#else + return GetSetMethodByExpression(fieldInfo); +#endif + } + + public static SetDelegate GetSetMethodByReflection(PropertyInfo propertyInfo) + { + MethodInfo methodInfo = GetSetterMethodInfo(propertyInfo); + return delegate(object source, object value) { methodInfo.Invoke(source, new object[] { value }); }; + } + + public static SetDelegate GetSetMethodByReflection(FieldInfo fieldInfo) + { + return delegate(object source, object value) { fieldInfo.SetValue(source, value); }; + } + +#if !SIMPLE_JSON_NO_LINQ_EXPRESSION + + public static SetDelegate GetSetMethodByExpression(PropertyInfo propertyInfo) + { + MethodInfo setMethodInfo = GetSetterMethodInfo(propertyInfo); + ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); + ParameterExpression value = Expression.Parameter(typeof(object), "value"); + UnaryExpression instanceCast = (!IsValueType(propertyInfo.DeclaringType)) ? Expression.TypeAs(instance, propertyInfo.DeclaringType) : Expression.Convert(instance, propertyInfo.DeclaringType); + UnaryExpression valueCast = (!IsValueType(propertyInfo.PropertyType)) ? Expression.TypeAs(value, propertyInfo.PropertyType) : Expression.Convert(value, propertyInfo.PropertyType); + Action compiled = Expression.Lambda>(Expression.Call(instanceCast, setMethodInfo, valueCast), new ParameterExpression[] { instance, value }).Compile(); + return delegate(object source, object val) { compiled(source, val); }; + } + + public static SetDelegate GetSetMethodByExpression(FieldInfo fieldInfo) + { + ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); + ParameterExpression value = Expression.Parameter(typeof(object), "value"); + Action compiled = Expression.Lambda>( + Assign(Expression.Field(Expression.Convert(instance, fieldInfo.DeclaringType), fieldInfo), Expression.Convert(value, fieldInfo.FieldType)), instance, value).Compile(); + return delegate(object source, object val) { compiled(source, val); }; + } + + public static BinaryExpression Assign(Expression left, Expression right) + { +#if SIMPLE_JSON_TYPEINFO + return Expression.Assign(left, right); +#else + MethodInfo assign = typeof(Assigner<>).MakeGenericType(left.Type).GetMethod("Assign"); + BinaryExpression assignExpr = Expression.Add(left, right, assign); + return assignExpr; +#endif + } + + private static class Assigner + { + public static T Assign(ref T left, T right) + { + return (left = right); + } + } + +#endif + + public sealed class ThreadSafeDictionary : IDictionary + { + private readonly object _lock = new object(); + private readonly ThreadSafeDictionaryValueFactory _valueFactory; + private Dictionary _dictionary; + + public ThreadSafeDictionary(ThreadSafeDictionaryValueFactory valueFactory) + { + _valueFactory = valueFactory; + } + + private TValue Get(TKey key) + { + if (_dictionary == null) + return AddValue(key); + TValue value; + if (!_dictionary.TryGetValue(key, out value)) + return AddValue(key); + return value; + } + + private TValue AddValue(TKey key) + { + TValue value = _valueFactory(key); + lock (_lock) + { + if (_dictionary == null) + { + _dictionary = new Dictionary(); + _dictionary[key] = value; + } + else + { + TValue val; + if (_dictionary.TryGetValue(key, out val)) + return val; + Dictionary dict = new Dictionary(_dictionary); + dict[key] = value; + _dictionary = dict; + } + } + return value; + } + + public void Add(TKey key, TValue value) + { + throw new NotImplementedException(); + } + + public bool ContainsKey(TKey key) + { + return _dictionary.ContainsKey(key); + } + + public ICollection Keys + { + get { return _dictionary.Keys; } + } + + public bool Remove(TKey key) + { + throw new NotImplementedException(); + } + + public bool TryGetValue(TKey key, out TValue value) + { + value = this[key]; + return true; + } + + public ICollection Values + { + get { return _dictionary.Values; } + } + + public TValue this[TKey key] + { + get { return Get(key); } + set { throw new NotImplementedException(); } + } + + public void Add(KeyValuePair item) + { + throw new NotImplementedException(); + } + + public void Clear() + { + throw new NotImplementedException(); + } + + public bool Contains(KeyValuePair item) + { + throw new NotImplementedException(); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + throw new NotImplementedException(); + } + + public int Count + { + get { return _dictionary.Count; } + } + + public bool IsReadOnly + { + get { throw new NotImplementedException(); } + } + + public bool Remove(KeyValuePair item) + { + throw new NotImplementedException(); + } + + public IEnumerator> GetEnumerator() + { + return _dictionary.GetEnumerator(); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return _dictionary.GetEnumerator(); + } + } + + } + } +} +// ReSharper restore LoopCanBeConvertedToQuery +// ReSharper restore RedundantExplicitArrayCreation +// ReSharper restore SuggestUseVarKeywordEvident diff --git a/src/JSInterop/src/Microsoft.JSInterop/Microsoft.JSInterop.csproj b/src/JSInterop/src/Microsoft.JSInterop/Microsoft.JSInterop.csproj new file mode 100644 index 000000000000..9f5c4f4abb61 --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop/Microsoft.JSInterop.csproj @@ -0,0 +1,7 @@ + + + + netstandard2.0 + + + diff --git a/src/JSInterop/src/Microsoft.JSInterop/Properties/AssemblyInfo.cs b/src/JSInterop/src/Microsoft.JSInterop/Properties/AssemblyInfo.cs new file mode 100644 index 000000000000..d65c89dc7f12 --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop/Properties/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Microsoft.JSInterop.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] diff --git a/src/JSInterop/src/Microsoft.JSInterop/TaskGenericsUtil.cs b/src/JSInterop/src/Microsoft.JSInterop/TaskGenericsUtil.cs new file mode 100644 index 000000000000..734e9863b84a --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop/TaskGenericsUtil.cs @@ -0,0 +1,116 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Concurrent; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.JSInterop +{ + internal static class TaskGenericsUtil + { + private static ConcurrentDictionary _cachedResultGetters + = new ConcurrentDictionary(); + + private static ConcurrentDictionary _cachedResultSetters + = new ConcurrentDictionary(); + + public static void SetTaskCompletionSourceResult(object taskCompletionSource, object result) + => CreateResultSetter(taskCompletionSource).SetResult(taskCompletionSource, result); + + public static void SetTaskCompletionSourceException(object taskCompletionSource, Exception exception) + => CreateResultSetter(taskCompletionSource).SetException(taskCompletionSource, exception); + + public static Type GetTaskCompletionSourceResultType(object taskCompletionSource) + => CreateResultSetter(taskCompletionSource).ResultType; + + public static object GetTaskResult(Task task) + { + var getter = _cachedResultGetters.GetOrAdd(task.GetType(), taskInstanceType => + { + var resultType = GetTaskResultType(taskInstanceType); + return resultType == null + ? new VoidTaskResultGetter() + : (ITaskResultGetter)Activator.CreateInstance( + typeof(TaskResultGetter<>).MakeGenericType(resultType)); + }); + return getter.GetResult(task); + } + + private static Type GetTaskResultType(Type taskType) + { + // It might be something derived from Task or Task, so we have to scan + // up the inheritance hierarchy to find the Task or Task + while (taskType != typeof(Task) && + (!taskType.IsGenericType || taskType.GetGenericTypeDefinition() != typeof(Task<>))) + { + taskType = taskType.BaseType + ?? throw new ArgumentException($"The type '{taskType.FullName}' is not inherited from '{typeof(Task).FullName}'."); + } + + return taskType.IsGenericType + ? taskType.GetGenericArguments().Single() + : null; + } + + interface ITcsResultSetter + { + Type ResultType { get; } + void SetResult(object taskCompletionSource, object result); + void SetException(object taskCompletionSource, Exception exception); + } + + private interface ITaskResultGetter + { + object GetResult(Task task); + } + + private class TaskResultGetter : ITaskResultGetter + { + public object GetResult(Task task) => ((Task)task).Result; + } + + private class VoidTaskResultGetter : ITaskResultGetter + { + public object GetResult(Task task) + { + task.Wait(); // Throw if the task failed + return null; + } + } + + private class TcsResultSetter : ITcsResultSetter + { + public Type ResultType => typeof(T); + + public void SetResult(object tcs, object result) + { + var typedTcs = (TaskCompletionSource)tcs; + + // If necessary, attempt a cast + var typedResult = result is T resultT + ? resultT + : (T)Convert.ChangeType(result, typeof(T)); + + typedTcs.SetResult(typedResult); + } + + public void SetException(object tcs, Exception exception) + { + var typedTcs = (TaskCompletionSource)tcs; + typedTcs.SetException(exception); + } + } + + private static ITcsResultSetter CreateResultSetter(object taskCompletionSource) + { + return _cachedResultSetters.GetOrAdd(taskCompletionSource.GetType(), tcsType => + { + var resultType = tcsType.GetGenericArguments().Single(); + return (ITcsResultSetter)Activator.CreateInstance( + typeof(TcsResultSetter<>).MakeGenericType(resultType)); + }); + } + } +} diff --git a/src/JSInterop/src/Mono.WebAssembly.Interop/InternalCalls.cs b/src/JSInterop/src/Mono.WebAssembly.Interop/InternalCalls.cs new file mode 100644 index 000000000000..60c0cdc42913 --- /dev/null +++ b/src/JSInterop/src/Mono.WebAssembly.Interop/InternalCalls.cs @@ -0,0 +1,25 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Runtime.CompilerServices; + +namespace WebAssembly.JSInterop +{ + /// + /// Methods that map to the functions compiled into the Mono WebAssembly runtime, + /// as defined by 'mono_add_internal_call' calls in driver.c + /// + internal class InternalCalls + { + // The exact namespace, type, and method names must match the corresponding entries + // in driver.c in the Mono distribution + + // We're passing asyncHandle by ref not because we want it to be writable, but so it gets + // passed as a pointer (4 bytes). We can pass 4-byte values, but not 8-byte ones. + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern string InvokeJSMarshalled(out string exception, ref long asyncHandle, string functionIdentifier, string argsJson); + + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern TRes InvokeJSUnmarshalled(out string exception, string functionIdentifier, T0 arg0, T1 arg1, T2 arg2); + } +} diff --git a/src/JSInterop/src/Mono.WebAssembly.Interop/Mono.WebAssembly.Interop.csproj b/src/JSInterop/src/Mono.WebAssembly.Interop/Mono.WebAssembly.Interop.csproj new file mode 100644 index 000000000000..81f1173b5585 --- /dev/null +++ b/src/JSInterop/src/Mono.WebAssembly.Interop/Mono.WebAssembly.Interop.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/JSInterop/src/Mono.WebAssembly.Interop/MonoWebAssemblyJSRuntime.cs b/src/JSInterop/src/Mono.WebAssembly.Interop/MonoWebAssemblyJSRuntime.cs new file mode 100644 index 000000000000..9a502b8bc842 --- /dev/null +++ b/src/JSInterop/src/Mono.WebAssembly.Interop/MonoWebAssemblyJSRuntime.cs @@ -0,0 +1,114 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.JSInterop; +using WebAssembly.JSInterop; + +namespace Mono.WebAssembly.Interop +{ + /// + /// Provides methods for invoking JavaScript functions for applications running + /// on the Mono WebAssembly runtime. + /// + public class MonoWebAssemblyJSRuntime : JSInProcessRuntimeBase + { + /// + protected override string InvokeJS(string identifier, string argsJson) + { + var noAsyncHandle = default(long); + var result = InternalCalls.InvokeJSMarshalled(out var exception, ref noAsyncHandle, identifier, argsJson); + return exception != null + ? throw new JSException(exception) + : result; + } + + /// + protected override void BeginInvokeJS(long asyncHandle, string identifier, string argsJson) + { + InternalCalls.InvokeJSMarshalled(out _, ref asyncHandle, identifier, argsJson); + } + + // Invoked via Mono's JS interop mechanism (invoke_method) + private static string InvokeDotNet(string assemblyName, string methodIdentifier, string dotNetObjectId, string argsJson) + => DotNetDispatcher.Invoke(assemblyName, methodIdentifier, dotNetObjectId == null ? default : long.Parse(dotNetObjectId), argsJson); + + // Invoked via Mono's JS interop mechanism (invoke_method) + private static void BeginInvokeDotNet(string callId, string assemblyNameOrDotNetObjectId, string methodIdentifier, string argsJson) + { + // Figure out whether 'assemblyNameOrDotNetObjectId' is the assembly name or the instance ID + // We only need one for any given call. This helps to work around the limitation that we can + // only pass a maximum of 4 args in a call from JS to Mono WebAssembly. + string assemblyName; + long dotNetObjectId; + if (char.IsDigit(assemblyNameOrDotNetObjectId[0])) + { + dotNetObjectId = long.Parse(assemblyNameOrDotNetObjectId); + assemblyName = null; + } + else + { + dotNetObjectId = default; + assemblyName = assemblyNameOrDotNetObjectId; + } + + DotNetDispatcher.BeginInvoke(callId, assemblyName, methodIdentifier, dotNetObjectId, argsJson); + } + + #region Custom MonoWebAssemblyJSRuntime methods + + /// + /// Invokes the JavaScript function registered with the specified identifier. + /// + /// The .NET type corresponding to the function's return value type. + /// The identifier used when registering the target function. + /// The result of the function invocation. + public TRes InvokeUnmarshalled(string identifier) + => InvokeUnmarshalled(identifier, null, null, null); + + /// + /// Invokes the JavaScript function registered with the specified identifier. + /// + /// The type of the first argument. + /// The .NET type corresponding to the function's return value type. + /// The identifier used when registering the target function. + /// The first argument. + /// The result of the function invocation. + public TRes InvokeUnmarshalled(string identifier, T0 arg0) + => InvokeUnmarshalled(identifier, arg0, null, null); + + /// + /// Invokes the JavaScript function registered with the specified identifier. + /// + /// The type of the first argument. + /// The type of the second argument. + /// The .NET type corresponding to the function's return value type. + /// The identifier used when registering the target function. + /// The first argument. + /// The second argument. + /// The result of the function invocation. + public TRes InvokeUnmarshalled(string identifier, T0 arg0, T1 arg1) + => InvokeUnmarshalled(identifier, arg0, arg1, null); + + /// + /// Invokes the JavaScript function registered with the specified identifier. + /// + /// The type of the first argument. + /// The type of the second argument. + /// The type of the third argument. + /// The .NET type corresponding to the function's return value type. + /// The identifier used when registering the target function. + /// The first argument. + /// The second argument. + /// The third argument. + /// The result of the function invocation. + public TRes InvokeUnmarshalled(string identifier, T0 arg0, T1 arg1, T2 arg2) + { + var result = InternalCalls.InvokeJSUnmarshalled(out var exception, identifier, arg0, arg1, arg2); + return exception != null + ? throw new JSException(exception) + : result; + } + + #endregion + } +} diff --git a/src/JSInterop/test/Microsoft.JSInterop.Test/DotNetDispatcherTest.cs b/src/JSInterop/test/Microsoft.JSInterop.Test/DotNetDispatcherTest.cs new file mode 100644 index 000000000000..eec537f987d2 --- /dev/null +++ b/src/JSInterop/test/Microsoft.JSInterop.Test/DotNetDispatcherTest.cs @@ -0,0 +1,443 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Linq; +using System.Threading.Tasks; +using Xunit; + +namespace Microsoft.JSInterop.Test +{ + public class DotNetDispatcherTest + { + private readonly static string thisAssemblyName + = typeof(DotNetDispatcherTest).Assembly.GetName().Name; + private readonly TestJSRuntime jsRuntime + = new TestJSRuntime(); + + [Fact] + public void CannotInvokeWithEmptyAssemblyName() + { + var ex = Assert.Throws(() => + { + DotNetDispatcher.Invoke(" ", "SomeMethod", default, "[]"); + }); + + Assert.StartsWith("Cannot be null, empty, or whitespace.", ex.Message); + Assert.Equal("assemblyName", ex.ParamName); + } + + [Fact] + public void CannotInvokeWithEmptyMethodIdentifier() + { + var ex = Assert.Throws(() => + { + DotNetDispatcher.Invoke("SomeAssembly", " ", default, "[]"); + }); + + Assert.StartsWith("Cannot be null, empty, or whitespace.", ex.Message); + Assert.Equal("methodIdentifier", ex.ParamName); + } + + [Fact] + public void CannotInvokeMethodsOnUnloadedAssembly() + { + var assemblyName = "Some.Fake.Assembly"; + var ex = Assert.Throws(() => + { + DotNetDispatcher.Invoke(assemblyName, "SomeMethod", default, null); + }); + + Assert.Equal($"There is no loaded assembly with the name '{assemblyName}'.", ex.Message); + } + + // Note: Currently it's also not possible to invoke generic methods. + // That's not something determined by DotNetDispatcher, but rather by the fact that we + // don't close over the generics in the reflection code. + // Not defining this behavior through unit tests because the default outcome is + // fine (an exception stating what info is missing). + + [Theory] + [InlineData("MethodOnInternalType")] + [InlineData("PrivateMethod")] + [InlineData("ProtectedMethod")] + [InlineData("StaticMethodWithoutAttribute")] // That's not really its identifier; just making the point that there's no way to invoke it + [InlineData("InstanceMethodWithoutAttribute")] // That's not really its identifier; just making the point that there's no way to invoke it + public void CannotInvokeUnsuitableMethods(string methodIdentifier) + { + var ex = Assert.Throws(() => + { + DotNetDispatcher.Invoke(thisAssemblyName, methodIdentifier, default, null); + }); + + Assert.Equal($"The assembly '{thisAssemblyName}' does not contain a public method with [JSInvokableAttribute(\"{methodIdentifier}\")].", ex.Message); + } + + [Fact] + public Task CanInvokeStaticVoidMethod() => WithJSRuntime(jsRuntime => + { + // Arrange/Act + SomePublicType.DidInvokeMyInvocableStaticVoid = false; + var resultJson = DotNetDispatcher.Invoke(thisAssemblyName, "InvocableStaticVoid", default, null); + + // Assert + Assert.Null(resultJson); + Assert.True(SomePublicType.DidInvokeMyInvocableStaticVoid); + }); + + [Fact] + public Task CanInvokeStaticNonVoidMethod() => WithJSRuntime(jsRuntime => + { + // Arrange/Act + var resultJson = DotNetDispatcher.Invoke(thisAssemblyName, "InvocableStaticNonVoid", default, null); + var result = Json.Deserialize(resultJson); + + // Assert + Assert.Equal("Test", result.StringVal); + Assert.Equal(123, result.IntVal); + }); + + [Fact] + public Task CanInvokeStaticNonVoidMethodWithoutCustomIdentifier() => WithJSRuntime(jsRuntime => + { + // Arrange/Act + var resultJson = DotNetDispatcher.Invoke(thisAssemblyName, nameof(SomePublicType.InvokableMethodWithoutCustomIdentifier), default, null); + var result = Json.Deserialize(resultJson); + + // Assert + Assert.Equal("InvokableMethodWithoutCustomIdentifier", result.StringVal); + Assert.Equal(456, result.IntVal); + }); + + [Fact] + public Task CanInvokeStaticWithParams() => WithJSRuntime(jsRuntime => + { + // Arrange: Track a .NET object to use as an arg + var arg3 = new TestDTO { IntVal = 999, StringVal = "My string" }; + jsRuntime.Invoke("unimportant", new DotNetObjectRef(arg3)); + + // Arrange: Remaining args + var argsJson = Json.Serialize(new object[] { + new TestDTO { StringVal = "Another string", IntVal = 456 }, + new[] { 100, 200 }, + "__dotNetObject:1" + }); + + // Act + var resultJson = DotNetDispatcher.Invoke(thisAssemblyName, "InvocableStaticWithParams", default, argsJson); + var result = Json.Deserialize(resultJson); + + // Assert: First result value marshalled via JSON + var resultDto1 = (TestDTO)jsRuntime.ArgSerializerStrategy.DeserializeObject(result[0], typeof(TestDTO)); + Assert.Equal("ANOTHER STRING", resultDto1.StringVal); + Assert.Equal(756, resultDto1.IntVal); + + // Assert: Second result value marshalled by ref + var resultDto2Ref = (string)result[1]; + Assert.Equal("__dotNetObject:2", resultDto2Ref); + var resultDto2 = (TestDTO)jsRuntime.ArgSerializerStrategy.FindDotNetObject(2); + Assert.Equal("MY STRING", resultDto2.StringVal); + Assert.Equal(1299, resultDto2.IntVal); + }); + + [Fact] + public Task CanInvokeInstanceVoidMethod() => WithJSRuntime(jsRuntime => + { + // Arrange: Track some instance + var targetInstance = new SomePublicType(); + jsRuntime.Invoke("unimportant", new DotNetObjectRef(targetInstance)); + + // Act + var resultJson = DotNetDispatcher.Invoke(null, "InvokableInstanceVoid", 1, null); + + // Assert + Assert.Null(resultJson); + Assert.True(targetInstance.DidInvokeMyInvocableInstanceVoid); + }); + + [Fact] + public Task CanInvokeBaseInstanceVoidMethod() => WithJSRuntime(jsRuntime => + { + // Arrange: Track some instance + var targetInstance = new DerivedClass(); + jsRuntime.Invoke("unimportant", new DotNetObjectRef(targetInstance)); + + // Act + var resultJson = DotNetDispatcher.Invoke(null, "BaseClassInvokableInstanceVoid", 1, null); + + // Assert + Assert.Null(resultJson); + Assert.True(targetInstance.DidInvokeMyBaseClassInvocableInstanceVoid); + }); + + [Fact] + public Task CannotUseDotNetObjectRefAfterDisposal() => WithJSRuntime(jsRuntime => + { + // This test addresses the case where the developer calls objectRef.Dispose() + // from .NET code, as opposed to .dispose() from JS code + + // Arrange: Track some instance, then dispose it + var targetInstance = new SomePublicType(); + var objectRef = new DotNetObjectRef(targetInstance); + jsRuntime.Invoke("unimportant", objectRef); + objectRef.Dispose(); + + // Act/Assert + var ex = Assert.Throws( + () => DotNetDispatcher.Invoke(null, "InvokableInstanceVoid", 1, null)); + Assert.StartsWith("There is no tracked object with id '1'.", ex.Message); + }); + + [Fact] + public Task CannotUseDotNetObjectRefAfterReleaseDotNetObject() => WithJSRuntime(jsRuntime => + { + // This test addresses the case where the developer calls .dispose() + // from JS code, as opposed to objectRef.Dispose() from .NET code + + // Arrange: Track some instance, then dispose it + var targetInstance = new SomePublicType(); + var objectRef = new DotNetObjectRef(targetInstance); + jsRuntime.Invoke("unimportant", objectRef); + DotNetDispatcher.ReleaseDotNetObject(1); + + // Act/Assert + var ex = Assert.Throws( + () => DotNetDispatcher.Invoke(null, "InvokableInstanceVoid", 1, null)); + Assert.StartsWith("There is no tracked object with id '1'.", ex.Message); + }); + + [Fact] + public Task CanInvokeInstanceMethodWithParams() => WithJSRuntime(jsRuntime => + { + // Arrange: Track some instance plus another object we'll pass as a param + var targetInstance = new SomePublicType(); + var arg2 = new TestDTO { IntVal = 1234, StringVal = "My string" }; + jsRuntime.Invoke("unimportant", + new DotNetObjectRef(targetInstance), + new DotNetObjectRef(arg2)); + var argsJson = "[\"myvalue\",\"__dotNetObject:2\"]"; + + // Act + var resultJson = DotNetDispatcher.Invoke(null, "InvokableInstanceMethod", 1, argsJson); + + // Assert + Assert.Equal("[\"You passed myvalue\",\"__dotNetObject:3\"]", resultJson); + var resultDto = (TestDTO)jsRuntime.ArgSerializerStrategy.FindDotNetObject(3); + Assert.Equal(1235, resultDto.IntVal); + Assert.Equal("MY STRING", resultDto.StringVal); + }); + + [Fact] + public void CannotInvokeWithIncorrectNumberOfParams() + { + // Arrange + var argsJson = Json.Serialize(new object[] { 1, 2, 3, 4 }); + + // Act/Assert + var ex = Assert.Throws(() => + { + DotNetDispatcher.Invoke(thisAssemblyName, "InvocableStaticWithParams", default, argsJson); + }); + + Assert.Equal("In call to 'InvocableStaticWithParams', expected 3 parameters but received 4.", ex.Message); + } + + [Fact] + public Task CanInvokeAsyncMethod() => WithJSRuntime(async jsRuntime => + { + // Arrange: Track some instance plus another object we'll pass as a param + var targetInstance = new SomePublicType(); + var arg2 = new TestDTO { IntVal = 1234, StringVal = "My string" }; + jsRuntime.Invoke("unimportant", new DotNetObjectRef(targetInstance), new DotNetObjectRef(arg2)); + + // Arrange: all args + var argsJson = Json.Serialize(new object[] + { + new TestDTO { IntVal = 1000, StringVal = "String via JSON" }, + "__dotNetObject:2" + }); + + // Act + var callId = "123"; + var resultTask = jsRuntime.NextInvocationTask; + DotNetDispatcher.BeginInvoke(callId, null, "InvokableAsyncMethod", 1, argsJson); + await resultTask; + var result = Json.Deserialize(jsRuntime.LastInvocationArgsJson); + var resultValue = (SimpleJson.JsonArray)result[2]; + + // Assert: Correct info to complete the async call + Assert.Equal(0, jsRuntime.LastInvocationAsyncHandle); // 0 because it doesn't want a further callback from JS to .NET + Assert.Equal("DotNet.jsCallDispatcher.endInvokeDotNetFromJS", jsRuntime.LastInvocationIdentifier); + Assert.Equal(3, result.Count); + Assert.Equal(callId, result[0]); + Assert.True((bool)result[1]); // Success flag + + // Assert: First result value marshalled via JSON + var resultDto1 = (TestDTO)jsRuntime.ArgSerializerStrategy.DeserializeObject(resultValue[0], typeof(TestDTO)); + Assert.Equal("STRING VIA JSON", resultDto1.StringVal); + Assert.Equal(2000, resultDto1.IntVal); + + // Assert: Second result value marshalled by ref + var resultDto2Ref = (string)resultValue[1]; + Assert.Equal("__dotNetObject:3", resultDto2Ref); + var resultDto2 = (TestDTO)jsRuntime.ArgSerializerStrategy.FindDotNetObject(3); + Assert.Equal("MY STRING", resultDto2.StringVal); + Assert.Equal(2468, resultDto2.IntVal); + }); + + Task WithJSRuntime(Action testCode) + { + return WithJSRuntime(jsRuntime => + { + testCode(jsRuntime); + return Task.CompletedTask; + }); + } + + async Task WithJSRuntime(Func testCode) + { + // Since the tests rely on the asynclocal JSRuntime.Current, ensure we + // are on a distinct async context with a non-null JSRuntime.Current + await Task.Yield(); + + var runtime = new TestJSRuntime(); + JSRuntime.SetCurrentJSRuntime(runtime); + await testCode(runtime); + } + + internal class SomeInteralType + { + [JSInvokable("MethodOnInternalType")] public void MyMethod() { } + } + + public class SomePublicType + { + public static bool DidInvokeMyInvocableStaticVoid; + public bool DidInvokeMyInvocableInstanceVoid; + + [JSInvokable("PrivateMethod")] private static void MyPrivateMethod() { } + [JSInvokable("ProtectedMethod")] protected static void MyProtectedMethod() { } + protected static void StaticMethodWithoutAttribute() { } + protected static void InstanceMethodWithoutAttribute() { } + + [JSInvokable("InvocableStaticVoid")] public static void MyInvocableVoid() + { + DidInvokeMyInvocableStaticVoid = true; + } + + [JSInvokable("InvocableStaticNonVoid")] + public static object MyInvocableNonVoid() + => new TestDTO { StringVal = "Test", IntVal = 123 }; + + [JSInvokable("InvocableStaticWithParams")] + public static object[] MyInvocableWithParams(TestDTO dtoViaJson, int[] incrementAmounts, TestDTO dtoByRef) + => new object[] + { + new TestDTO // Return via JSON marshalling + { + StringVal = dtoViaJson.StringVal.ToUpperInvariant(), + IntVal = dtoViaJson.IntVal + incrementAmounts.Sum() + }, + new DotNetObjectRef(new TestDTO // Return by ref + { + StringVal = dtoByRef.StringVal.ToUpperInvariant(), + IntVal = dtoByRef.IntVal + incrementAmounts.Sum() + }) + }; + + [JSInvokable] + public static TestDTO InvokableMethodWithoutCustomIdentifier() + => new TestDTO { StringVal = "InvokableMethodWithoutCustomIdentifier", IntVal = 456 }; + + [JSInvokable] + public void InvokableInstanceVoid() + { + DidInvokeMyInvocableInstanceVoid = true; + } + + [JSInvokable] + public object[] InvokableInstanceMethod(string someString, TestDTO someDTO) + { + // Returning an array to make the point that object references + // can be embedded anywhere in the result + return new object[] + { + $"You passed {someString}", + new DotNetObjectRef(new TestDTO + { + IntVal = someDTO.IntVal + 1, + StringVal = someDTO.StringVal.ToUpperInvariant() + }) + }; + } + + [JSInvokable] + public async Task InvokableAsyncMethod(TestDTO dtoViaJson, TestDTO dtoByRef) + { + await Task.Delay(50); + return new object[] + { + new TestDTO // Return via JSON + { + StringVal = dtoViaJson.StringVal.ToUpperInvariant(), + IntVal = dtoViaJson.IntVal * 2, + }, + new DotNetObjectRef(new TestDTO // Return by ref + { + StringVal = dtoByRef.StringVal.ToUpperInvariant(), + IntVal = dtoByRef.IntVal * 2, + }) + }; + } + } + + public class BaseClass + { + public bool DidInvokeMyBaseClassInvocableInstanceVoid; + + [JSInvokable] + public void BaseClassInvokableInstanceVoid() + { + DidInvokeMyBaseClassInvocableInstanceVoid = true; + } + } + + public class DerivedClass : BaseClass + { + } + + public class TestDTO + { + public string StringVal { get; set; } + public int IntVal { get; set; } + } + + public class TestJSRuntime : JSInProcessRuntimeBase + { + private TaskCompletionSource _nextInvocationTcs = new TaskCompletionSource(); + public Task NextInvocationTask => _nextInvocationTcs.Task; + public long LastInvocationAsyncHandle { get; private set; } + public string LastInvocationIdentifier { get; private set; } + public string LastInvocationArgsJson { get; private set; } + + protected override void BeginInvokeJS(long asyncHandle, string identifier, string argsJson) + { + LastInvocationAsyncHandle = asyncHandle; + LastInvocationIdentifier = identifier; + LastInvocationArgsJson = argsJson; + _nextInvocationTcs.SetResult(null); + _nextInvocationTcs = new TaskCompletionSource(); + } + + protected override string InvokeJS(string identifier, string argsJson) + { + LastInvocationAsyncHandle = default; + LastInvocationIdentifier = identifier; + LastInvocationArgsJson = argsJson; + _nextInvocationTcs.SetResult(null); + _nextInvocationTcs = new TaskCompletionSource(); + return null; + } + } + } +} diff --git a/src/JSInterop/test/Microsoft.JSInterop.Test/DotNetObjectRefTest.cs b/src/JSInterop/test/Microsoft.JSInterop.Test/DotNetObjectRefTest.cs new file mode 100644 index 000000000000..969dcae79d91 --- /dev/null +++ b/src/JSInterop/test/Microsoft.JSInterop.Test/DotNetObjectRefTest.cs @@ -0,0 +1,68 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Xunit; + +namespace Microsoft.JSInterop.Test +{ + public class DotNetObjectRefTest + { + [Fact] + public void CanAccessValue() + { + var obj = new object(); + Assert.Same(obj, new DotNetObjectRef(obj).Value); + } + + [Fact] + public void CanAssociateWithSameRuntimeMultipleTimes() + { + var objRef = new DotNetObjectRef(new object()); + var jsRuntime = new TestJsRuntime(); + objRef.EnsureAttachedToJsRuntime(jsRuntime); + objRef.EnsureAttachedToJsRuntime(jsRuntime); + } + + [Fact] + public void CannotAssociateWithDifferentRuntimes() + { + var objRef = new DotNetObjectRef(new object()); + var jsRuntime1 = new TestJsRuntime(); + var jsRuntime2 = new TestJsRuntime(); + objRef.EnsureAttachedToJsRuntime(jsRuntime1); + + var ex = Assert.Throws( + () => objRef.EnsureAttachedToJsRuntime(jsRuntime2)); + Assert.Contains("Do not attempt to re-use", ex.Message); + } + + [Fact] + public void NotifiesAssociatedJsRuntimeOfDisposal() + { + // Arrange + var objRef = new DotNetObjectRef(new object()); + var jsRuntime = new TestJsRuntime(); + objRef.EnsureAttachedToJsRuntime(jsRuntime); + + // Act + objRef.Dispose(); + + // Assert + Assert.Equal(new[] { objRef }, jsRuntime.UntrackedRefs); + } + + class TestJsRuntime : IJSRuntime + { + public List UntrackedRefs = new List(); + + public Task InvokeAsync(string identifier, params object[] args) + => throw new NotImplementedException(); + + public void UntrackObjectRef(DotNetObjectRef dotNetObjectRef) + => UntrackedRefs.Add(dotNetObjectRef); + } + } +} diff --git a/src/JSInterop/test/Microsoft.JSInterop.Test/JSInProcessRuntimeBaseTest.cs b/src/JSInterop/test/Microsoft.JSInterop.Test/JSInProcessRuntimeBaseTest.cs new file mode 100644 index 000000000000..d2e71f6eb217 --- /dev/null +++ b/src/JSInterop/test/Microsoft.JSInterop.Test/JSInProcessRuntimeBaseTest.cs @@ -0,0 +1,117 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace Microsoft.JSInterop.Test +{ + public class JSInProcessRuntimeBaseTest + { + [Fact] + public void DispatchesSyncCallsAndDeserializesResults() + { + // Arrange + var runtime = new TestJSInProcessRuntime + { + NextResultJson = Json.Serialize( + new TestDTO { IntValue = 123, StringValue = "Hello" }) + }; + + // Act + var syncResult = runtime.Invoke("test identifier 1", "arg1", 123, true ); + var call = runtime.InvokeCalls.Single(); + + // Assert + Assert.Equal(123, syncResult.IntValue); + Assert.Equal("Hello", syncResult.StringValue); + Assert.Equal("test identifier 1", call.Identifier); + Assert.Equal("[\"arg1\",123,true]", call.ArgsJson); + } + + [Fact] + public void SerializesDotNetObjectWrappersInKnownFormat() + { + // Arrange + var runtime = new TestJSInProcessRuntime { NextResultJson = null }; + var obj1 = new object(); + var obj2 = new object(); + var obj3 = new object(); + + // Act + // Showing we can pass the DotNetObject either as top-level args or nested + var syncResult = runtime.Invoke("test identifier", + new DotNetObjectRef(obj1), + new Dictionary + { + { "obj2", new DotNetObjectRef(obj2) }, + { "obj3", new DotNetObjectRef(obj3) } + }); + + // Assert: Handles null result string + Assert.Null(syncResult); + + // Assert: Serialized as expected + var call = runtime.InvokeCalls.Single(); + Assert.Equal("test identifier", call.Identifier); + Assert.Equal("[\"__dotNetObject:1\",{\"obj2\":\"__dotNetObject:2\",\"obj3\":\"__dotNetObject:3\"}]", call.ArgsJson); + + // Assert: Objects were tracked + Assert.Same(obj1, runtime.ArgSerializerStrategy.FindDotNetObject(1)); + Assert.Same(obj2, runtime.ArgSerializerStrategy.FindDotNetObject(2)); + Assert.Same(obj3, runtime.ArgSerializerStrategy.FindDotNetObject(3)); + } + + [Fact] + public void SyncCallResultCanIncludeDotNetObjects() + { + // Arrange + var runtime = new TestJSInProcessRuntime + { + NextResultJson = "[\"__dotNetObject:2\",\"__dotNetObject:1\"]" + }; + var obj1 = new object(); + var obj2 = new object(); + + // Act + var syncResult = runtime.Invoke("test identifier", + new DotNetObjectRef(obj1), + "some other arg", + new DotNetObjectRef(obj2)); + var call = runtime.InvokeCalls.Single(); + + // Assert + Assert.Equal(new[] { obj2, obj1 }, syncResult); + } + + class TestDTO + { + public int IntValue { get; set; } + public string StringValue { get; set; } + } + + class TestJSInProcessRuntime : JSInProcessRuntimeBase + { + public List InvokeCalls { get; set; } = new List(); + + public string NextResultJson { get; set; } + + protected override string InvokeJS(string identifier, string argsJson) + { + InvokeCalls.Add(new InvokeArgs { Identifier = identifier, ArgsJson = argsJson }); + return NextResultJson; + } + + public class InvokeArgs + { + public string Identifier { get; set; } + public string ArgsJson { get; set; } + } + + protected override void BeginInvokeJS(long asyncHandle, string identifier, string argsJson) + => throw new NotImplementedException("This test only covers sync calls"); + } + } +} diff --git a/src/JSInterop/test/Microsoft.JSInterop.Test/JSRuntimeBaseTest.cs b/src/JSInterop/test/Microsoft.JSInterop.Test/JSRuntimeBaseTest.cs new file mode 100644 index 000000000000..9193d6deb8d1 --- /dev/null +++ b/src/JSInterop/test/Microsoft.JSInterop.Test/JSRuntimeBaseTest.cs @@ -0,0 +1,191 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.JSInterop.Internal; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace Microsoft.JSInterop.Test +{ + public class JSRuntimeBaseTest + { + [Fact] + public void DispatchesAsyncCallsWithDistinctAsyncHandles() + { + // Arrange + var runtime = new TestJSRuntime(); + + // Act + runtime.InvokeAsync("test identifier 1", "arg1", 123, true ); + runtime.InvokeAsync("test identifier 2", "some other arg"); + + // Assert + Assert.Collection(runtime.BeginInvokeCalls, + call => + { + Assert.Equal("test identifier 1", call.Identifier); + Assert.Equal("[\"arg1\",123,true]", call.ArgsJson); + }, + call => + { + Assert.Equal("test identifier 2", call.Identifier); + Assert.Equal("[\"some other arg\"]", call.ArgsJson); + Assert.NotEqual(runtime.BeginInvokeCalls[0].AsyncHandle, call.AsyncHandle); + }); + } + + [Fact] + public void CanCompleteAsyncCallsAsSuccess() + { + // Arrange + var runtime = new TestJSRuntime(); + + // Act/Assert: Tasks not initially completed + var unrelatedTask = runtime.InvokeAsync("unrelated call", Array.Empty()); + var task = runtime.InvokeAsync("test identifier", Array.Empty()); + Assert.False(unrelatedTask.IsCompleted); + Assert.False(task.IsCompleted); + + // Act/Assert: Task can be completed + runtime.OnEndInvoke( + runtime.BeginInvokeCalls[1].AsyncHandle, + /* succeeded: */ true, + "my result"); + Assert.False(unrelatedTask.IsCompleted); + Assert.True(task.IsCompleted); + Assert.Equal("my result", task.Result); + } + + [Fact] + public void CanCompleteAsyncCallsAsFailure() + { + // Arrange + var runtime = new TestJSRuntime(); + + // Act/Assert: Tasks not initially completed + var unrelatedTask = runtime.InvokeAsync("unrelated call", Array.Empty()); + var task = runtime.InvokeAsync("test identifier", Array.Empty()); + Assert.False(unrelatedTask.IsCompleted); + Assert.False(task.IsCompleted); + + // Act/Assert: Task can be failed + runtime.OnEndInvoke( + runtime.BeginInvokeCalls[1].AsyncHandle, + /* succeeded: */ false, + "This is a test exception"); + Assert.False(unrelatedTask.IsCompleted); + Assert.True(task.IsCompleted); + + Assert.IsType(task.Exception); + Assert.IsType(task.Exception.InnerException); + Assert.Equal("This is a test exception", ((JSException)task.Exception.InnerException).Message); + } + + [Fact] + public void CannotCompleteSameAsyncCallMoreThanOnce() + { + // Arrange + var runtime = new TestJSRuntime(); + + // Act/Assert + runtime.InvokeAsync("test identifier", Array.Empty()); + var asyncHandle = runtime.BeginInvokeCalls[0].AsyncHandle; + runtime.OnEndInvoke(asyncHandle, true, null); + var ex = Assert.Throws(() => + { + // Second "end invoke" will fail + runtime.OnEndInvoke(asyncHandle, true, null); + }); + Assert.Equal($"There is no pending task with handle '{asyncHandle}'.", ex.Message); + } + + [Fact] + public void SerializesDotNetObjectWrappersInKnownFormat() + { + // Arrange + var runtime = new TestJSRuntime(); + var obj1 = new object(); + var obj2 = new object(); + var obj3 = new object(); + + // Act + // Showing we can pass the DotNetObject either as top-level args or nested + var obj1Ref = new DotNetObjectRef(obj1); + var obj1DifferentRef = new DotNetObjectRef(obj1); + runtime.InvokeAsync("test identifier", + obj1Ref, + new Dictionary + { + { "obj2", new DotNetObjectRef(obj2) }, + { "obj3", new DotNetObjectRef(obj3) }, + { "obj1SameRef", obj1Ref }, + { "obj1DifferentRef", obj1DifferentRef }, + }); + + // Assert: Serialized as expected + var call = runtime.BeginInvokeCalls.Single(); + Assert.Equal("test identifier", call.Identifier); + Assert.Equal("[\"__dotNetObject:1\",{\"obj2\":\"__dotNetObject:2\",\"obj3\":\"__dotNetObject:3\",\"obj1SameRef\":\"__dotNetObject:1\",\"obj1DifferentRef\":\"__dotNetObject:4\"}]", call.ArgsJson); + + // Assert: Objects were tracked + Assert.Same(obj1, runtime.ArgSerializerStrategy.FindDotNetObject(1)); + Assert.Same(obj2, runtime.ArgSerializerStrategy.FindDotNetObject(2)); + Assert.Same(obj3, runtime.ArgSerializerStrategy.FindDotNetObject(3)); + Assert.Same(obj1, runtime.ArgSerializerStrategy.FindDotNetObject(4)); + } + + [Fact] + public void SupportsCustomSerializationForArguments() + { + // Arrange + var runtime = new TestJSRuntime(); + + // Arrange/Act + runtime.InvokeAsync("test identifier", + new WithCustomArgSerializer()); + + // Asssert + var call = runtime.BeginInvokeCalls.Single(); + Assert.Equal("[{\"key1\":\"value1\",\"key2\":123}]", call.ArgsJson); + } + + class TestJSRuntime : JSRuntimeBase + { + public List BeginInvokeCalls = new List(); + + public class BeginInvokeAsyncArgs + { + public long AsyncHandle { get; set; } + public string Identifier { get; set; } + public string ArgsJson { get; set; } + } + + protected override void BeginInvokeJS(long asyncHandle, string identifier, string argsJson) + { + BeginInvokeCalls.Add(new BeginInvokeAsyncArgs + { + AsyncHandle = asyncHandle, + Identifier = identifier, + ArgsJson = argsJson, + }); + } + + public void OnEndInvoke(long asyncHandle, bool succeeded, object resultOrException) + => EndInvokeJS(asyncHandle, succeeded, resultOrException); + } + + class WithCustomArgSerializer : ICustomArgSerializer + { + public object ToJsonPrimitive() + { + return new Dictionary + { + { "key1", "value1" }, + { "key2", 123 }, + }; + } + } + } +} diff --git a/src/JSInterop/test/Microsoft.JSInterop.Test/JSRuntimeTest.cs b/src/JSInterop/test/Microsoft.JSInterop.Test/JSRuntimeTest.cs new file mode 100644 index 000000000000..d5fed45ea4ff --- /dev/null +++ b/src/JSInterop/test/Microsoft.JSInterop.Test/JSRuntimeTest.cs @@ -0,0 +1,37 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Linq; +using System.Threading.Tasks; +using Xunit; + +namespace Microsoft.JSInterop.Test +{ + public class JSRuntimeTest + { + [Fact] + public async Task CanHaveDistinctJSRuntimeInstancesInEachAsyncContext() + { + var tasks = Enumerable.Range(0, 20).Select(async _ => + { + var jsRuntime = new FakeJSRuntime(); + JSRuntime.SetCurrentJSRuntime(jsRuntime); + await Task.Delay(50).ConfigureAwait(false); + Assert.Same(jsRuntime, JSRuntime.Current); + }); + + await Task.WhenAll(tasks); + Assert.Null(JSRuntime.Current); + } + + private class FakeJSRuntime : IJSRuntime + { + public Task InvokeAsync(string identifier, params object[] args) + => throw new NotImplementedException(); + + public void UntrackObjectRef(DotNetObjectRef dotNetObjectRef) + => throw new NotImplementedException(); + } + } +} diff --git a/src/JSInterop/test/Microsoft.JSInterop.Test/JsonUtilTest.cs b/src/JSInterop/test/Microsoft.JSInterop.Test/JsonUtilTest.cs new file mode 100644 index 000000000000..1be98b681e19 --- /dev/null +++ b/src/JSInterop/test/Microsoft.JSInterop.Test/JsonUtilTest.cs @@ -0,0 +1,349 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.JSInterop.Internal; +using System; +using System.Collections.Generic; +using Xunit; + +namespace Microsoft.JSInterop.Test +{ + public class JsonUtilTest + { + // It's not useful to have a complete set of behavior specifications for + // what the JSON serializer/deserializer does in all cases here. We merely + // expose a simple wrapper over a third-party library that maintains its + // own specs and tests. + // + // We should only add tests here to cover behaviors that Blazor itself + // depends on. + + [Theory] + [InlineData(null, "null")] + [InlineData("My string", "\"My string\"")] + [InlineData(123, "123")] + [InlineData(123.456f, "123.456")] + [InlineData(123.456d, "123.456")] + [InlineData(true, "true")] + public void CanSerializePrimitivesToJson(object value, string expectedJson) + { + Assert.Equal(expectedJson, Json.Serialize(value)); + } + + [Theory] + [InlineData("null", null)] + [InlineData("\"My string\"", "My string")] + [InlineData("123", 123L)] // Would also accept 123 as a System.Int32, but Int64 is fine as a default + [InlineData("123.456", 123.456d)] + [InlineData("true", true)] + public void CanDeserializePrimitivesFromJson(string json, object expectedValue) + { + Assert.Equal(expectedValue, Json.Deserialize(json)); + } + + [Fact] + public void CanSerializeClassToJson() + { + // Arrange + var person = new Person + { + Id = 1844, + Name = "Athos", + Pets = new[] { "Aramis", "Porthos", "D'Artagnan" }, + Hobby = Hobbies.Swordfighting, + SecondaryHobby = Hobbies.Reading, + Nicknames = new List { "Comte de la Fère", "Armand" }, + BirthInstant = new DateTimeOffset(1825, 8, 6, 18, 45, 21, TimeSpan.FromHours(-6)), + Age = new TimeSpan(7665, 1, 30, 0), + Allergies = new Dictionary { { "Ducks", true }, { "Geese", false } }, + }; + + // Act/Assert + Assert.Equal( + "{\"id\":1844,\"name\":\"Athos\",\"pets\":[\"Aramis\",\"Porthos\",\"D'Artagnan\"],\"hobby\":2,\"secondaryHobby\":1,\"nullHobby\":null,\"nicknames\":[\"Comte de la Fère\",\"Armand\"],\"birthInstant\":\"1825-08-06T18:45:21.0000000-06:00\",\"age\":\"7665.01:30:00\",\"allergies\":{\"Ducks\":true,\"Geese\":false}}", + Json.Serialize(person)); + } + + [Fact] + public void CanDeserializeClassFromJson() + { + // Arrange + var json = "{\"id\":1844,\"name\":\"Athos\",\"pets\":[\"Aramis\",\"Porthos\",\"D'Artagnan\"],\"hobby\":2,\"secondaryHobby\":1,\"nullHobby\":null,\"nicknames\":[\"Comte de la Fère\",\"Armand\"],\"birthInstant\":\"1825-08-06T18:45:21.0000000-06:00\",\"age\":\"7665.01:30:00\",\"allergies\":{\"Ducks\":true,\"Geese\":false}}"; + + // Act + var person = Json.Deserialize(json); + + // Assert + Assert.Equal(1844, person.Id); + Assert.Equal("Athos", person.Name); + Assert.Equal(new[] { "Aramis", "Porthos", "D'Artagnan" }, person.Pets); + Assert.Equal(Hobbies.Swordfighting, person.Hobby); + Assert.Equal(Hobbies.Reading, person.SecondaryHobby); + Assert.Null(person.NullHobby); + Assert.Equal(new[] { "Comte de la Fère", "Armand" }, person.Nicknames); + Assert.Equal(new DateTimeOffset(1825, 8, 6, 18, 45, 21, TimeSpan.FromHours(-6)), person.BirthInstant); + Assert.Equal(new TimeSpan(7665, 1, 30, 0), person.Age); + Assert.Equal(new Dictionary { { "Ducks", true }, { "Geese", false } }, person.Allergies); + } + + [Fact] + public void CanDeserializeWithCaseInsensitiveKeys() + { + // Arrange + var json = "{\"ID\":1844,\"NamE\":\"Athos\"}"; + + // Act + var person = Json.Deserialize(json); + + // Assert + Assert.Equal(1844, person.Id); + Assert.Equal("Athos", person.Name); + } + + [Fact] + public void DeserializationPrefersPropertiesOverFields() + { + // Arrange + var json = "{\"member1\":\"Hello\"}"; + + // Act + var person = Json.Deserialize(json); + + // Assert + Assert.Equal("Hello", person.Member1); + Assert.Null(person.member1); + } + + [Fact] + public void CanSerializeStructToJson() + { + // Arrange + var commandResult = new SimpleStruct + { + StringProperty = "Test", + BoolProperty = true, + NullableIntProperty = 1 + }; + + // Act + var result = Json.Serialize(commandResult); + + // Assert + Assert.Equal("{\"stringProperty\":\"Test\",\"boolProperty\":true,\"nullableIntProperty\":1}", result); + } + + [Fact] + public void CanDeserializeStructFromJson() + { + // Arrange + var json = "{\"stringProperty\":\"Test\",\"boolProperty\":true,\"nullableIntProperty\":1}"; + + //Act + var simpleError = Json.Deserialize(json); + + // Assert + Assert.Equal("Test", simpleError.StringProperty); + Assert.True(simpleError.BoolProperty); + Assert.Equal(1, simpleError.NullableIntProperty); + } + + [Fact] + public void CanCreateInstanceOfClassWithPrivateConstructor() + { + // Arrange + var expectedName = "NameValue"; + var json = $"{{\"Name\":\"{expectedName}\"}}"; + + // Act + var instance = Json.Deserialize(json); + + // Assert + Assert.Equal(expectedName, instance.Name); + } + + [Fact] + public void CanSetValueOfPublicPropertiesWithNonPublicSetters() + { + // Arrange + var expectedPrivateValue = "PrivateValue"; + var expectedProtectedValue = "ProtectedValue"; + var expectedInternalValue = "InternalValue"; + + var json = "{" + + $"\"PrivateSetter\":\"{expectedPrivateValue}\"," + + $"\"ProtectedSetter\":\"{expectedProtectedValue}\"," + + $"\"InternalSetter\":\"{expectedInternalValue}\"," + + "}"; + + // Act + var instance = Json.Deserialize(json); + + // Assert + Assert.Equal(expectedPrivateValue, instance.PrivateSetter); + Assert.Equal(expectedProtectedValue, instance.ProtectedSetter); + Assert.Equal(expectedInternalValue, instance.InternalSetter); + } + + [Fact] + public void RejectsTypesWithAmbiguouslyNamedProperties() + { + var ex = Assert.Throws(() => + { + Json.Deserialize("{}"); + }); + + Assert.Equal($"The type '{typeof(ClashingProperties).FullName}' contains multiple public properties " + + $"with names case-insensitively matching '{nameof(ClashingProperties.PROP1).ToLowerInvariant()}'. " + + $"Such types cannot be used for JSON deserialization.", + ex.Message); + } + + [Fact] + public void RejectsTypesWithAmbiguouslyNamedFields() + { + var ex = Assert.Throws(() => + { + Json.Deserialize("{}"); + }); + + Assert.Equal($"The type '{typeof(ClashingFields).FullName}' contains multiple public fields " + + $"with names case-insensitively matching '{nameof(ClashingFields.Field1).ToLowerInvariant()}'. " + + $"Such types cannot be used for JSON deserialization.", + ex.Message); + } + + [Fact] + public void NonEmptyConstructorThrowsUsefulException() + { + // Arrange + var json = "{\"Property\":1}"; + var type = typeof(NonEmptyConstructorPoco); + + // Act + var exception = Assert.Throws(() => + { + Json.Deserialize(json); + }); + + // Assert + Assert.Equal( + $"Cannot deserialize JSON into type '{type.FullName}' because it does not have a public parameterless constructor.", + exception.Message); + } + + // Test cases based on https://github.com/JamesNK/Newtonsoft.Json/blob/122afba9908832bd5ac207164ee6c303bfd65cf1/Src/Newtonsoft.Json.Tests/Utilities/StringUtilsTests.cs#L41 + // The only difference is that our logic doesn't have to handle space-separated words, + // because we're only use this for camelcasing .NET member names + // + // Not all of the following cases are really valid .NET member names, but we have no reason + // to implement more logic to detect invalid member names besides the basics (null or empty). + [Theory] + [InlineData("URLValue", "urlValue")] + [InlineData("URL", "url")] + [InlineData("ID", "id")] + [InlineData("I", "i")] + [InlineData("Person", "person")] + [InlineData("xPhone", "xPhone")] + [InlineData("XPhone", "xPhone")] + [InlineData("X_Phone", "x_Phone")] + [InlineData("X__Phone", "x__Phone")] + [InlineData("IsCIA", "isCIA")] + [InlineData("VmQ", "vmQ")] + [InlineData("Xml2Json", "xml2Json")] + [InlineData("SnAkEcAsE", "snAkEcAsE")] + [InlineData("SnA__kEcAsE", "snA__kEcAsE")] + [InlineData("already_snake_case_", "already_snake_case_")] + [InlineData("IsJSONProperty", "isJSONProperty")] + [InlineData("SHOUTING_CASE", "shoutinG_CASE")] + [InlineData("9999-12-31T23:59:59.9999999Z", "9999-12-31T23:59:59.9999999Z")] + [InlineData("Hi!! This is text. Time to test.", "hi!! This is text. Time to test.")] + [InlineData("BUILDING", "building")] + [InlineData("BUILDINGProperty", "buildingProperty")] + public void MemberNameToCamelCase_Valid(string input, string expectedOutput) + { + Assert.Equal(expectedOutput, CamelCase.MemberNameToCamelCase(input)); + } + + [Theory] + [InlineData("")] + [InlineData(null)] + public void MemberNameToCamelCase_Invalid(string input) + { + var ex = Assert.Throws(() => + CamelCase.MemberNameToCamelCase(input)); + Assert.Equal("value", ex.ParamName); + Assert.StartsWith($"The value '{input ?? "null"}' is not a valid member name.", ex.Message); + } + + class NonEmptyConstructorPoco + { + public NonEmptyConstructorPoco(int parameter) {} + + public int Property { get; set; } + } + + struct SimpleStruct + { + public string StringProperty { get; set; } + public bool BoolProperty { get; set; } + public int? NullableIntProperty { get; set; } + } + + class Person + { + public int Id { get; set; } + public string Name { get; set; } + public string[] Pets { get; set; } + public Hobbies Hobby { get; set; } + public Hobbies? SecondaryHobby { get; set; } + public Hobbies? NullHobby { get; set; } + public IList Nicknames { get; set; } + public DateTimeOffset BirthInstant { get; set; } + public TimeSpan Age { get; set; } + public IDictionary Allergies { get; set; } + } + + enum Hobbies { Reading = 1, Swordfighting = 2 } + +#pragma warning disable 0649 + class ClashingProperties + { + public string Prop1 { get; set; } + public int PROP1 { get; set; } + } + + class ClashingFields + { + public string Field1; + public int field1; + } + + class PrefersPropertiesOverFields + { + public string member1; + public string Member1 { get; set; } + } +#pragma warning restore 0649 + + class PrivateConstructor + { + public string Name { get; set; } + + private PrivateConstructor() + { + } + + public PrivateConstructor(string name) + { + Name = name; + } + } + + class NonPublicSetterOnPublicProperty + { + public string PrivateSetter { get; private set; } + public string ProtectedSetter { get; protected set; } + public string InternalSetter { get; internal set; } + } + } +} diff --git a/src/JSInterop/test/Microsoft.JSInterop.Test/Microsoft.JSInterop.Test.csproj b/src/JSInterop/test/Microsoft.JSInterop.Test/Microsoft.JSInterop.Test.csproj new file mode 100644 index 000000000000..893a11d33a81 --- /dev/null +++ b/src/JSInterop/test/Microsoft.JSInterop.Test/Microsoft.JSInterop.Test.csproj @@ -0,0 +1,20 @@ + + + + netcoreapp2.1 + false + 7.3 + + + + + + + + + + + + + + From 4de3b5e105caf3b9598a7f476d7ca9e244c5b28d Mon Sep 17 00:00:00 2001 From: Chris Ross Date: Wed, 9 Jan 2019 13:38:12 -0800 Subject: [PATCH 041/183] Move the HostFactoryResolver to Extensions (dotnet/extensions#925) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/9cd9cea3983ec3421038c4b540161f0686273754 --- .../HostFactoryResolver.cs | 112 +++++++++++++++++ .../Shared.Tests/HostFactoryResolverTests.cs | 116 ++++++++++++++++++ .../Microsoft.AspNetCore.Shared.Tests.csproj | 10 ++ .../BuildWebHostInvalidSignature.csproj | 12 ++ .../BuildWebHostInvalidSignature/Program.cs | 17 +++ .../BuildWebHostPatternTestSite.csproj | 12 ++ .../BuildWebHostPatternTestSite/Program.cs | 16 +++ .../CreateHostBuilderInvalidSignature.csproj | 12 ++ .../Program.cs | 18 +++ .../CreateHostBuilderPatternTestSite.csproj | 12 ++ .../Program.cs | 19 +++ ...reateWebHostBuilderInvalidSignature.csproj | 12 ++ .../Program.cs | 17 +++ ...CreateWebHostBuilderPatternTestSite.csproj | 12 ++ .../Program.cs | 19 +++ .../test/testassets/MockHostTypes/Host.cs | 12 ++ .../testassets/MockHostTypes/HostBuilder.cs | 10 ++ .../test/testassets/MockHostTypes/IHost.cs | 12 ++ .../testassets/MockHostTypes/IHostBuilder.cs | 10 ++ .../test/testassets/MockHostTypes/IWebHost.cs | 12 ++ .../MockHostTypes/IWebHostBuilder.cs | 10 ++ .../MockHostTypes/MockHostTypes.csproj | 7 ++ .../MockHostTypes/ServiceProvider.cs | 12 ++ .../test/testassets/MockHostTypes/WebHost.cs | 12 ++ .../MockHostTypes/WebHostBuilder.cs | 10 ++ 25 files changed, 523 insertions(+) create mode 100644 src/Shared/HostFactoryResolver/HostFactoryResolver.cs create mode 100644 src/Shared/test/Shared.Tests/HostFactoryResolverTests.cs create mode 100644 src/Shared/test/testassets/BuildWebHostInvalidSignature/BuildWebHostInvalidSignature.csproj create mode 100644 src/Shared/test/testassets/BuildWebHostInvalidSignature/Program.cs create mode 100644 src/Shared/test/testassets/BuildWebHostPatternTestSite/BuildWebHostPatternTestSite.csproj create mode 100644 src/Shared/test/testassets/BuildWebHostPatternTestSite/Program.cs create mode 100644 src/Shared/test/testassets/CreateHostBuilderInvalidSignature/CreateHostBuilderInvalidSignature.csproj create mode 100644 src/Shared/test/testassets/CreateHostBuilderInvalidSignature/Program.cs create mode 100644 src/Shared/test/testassets/CreateHostBuilderPatternTestSite/CreateHostBuilderPatternTestSite.csproj create mode 100644 src/Shared/test/testassets/CreateHostBuilderPatternTestSite/Program.cs create mode 100644 src/Shared/test/testassets/CreateWebHostBuilderInvalidSignature/CreateWebHostBuilderInvalidSignature.csproj create mode 100644 src/Shared/test/testassets/CreateWebHostBuilderInvalidSignature/Program.cs create mode 100644 src/Shared/test/testassets/CreateWebHostBuilderPatternTestSite/CreateWebHostBuilderPatternTestSite.csproj create mode 100644 src/Shared/test/testassets/CreateWebHostBuilderPatternTestSite/Program.cs create mode 100644 src/Shared/test/testassets/MockHostTypes/Host.cs create mode 100644 src/Shared/test/testassets/MockHostTypes/HostBuilder.cs create mode 100644 src/Shared/test/testassets/MockHostTypes/IHost.cs create mode 100644 src/Shared/test/testassets/MockHostTypes/IHostBuilder.cs create mode 100644 src/Shared/test/testassets/MockHostTypes/IWebHost.cs create mode 100644 src/Shared/test/testassets/MockHostTypes/IWebHostBuilder.cs create mode 100644 src/Shared/test/testassets/MockHostTypes/MockHostTypes.csproj create mode 100644 src/Shared/test/testassets/MockHostTypes/ServiceProvider.cs create mode 100644 src/Shared/test/testassets/MockHostTypes/WebHost.cs create mode 100644 src/Shared/test/testassets/MockHostTypes/WebHostBuilder.cs diff --git a/src/Shared/HostFactoryResolver/HostFactoryResolver.cs b/src/Shared/HostFactoryResolver/HostFactoryResolver.cs new file mode 100644 index 000000000000..cb9f81123795 --- /dev/null +++ b/src/Shared/HostFactoryResolver/HostFactoryResolver.cs @@ -0,0 +1,112 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Reflection; + +namespace Microsoft.Extensions.Hosting +{ + internal class HostFactoryResolver + { + public static readonly string BuildWebHost = nameof(BuildWebHost); + public static readonly string CreateWebHostBuilder = nameof(CreateWebHostBuilder); + public static readonly string CreateHostBuilder = nameof(CreateHostBuilder); + + public static Func ResolveWebHostFactory(Assembly assembly) + { + return ResolveFactory(assembly, BuildWebHost); + } + + public static Func ResolveWebHostBuilderFactory(Assembly assembly) + { + return ResolveFactory(assembly, CreateWebHostBuilder); + } + + public static Func ResolveHostBuilderFactory(Assembly assembly) + { + return ResolveFactory(assembly, CreateHostBuilder); + } + + private static Func ResolveFactory(Assembly assembly, string name) + { + var programType = assembly?.EntryPoint?.DeclaringType; + if (programType == null) + { + return null; + } + + var factory = programType.GetTypeInfo().GetDeclaredMethod(name); + if (!IsFactory(factory)) + { + return null; + } + + return args => (T)factory.Invoke(null, new object[] { args }); + } + + // TReturn Factory(string[] args); + private static bool IsFactory(MethodInfo factory) + { + return factory != null + && typeof(TReturn).IsAssignableFrom(factory.ReturnType) + && factory.GetParameters().Length == 1 + && typeof(string[]).Equals(factory.GetParameters()[0].ParameterType); + } + + // Used by EF tooling without any Hosting references. Looses some return type safety checks. + public static Func ResolveServiceProviderFactory(Assembly assembly) + { + // Prefer the older patterns by default for back compat. + var webHostFactory = ResolveWebHostFactory(assembly); + if (webHostFactory != null) + { + return args => + { + var webHost = webHostFactory(args); + return GetServiceProvider(webHost); + }; + } + + var webHostBuilderFactory = ResolveWebHostBuilderFactory(assembly); + if (webHostBuilderFactory != null) + { + return args => + { + var webHostBuilder = webHostBuilderFactory(args); + var webHost = Build(webHostBuilder); + return GetServiceProvider(webHost); + }; + } + + var hostBuilderFactory = ResolveHostBuilderFactory(assembly); + if (hostBuilderFactory != null) + { + return args => + { + var hostBuilder = hostBuilderFactory(args); + var host = Build(hostBuilder); + return GetServiceProvider(host); + }; + } + + return null; + } + + private static object Build(object builder) + { + var buildMethod = builder.GetType().GetMethod("Build"); + return buildMethod?.Invoke(builder, Array.Empty()); + } + + private static IServiceProvider GetServiceProvider(object host) + { + if (host == null) + { + return null; + } + var hostType = host.GetType(); + var servicesProperty = hostType.GetTypeInfo().GetDeclaredProperty("Services"); + return (IServiceProvider)servicesProperty.GetValue(host); + } + } +} diff --git a/src/Shared/test/Shared.Tests/HostFactoryResolverTests.cs b/src/Shared/test/Shared.Tests/HostFactoryResolverTests.cs new file mode 100644 index 000000000000..a26fb7b1332d --- /dev/null +++ b/src/Shared/test/Shared.Tests/HostFactoryResolverTests.cs @@ -0,0 +1,116 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using MockHostTypes; +using System; +using Xunit; + +namespace Microsoft.Extensions.Hosting.Tests +{ + public class HostFactoryResolverTests + { + [Fact] + public void BuildWebHostPattern_CanFindWebHost() + { + var factory = HostFactoryResolver.ResolveWebHostFactory(typeof(BuildWebHostPatternTestSite.Program).Assembly); + + Assert.NotNull(factory); + Assert.IsAssignableFrom(factory(Array.Empty())); + } + + [Fact] + public void BuildWebHostPattern_CanFindServiceProvider() + { + var factory = HostFactoryResolver.ResolveServiceProviderFactory(typeof(BuildWebHostPatternTestSite.Program).Assembly); + + Assert.NotNull(factory); + Assert.IsAssignableFrom(factory(Array.Empty())); + } + + [Fact] + public void BuildWebHostPattern__Invalid_CantFindWebHost() + { + var factory = HostFactoryResolver.ResolveWebHostFactory(typeof(BuildWebHostInvalidSignature.Program).Assembly); + + Assert.Null(factory); + } + + [Fact] + public void BuildWebHostPattern__Invalid_CantFindServiceProvider() + { + var factory = HostFactoryResolver.ResolveServiceProviderFactory(typeof(BuildWebHostInvalidSignature.Program).Assembly); + + Assert.Null(factory); + } + + [Fact] + public void CreateWebHostBuilderPattern_CanFindWebHostBuilder() + { + var factory = HostFactoryResolver.ResolveWebHostBuilderFactory(typeof(CreateWebHostBuilderPatternTestSite.Program).Assembly); + + Assert.NotNull(factory); + Assert.IsAssignableFrom(factory(Array.Empty())); + } + + [Fact] + public void CreateWebHostBuilderPattern_CanFindServiceProvider() + { + var factory = HostFactoryResolver.ResolveServiceProviderFactory(typeof(CreateWebHostBuilderPatternTestSite.Program).Assembly); + + Assert.NotNull(factory); + Assert.IsAssignableFrom(factory(Array.Empty())); + } + + [Fact] + public void CreateWebHostBuilderPattern__Invalid_CantFindWebHostBuilder() + { + var factory = HostFactoryResolver.ResolveWebHostBuilderFactory(typeof(CreateWebHostBuilderInvalidSignature.Program).Assembly); + + Assert.Null(factory); + } + + [Fact] + public void CreateWebHostBuilderPattern__InvalidReturnType_CanFindServiceProvider() + { + var factory = HostFactoryResolver.ResolveServiceProviderFactory(typeof(CreateWebHostBuilderInvalidSignature.Program).Assembly); + + Assert.NotNull(factory); + Assert.Null(factory(Array.Empty())); + + } + + [Fact] + public void CreateHostBuilderPattern_CanFindHostBuilder() + { + var factory = HostFactoryResolver.ResolveHostBuilderFactory(typeof(CreateHostBuilderPatternTestSite.Program).Assembly); + + Assert.NotNull(factory); + Assert.IsAssignableFrom(factory(Array.Empty())); + } + + [Fact] + public void CreateHostBuilderPattern_CanFindServiceProvider() + { + var factory = HostFactoryResolver.ResolveServiceProviderFactory(typeof(CreateHostBuilderPatternTestSite.Program).Assembly); + + Assert.NotNull(factory); + Assert.IsAssignableFrom(factory(Array.Empty())); + } + + [Fact] + public void CreateHostBuilderPattern__Invalid_CantFindHostBuilder() + { + var factory = HostFactoryResolver.ResolveHostBuilderFactory(typeof(CreateHostBuilderInvalidSignature.Program).Assembly); + + Assert.Null(factory); + } + + [Fact] + public void CreateHostBuilderPattern__Invalid_CantFindServiceProvider() + { + var factory = HostFactoryResolver.ResolveServiceProviderFactory(typeof(CreateHostBuilderInvalidSignature.Program).Assembly); + + Assert.Null(factory); + } + } +} diff --git a/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj b/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj index a5950b7fc929..222c88bc679e 100644 --- a/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj +++ b/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj @@ -13,6 +13,16 @@ " /> + + + + + + + + + + diff --git a/src/Shared/test/testassets/BuildWebHostInvalidSignature/BuildWebHostInvalidSignature.csproj b/src/Shared/test/testassets/BuildWebHostInvalidSignature/BuildWebHostInvalidSignature.csproj new file mode 100644 index 000000000000..6368289f65e7 --- /dev/null +++ b/src/Shared/test/testassets/BuildWebHostInvalidSignature/BuildWebHostInvalidSignature.csproj @@ -0,0 +1,12 @@ + + + + netcoreapp3.0;net472 + Exe + + + + + + + diff --git a/src/Shared/test/testassets/BuildWebHostInvalidSignature/Program.cs b/src/Shared/test/testassets/BuildWebHostInvalidSignature/Program.cs new file mode 100644 index 000000000000..ba9e3dab6a9c --- /dev/null +++ b/src/Shared/test/testassets/BuildWebHostInvalidSignature/Program.cs @@ -0,0 +1,17 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using MockHostTypes; + +namespace BuildWebHostInvalidSignature +{ + public class Program + { + static void Main(string[] args) + { + } + + // Missing string[] args + public static IWebHost BuildWebHost() => null; + } +} diff --git a/src/Shared/test/testassets/BuildWebHostPatternTestSite/BuildWebHostPatternTestSite.csproj b/src/Shared/test/testassets/BuildWebHostPatternTestSite/BuildWebHostPatternTestSite.csproj new file mode 100644 index 000000000000..6368289f65e7 --- /dev/null +++ b/src/Shared/test/testassets/BuildWebHostPatternTestSite/BuildWebHostPatternTestSite.csproj @@ -0,0 +1,12 @@ + + + + netcoreapp3.0;net472 + Exe + + + + + + + diff --git a/src/Shared/test/testassets/BuildWebHostPatternTestSite/Program.cs b/src/Shared/test/testassets/BuildWebHostPatternTestSite/Program.cs new file mode 100644 index 000000000000..b1d0655e4dea --- /dev/null +++ b/src/Shared/test/testassets/BuildWebHostPatternTestSite/Program.cs @@ -0,0 +1,16 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using MockHostTypes; + +namespace BuildWebHostPatternTestSite +{ + public class Program + { + static void Main(string[] args) + { + } + + public static IWebHost BuildWebHost(string[] args) => new WebHost(); + } +} diff --git a/src/Shared/test/testassets/CreateHostBuilderInvalidSignature/CreateHostBuilderInvalidSignature.csproj b/src/Shared/test/testassets/CreateHostBuilderInvalidSignature/CreateHostBuilderInvalidSignature.csproj new file mode 100644 index 000000000000..6368289f65e7 --- /dev/null +++ b/src/Shared/test/testassets/CreateHostBuilderInvalidSignature/CreateHostBuilderInvalidSignature.csproj @@ -0,0 +1,12 @@ + + + + netcoreapp3.0;net472 + Exe + + + + + + + diff --git a/src/Shared/test/testassets/CreateHostBuilderInvalidSignature/Program.cs b/src/Shared/test/testassets/CreateHostBuilderInvalidSignature/Program.cs new file mode 100644 index 000000000000..8451301a2007 --- /dev/null +++ b/src/Shared/test/testassets/CreateHostBuilderInvalidSignature/Program.cs @@ -0,0 +1,18 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using MockHostTypes; + +namespace CreateHostBuilderInvalidSignature +{ + public class Program + { + public static void Main(string[] args) + { + var webHost = CreateHostBuilder(null, args).Build(); + } + + // Extra parameter + private static IHostBuilder CreateHostBuilder(object extraParam, string[] args) => null; + } +} diff --git a/src/Shared/test/testassets/CreateHostBuilderPatternTestSite/CreateHostBuilderPatternTestSite.csproj b/src/Shared/test/testassets/CreateHostBuilderPatternTestSite/CreateHostBuilderPatternTestSite.csproj new file mode 100644 index 000000000000..6368289f65e7 --- /dev/null +++ b/src/Shared/test/testassets/CreateHostBuilderPatternTestSite/CreateHostBuilderPatternTestSite.csproj @@ -0,0 +1,12 @@ + + + + netcoreapp3.0;net472 + Exe + + + + + + + diff --git a/src/Shared/test/testassets/CreateHostBuilderPatternTestSite/Program.cs b/src/Shared/test/testassets/CreateHostBuilderPatternTestSite/Program.cs new file mode 100644 index 000000000000..70edf1609766 --- /dev/null +++ b/src/Shared/test/testassets/CreateHostBuilderPatternTestSite/Program.cs @@ -0,0 +1,19 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using MockHostTypes; + +namespace CreateHostBuilderPatternTestSite +{ + public class Program + { + public static void Main(string[] args) + { + var webHost = CreateHostBuilder(args).Build(); + } + + // Do not change the signature of this method. It's used for tests. + private static HostBuilder CreateHostBuilder(string[] args) => + new HostBuilder(); + } +} diff --git a/src/Shared/test/testassets/CreateWebHostBuilderInvalidSignature/CreateWebHostBuilderInvalidSignature.csproj b/src/Shared/test/testassets/CreateWebHostBuilderInvalidSignature/CreateWebHostBuilderInvalidSignature.csproj new file mode 100644 index 000000000000..6368289f65e7 --- /dev/null +++ b/src/Shared/test/testassets/CreateWebHostBuilderInvalidSignature/CreateWebHostBuilderInvalidSignature.csproj @@ -0,0 +1,12 @@ + + + + netcoreapp3.0;net472 + Exe + + + + + + + diff --git a/src/Shared/test/testassets/CreateWebHostBuilderInvalidSignature/Program.cs b/src/Shared/test/testassets/CreateWebHostBuilderInvalidSignature/Program.cs new file mode 100644 index 000000000000..1533acbf5783 --- /dev/null +++ b/src/Shared/test/testassets/CreateWebHostBuilderInvalidSignature/Program.cs @@ -0,0 +1,17 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using MockHostTypes; + +namespace CreateWebHostBuilderInvalidSignature +{ + public class Program + { + static void Main(string[] args) + { + } + + // Wrong return type + public static IWebHost CreateWebHostBuilder(string[] args) => new WebHost(); + } +} diff --git a/src/Shared/test/testassets/CreateWebHostBuilderPatternTestSite/CreateWebHostBuilderPatternTestSite.csproj b/src/Shared/test/testassets/CreateWebHostBuilderPatternTestSite/CreateWebHostBuilderPatternTestSite.csproj new file mode 100644 index 000000000000..6368289f65e7 --- /dev/null +++ b/src/Shared/test/testassets/CreateWebHostBuilderPatternTestSite/CreateWebHostBuilderPatternTestSite.csproj @@ -0,0 +1,12 @@ + + + + netcoreapp3.0;net472 + Exe + + + + + + + diff --git a/src/Shared/test/testassets/CreateWebHostBuilderPatternTestSite/Program.cs b/src/Shared/test/testassets/CreateWebHostBuilderPatternTestSite/Program.cs new file mode 100644 index 000000000000..caab3cb22490 --- /dev/null +++ b/src/Shared/test/testassets/CreateWebHostBuilderPatternTestSite/Program.cs @@ -0,0 +1,19 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using MockHostTypes; + +namespace CreateWebHostBuilderPatternTestSite +{ + public class Program + { + public static void Main(string[] args) + { + var webHost = CreateWebHostBuilder(args).Build(); + } + + // Do not change the signature of this method. It's used for tests. + private static IWebHostBuilder CreateWebHostBuilder(string[] args) => + new WebHostBuilder(); + } +} diff --git a/src/Shared/test/testassets/MockHostTypes/Host.cs b/src/Shared/test/testassets/MockHostTypes/Host.cs new file mode 100644 index 000000000000..412ab63ef3eb --- /dev/null +++ b/src/Shared/test/testassets/MockHostTypes/Host.cs @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace MockHostTypes +{ + public class Host : IHost + { + public IServiceProvider Services { get; } = new ServiceProvider(); + } +} diff --git a/src/Shared/test/testassets/MockHostTypes/HostBuilder.cs b/src/Shared/test/testassets/MockHostTypes/HostBuilder.cs new file mode 100644 index 000000000000..eb62e9a4b131 --- /dev/null +++ b/src/Shared/test/testassets/MockHostTypes/HostBuilder.cs @@ -0,0 +1,10 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace MockHostTypes +{ + public class HostBuilder : IHostBuilder + { + public IHost Build() => new Host(); + } +} diff --git a/src/Shared/test/testassets/MockHostTypes/IHost.cs b/src/Shared/test/testassets/MockHostTypes/IHost.cs new file mode 100644 index 000000000000..27c6dbaf7153 --- /dev/null +++ b/src/Shared/test/testassets/MockHostTypes/IHost.cs @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace MockHostTypes +{ + public interface IHost + { + IServiceProvider Services { get; } + } +} diff --git a/src/Shared/test/testassets/MockHostTypes/IHostBuilder.cs b/src/Shared/test/testassets/MockHostTypes/IHostBuilder.cs new file mode 100644 index 000000000000..2053b5210688 --- /dev/null +++ b/src/Shared/test/testassets/MockHostTypes/IHostBuilder.cs @@ -0,0 +1,10 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace MockHostTypes +{ + public interface IHostBuilder + { + IHost Build(); + } +} diff --git a/src/Shared/test/testassets/MockHostTypes/IWebHost.cs b/src/Shared/test/testassets/MockHostTypes/IWebHost.cs new file mode 100644 index 000000000000..f93bba440c94 --- /dev/null +++ b/src/Shared/test/testassets/MockHostTypes/IWebHost.cs @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace MockHostTypes +{ + public interface IWebHost + { + IServiceProvider Services { get; } + } +} diff --git a/src/Shared/test/testassets/MockHostTypes/IWebHostBuilder.cs b/src/Shared/test/testassets/MockHostTypes/IWebHostBuilder.cs new file mode 100644 index 000000000000..1159ae103ee6 --- /dev/null +++ b/src/Shared/test/testassets/MockHostTypes/IWebHostBuilder.cs @@ -0,0 +1,10 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace MockHostTypes +{ + public interface IWebHostBuilder + { + IWebHost Build(); + } +} diff --git a/src/Shared/test/testassets/MockHostTypes/MockHostTypes.csproj b/src/Shared/test/testassets/MockHostTypes/MockHostTypes.csproj new file mode 100644 index 000000000000..3272f8d93ae7 --- /dev/null +++ b/src/Shared/test/testassets/MockHostTypes/MockHostTypes.csproj @@ -0,0 +1,7 @@ + + + + netcoreapp3.0;net472 + + + diff --git a/src/Shared/test/testassets/MockHostTypes/ServiceProvider.cs b/src/Shared/test/testassets/MockHostTypes/ServiceProvider.cs new file mode 100644 index 000000000000..7b550c9d32d3 --- /dev/null +++ b/src/Shared/test/testassets/MockHostTypes/ServiceProvider.cs @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace MockHostTypes +{ + public class ServiceProvider : IServiceProvider + { + public object GetService(Type serviceType) => null; + } +} diff --git a/src/Shared/test/testassets/MockHostTypes/WebHost.cs b/src/Shared/test/testassets/MockHostTypes/WebHost.cs new file mode 100644 index 000000000000..77d3d58ca4bc --- /dev/null +++ b/src/Shared/test/testassets/MockHostTypes/WebHost.cs @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace MockHostTypes +{ + public class WebHost : IWebHost + { + public IServiceProvider Services { get; } = new ServiceProvider(); + } +} diff --git a/src/Shared/test/testassets/MockHostTypes/WebHostBuilder.cs b/src/Shared/test/testassets/MockHostTypes/WebHostBuilder.cs new file mode 100644 index 000000000000..216fb28d60e4 --- /dev/null +++ b/src/Shared/test/testassets/MockHostTypes/WebHostBuilder.cs @@ -0,0 +1,10 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace MockHostTypes +{ + public class WebHostBuilder : IWebHostBuilder + { + public IWebHost Build() => new WebHost(); + } +} From 33ee3dc379005a1c465b5ff7c8b8f89e4607e56e Mon Sep 17 00:00:00 2001 From: Pranav K Date: Wed, 9 Jan 2019 11:21:29 -0800 Subject: [PATCH 042/183] Refactoring to do KoreBuild \ mondorepo \n\nCommit migrated from https://github.com/dotnet/extensions/commit/3a884e2e1e6ae3f1fc4381fc8fbb428de5cff063 --- src/JSInterop/.editorconfig | 27 --------- src/JSInterop/Directory.Build.props | 7 +++ .../src}/Microsoft.JSInterop.JS.csproj | 2 +- .../src}/package-lock.json | 0 .../src}/package.json | 0 .../src}/src/Microsoft.JSInterop.ts | 0 .../src}/tsconfig.json | 0 src/JSInterop/Microsoft.JSInterop.sln | 58 ------------------- .../src}/DotNetDispatcher.cs | 0 .../src}/DotNetObjectRef.cs | 0 .../src}/ICustomArgSerializer.cs | 0 .../src}/IJSInProcessRuntime.cs | 0 .../src}/IJSRuntime.cs | 0 .../src}/InteropArgSerializerStrategy.cs | 0 .../src}/JSAsyncCallResult.cs | 0 .../src}/JSException.cs | 0 .../src}/JSInProcessRuntimeBase.cs | 0 .../src}/JSInvokableAttribute.cs | 0 .../src}/JSRuntime.cs | 0 .../src}/JSRuntimeBase.cs | 0 .../src}/Json/CamelCase.cs | 0 .../src}/Json/Json.cs | 0 .../src}/Json/SimpleJson/README.txt | 0 .../src}/Json/SimpleJson/SimpleJson.cs | 0 .../src/Microsoft.JSInterop.csproj | 10 ++++ .../src}/Properties/AssemblyInfo.cs | 0 .../src}/TaskGenericsUtil.cs | 0 .../test}/DotNetDispatcherTest.cs | 0 .../test}/DotNetObjectRefTest.cs | 0 .../test}/JSInProcessRuntimeBaseTest.cs | 0 .../test}/JSRuntimeBaseTest.cs | 0 .../test}/JSRuntimeTest.cs | 0 .../test}/JsonUtilTest.cs | 0 .../test/Microsoft.JSInterop.Test.csproj | 11 ++++ .../src}/InternalCalls.cs | 0 .../src/Mono.WebAssembly.Interop.csproj | 14 +++++ .../src}/MonoWebAssemblyJSRuntime.cs | 0 .../src/Microsoft.JSInterop.JS/.gitignore | 1 - .../Microsoft.JSInterop.csproj | 7 --- .../Mono.WebAssembly.Interop.csproj | 11 ---- .../Microsoft.JSInterop.Test.csproj | 20 ------- 41 files changed, 43 insertions(+), 125 deletions(-) delete mode 100644 src/JSInterop/.editorconfig create mode 100644 src/JSInterop/Directory.Build.props rename src/JSInterop/{src/Microsoft.JSInterop.JS => Microsoft.JSInterop.JS/src}/Microsoft.JSInterop.JS.csproj (76%) rename src/JSInterop/{src/Microsoft.JSInterop.JS => Microsoft.JSInterop.JS/src}/package-lock.json (100%) rename src/JSInterop/{src/Microsoft.JSInterop.JS => Microsoft.JSInterop.JS/src}/package.json (100%) rename src/JSInterop/{src/Microsoft.JSInterop.JS => Microsoft.JSInterop.JS/src}/src/Microsoft.JSInterop.ts (100%) rename src/JSInterop/{src/Microsoft.JSInterop.JS => Microsoft.JSInterop.JS/src}/tsconfig.json (100%) delete mode 100644 src/JSInterop/Microsoft.JSInterop.sln rename src/JSInterop/{src/Microsoft.JSInterop => Microsoft.JSInterop/src}/DotNetDispatcher.cs (100%) rename src/JSInterop/{src/Microsoft.JSInterop => Microsoft.JSInterop/src}/DotNetObjectRef.cs (100%) rename src/JSInterop/{src/Microsoft.JSInterop => Microsoft.JSInterop/src}/ICustomArgSerializer.cs (100%) rename src/JSInterop/{src/Microsoft.JSInterop => Microsoft.JSInterop/src}/IJSInProcessRuntime.cs (100%) rename src/JSInterop/{src/Microsoft.JSInterop => Microsoft.JSInterop/src}/IJSRuntime.cs (100%) rename src/JSInterop/{src/Microsoft.JSInterop => Microsoft.JSInterop/src}/InteropArgSerializerStrategy.cs (100%) rename src/JSInterop/{src/Microsoft.JSInterop => Microsoft.JSInterop/src}/JSAsyncCallResult.cs (100%) rename src/JSInterop/{src/Microsoft.JSInterop => Microsoft.JSInterop/src}/JSException.cs (100%) rename src/JSInterop/{src/Microsoft.JSInterop => Microsoft.JSInterop/src}/JSInProcessRuntimeBase.cs (100%) rename src/JSInterop/{src/Microsoft.JSInterop => Microsoft.JSInterop/src}/JSInvokableAttribute.cs (100%) rename src/JSInterop/{src/Microsoft.JSInterop => Microsoft.JSInterop/src}/JSRuntime.cs (100%) rename src/JSInterop/{src/Microsoft.JSInterop => Microsoft.JSInterop/src}/JSRuntimeBase.cs (100%) rename src/JSInterop/{src/Microsoft.JSInterop => Microsoft.JSInterop/src}/Json/CamelCase.cs (100%) rename src/JSInterop/{src/Microsoft.JSInterop => Microsoft.JSInterop/src}/Json/Json.cs (100%) rename src/JSInterop/{src/Microsoft.JSInterop => Microsoft.JSInterop/src}/Json/SimpleJson/README.txt (100%) rename src/JSInterop/{src/Microsoft.JSInterop => Microsoft.JSInterop/src}/Json/SimpleJson/SimpleJson.cs (100%) create mode 100644 src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj rename src/JSInterop/{src/Microsoft.JSInterop => Microsoft.JSInterop/src}/Properties/AssemblyInfo.cs (100%) rename src/JSInterop/{src/Microsoft.JSInterop => Microsoft.JSInterop/src}/TaskGenericsUtil.cs (100%) rename src/JSInterop/{test/Microsoft.JSInterop.Test => Microsoft.JSInterop/test}/DotNetDispatcherTest.cs (100%) rename src/JSInterop/{test/Microsoft.JSInterop.Test => Microsoft.JSInterop/test}/DotNetObjectRefTest.cs (100%) rename src/JSInterop/{test/Microsoft.JSInterop.Test => Microsoft.JSInterop/test}/JSInProcessRuntimeBaseTest.cs (100%) rename src/JSInterop/{test/Microsoft.JSInterop.Test => Microsoft.JSInterop/test}/JSRuntimeBaseTest.cs (100%) rename src/JSInterop/{test/Microsoft.JSInterop.Test => Microsoft.JSInterop/test}/JSRuntimeTest.cs (100%) rename src/JSInterop/{test/Microsoft.JSInterop.Test => Microsoft.JSInterop/test}/JsonUtilTest.cs (100%) create mode 100644 src/JSInterop/Microsoft.JSInterop/test/Microsoft.JSInterop.Test.csproj rename src/JSInterop/{src/Mono.WebAssembly.Interop => Mono.WebAssembly.Interop/src}/InternalCalls.cs (100%) create mode 100644 src/JSInterop/Mono.WebAssembly.Interop/src/Mono.WebAssembly.Interop.csproj rename src/JSInterop/{src/Mono.WebAssembly.Interop => Mono.WebAssembly.Interop/src}/MonoWebAssemblyJSRuntime.cs (100%) delete mode 100644 src/JSInterop/src/Microsoft.JSInterop.JS/.gitignore delete mode 100644 src/JSInterop/src/Microsoft.JSInterop/Microsoft.JSInterop.csproj delete mode 100644 src/JSInterop/src/Mono.WebAssembly.Interop/Mono.WebAssembly.Interop.csproj delete mode 100644 src/JSInterop/test/Microsoft.JSInterop.Test/Microsoft.JSInterop.Test.csproj diff --git a/src/JSInterop/.editorconfig b/src/JSInterop/.editorconfig deleted file mode 100644 index 0d238f8e41d1..000000000000 --- a/src/JSInterop/.editorconfig +++ /dev/null @@ -1,27 +0,0 @@ -# All Files -[*] -charset = utf-8 -end_of_line = crlf -indent_style = space -indent_size = 4 -insert_final_newline = false -trim_trailing_whitespace = true - -# Solution Files -[*.sln] -indent_style = tab - -# Markdown Files -[*.md] -trim_trailing_whitespace = false - -# Web Files -[*.{htm,html,js,ts,css,scss,less}] -insert_final_newline = true -indent_size = 2 - -[*.{yml,json}] -indent_size = 2 - -[*.{xml,csproj,config,*proj,targets,props}] -indent_size = 2 diff --git a/src/JSInterop/Directory.Build.props b/src/JSInterop/Directory.Build.props new file mode 100644 index 000000000000..f25c1d90ce80 --- /dev/null +++ b/src/JSInterop/Directory.Build.props @@ -0,0 +1,7 @@ + + + + + true + + diff --git a/src/JSInterop/src/Microsoft.JSInterop.JS/Microsoft.JSInterop.JS.csproj b/src/JSInterop/Microsoft.JSInterop.JS/src/Microsoft.JSInterop.JS.csproj similarity index 76% rename from src/JSInterop/src/Microsoft.JSInterop.JS/Microsoft.JSInterop.JS.csproj rename to src/JSInterop/Microsoft.JSInterop.JS/src/Microsoft.JSInterop.JS.csproj index 4a6ba93ebbfc..7f806d0afa3b 100644 --- a/src/JSInterop/src/Microsoft.JSInterop.JS/Microsoft.JSInterop.JS.csproj +++ b/src/JSInterop/Microsoft.JSInterop.JS/src/Microsoft.JSInterop.JS.csproj @@ -7,7 +7,7 @@ - + diff --git a/src/JSInterop/src/Microsoft.JSInterop.JS/package-lock.json b/src/JSInterop/Microsoft.JSInterop.JS/src/package-lock.json similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop.JS/package-lock.json rename to src/JSInterop/Microsoft.JSInterop.JS/src/package-lock.json diff --git a/src/JSInterop/src/Microsoft.JSInterop.JS/package.json b/src/JSInterop/Microsoft.JSInterop.JS/src/package.json similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop.JS/package.json rename to src/JSInterop/Microsoft.JSInterop.JS/src/package.json diff --git a/src/JSInterop/src/Microsoft.JSInterop.JS/src/Microsoft.JSInterop.ts b/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop.JS/src/Microsoft.JSInterop.ts rename to src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts diff --git a/src/JSInterop/src/Microsoft.JSInterop.JS/tsconfig.json b/src/JSInterop/Microsoft.JSInterop.JS/src/tsconfig.json similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop.JS/tsconfig.json rename to src/JSInterop/Microsoft.JSInterop.JS/src/tsconfig.json diff --git a/src/JSInterop/Microsoft.JSInterop.sln b/src/JSInterop/Microsoft.JSInterop.sln deleted file mode 100644 index d646a6b0681c..000000000000 --- a/src/JSInterop/Microsoft.JSInterop.sln +++ /dev/null @@ -1,58 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27703.2042 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{1290437E-A890-419E-A317-D0F7FEE185A5}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{B98D4F51-88FB-471C-B56F-752E8EE502E7}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.JSInterop", "src\Microsoft.JSInterop\Microsoft.JSInterop.csproj", "{CB4CD4A6-9BAA-46D1-944F-CE56DEC2663C}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.JSInterop.Test", "test\Microsoft.JSInterop.Test\Microsoft.JSInterop.Test.csproj", "{7FF8B199-52C0-4DFE-A73B-0C9E18220C0E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.JSInterop.JS", "src\Microsoft.JSInterop.JS\Microsoft.JSInterop.JS.csproj", "{60BA5AAD-264A-437E-8319-577841C66CC6}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{BE4CBB33-5C40-4A07-B6FC-1D7C3AE13024}" - ProjectSection(SolutionItems) = preProject - README.md = README.md - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mono.WebAssembly.Interop", "src\Mono.WebAssembly.Interop\Mono.WebAssembly.Interop.csproj", "{10145E99-1B2D-40C5-9595-582BDAF3E024}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {CB4CD4A6-9BAA-46D1-944F-CE56DEC2663C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CB4CD4A6-9BAA-46D1-944F-CE56DEC2663C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CB4CD4A6-9BAA-46D1-944F-CE56DEC2663C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CB4CD4A6-9BAA-46D1-944F-CE56DEC2663C}.Release|Any CPU.Build.0 = Release|Any CPU - {7FF8B199-52C0-4DFE-A73B-0C9E18220C0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7FF8B199-52C0-4DFE-A73B-0C9E18220C0E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7FF8B199-52C0-4DFE-A73B-0C9E18220C0E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7FF8B199-52C0-4DFE-A73B-0C9E18220C0E}.Release|Any CPU.Build.0 = Release|Any CPU - {60BA5AAD-264A-437E-8319-577841C66CC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {60BA5AAD-264A-437E-8319-577841C66CC6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {60BA5AAD-264A-437E-8319-577841C66CC6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {60BA5AAD-264A-437E-8319-577841C66CC6}.Release|Any CPU.Build.0 = Release|Any CPU - {10145E99-1B2D-40C5-9595-582BDAF3E024}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {10145E99-1B2D-40C5-9595-582BDAF3E024}.Debug|Any CPU.Build.0 = Debug|Any CPU - {10145E99-1B2D-40C5-9595-582BDAF3E024}.Release|Any CPU.ActiveCfg = Release|Any CPU - {10145E99-1B2D-40C5-9595-582BDAF3E024}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {CB4CD4A6-9BAA-46D1-944F-CE56DEC2663C} = {1290437E-A890-419E-A317-D0F7FEE185A5} - {7FF8B199-52C0-4DFE-A73B-0C9E18220C0E} = {B98D4F51-88FB-471C-B56F-752E8EE502E7} - {60BA5AAD-264A-437E-8319-577841C66CC6} = {1290437E-A890-419E-A317-D0F7FEE185A5} - {10145E99-1B2D-40C5-9595-582BDAF3E024} = {1290437E-A890-419E-A317-D0F7FEE185A5} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {7E07ABF2-427A-43FA-A6A4-82B21B96ACAF} - EndGlobalSection -EndGlobal diff --git a/src/JSInterop/src/Microsoft.JSInterop/DotNetDispatcher.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop/DotNetDispatcher.cs rename to src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs diff --git a/src/JSInterop/src/Microsoft.JSInterop/DotNetObjectRef.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRef.cs similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop/DotNetObjectRef.cs rename to src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRef.cs diff --git a/src/JSInterop/src/Microsoft.JSInterop/ICustomArgSerializer.cs b/src/JSInterop/Microsoft.JSInterop/src/ICustomArgSerializer.cs similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop/ICustomArgSerializer.cs rename to src/JSInterop/Microsoft.JSInterop/src/ICustomArgSerializer.cs diff --git a/src/JSInterop/src/Microsoft.JSInterop/IJSInProcessRuntime.cs b/src/JSInterop/Microsoft.JSInterop/src/IJSInProcessRuntime.cs similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop/IJSInProcessRuntime.cs rename to src/JSInterop/Microsoft.JSInterop/src/IJSInProcessRuntime.cs diff --git a/src/JSInterop/src/Microsoft.JSInterop/IJSRuntime.cs b/src/JSInterop/Microsoft.JSInterop/src/IJSRuntime.cs similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop/IJSRuntime.cs rename to src/JSInterop/Microsoft.JSInterop/src/IJSRuntime.cs diff --git a/src/JSInterop/src/Microsoft.JSInterop/InteropArgSerializerStrategy.cs b/src/JSInterop/Microsoft.JSInterop/src/InteropArgSerializerStrategy.cs similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop/InteropArgSerializerStrategy.cs rename to src/JSInterop/Microsoft.JSInterop/src/InteropArgSerializerStrategy.cs diff --git a/src/JSInterop/src/Microsoft.JSInterop/JSAsyncCallResult.cs b/src/JSInterop/Microsoft.JSInterop/src/JSAsyncCallResult.cs similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop/JSAsyncCallResult.cs rename to src/JSInterop/Microsoft.JSInterop/src/JSAsyncCallResult.cs diff --git a/src/JSInterop/src/Microsoft.JSInterop/JSException.cs b/src/JSInterop/Microsoft.JSInterop/src/JSException.cs similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop/JSException.cs rename to src/JSInterop/Microsoft.JSInterop/src/JSException.cs diff --git a/src/JSInterop/src/Microsoft.JSInterop/JSInProcessRuntimeBase.cs b/src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntimeBase.cs similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop/JSInProcessRuntimeBase.cs rename to src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntimeBase.cs diff --git a/src/JSInterop/src/Microsoft.JSInterop/JSInvokableAttribute.cs b/src/JSInterop/Microsoft.JSInterop/src/JSInvokableAttribute.cs similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop/JSInvokableAttribute.cs rename to src/JSInterop/Microsoft.JSInterop/src/JSInvokableAttribute.cs diff --git a/src/JSInterop/src/Microsoft.JSInterop/JSRuntime.cs b/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop/JSRuntime.cs rename to src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs diff --git a/src/JSInterop/src/Microsoft.JSInterop/JSRuntimeBase.cs b/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop/JSRuntimeBase.cs rename to src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs diff --git a/src/JSInterop/src/Microsoft.JSInterop/Json/CamelCase.cs b/src/JSInterop/Microsoft.JSInterop/src/Json/CamelCase.cs similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop/Json/CamelCase.cs rename to src/JSInterop/Microsoft.JSInterop/src/Json/CamelCase.cs diff --git a/src/JSInterop/src/Microsoft.JSInterop/Json/Json.cs b/src/JSInterop/Microsoft.JSInterop/src/Json/Json.cs similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop/Json/Json.cs rename to src/JSInterop/Microsoft.JSInterop/src/Json/Json.cs diff --git a/src/JSInterop/src/Microsoft.JSInterop/Json/SimpleJson/README.txt b/src/JSInterop/Microsoft.JSInterop/src/Json/SimpleJson/README.txt similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop/Json/SimpleJson/README.txt rename to src/JSInterop/Microsoft.JSInterop/src/Json/SimpleJson/README.txt diff --git a/src/JSInterop/src/Microsoft.JSInterop/Json/SimpleJson/SimpleJson.cs b/src/JSInterop/Microsoft.JSInterop/src/Json/SimpleJson/SimpleJson.cs similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop/Json/SimpleJson/SimpleJson.cs rename to src/JSInterop/Microsoft.JSInterop/src/Json/SimpleJson/SimpleJson.cs diff --git a/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj b/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj new file mode 100644 index 000000000000..bdfea26ad505 --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj @@ -0,0 +1,10 @@ + + + + netstandard2.0 + Abstractions and features for interop between .NET and JavaScript code. + javascript;interop + true + + + diff --git a/src/JSInterop/src/Microsoft.JSInterop/Properties/AssemblyInfo.cs b/src/JSInterop/Microsoft.JSInterop/src/Properties/AssemblyInfo.cs similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop/Properties/AssemblyInfo.cs rename to src/JSInterop/Microsoft.JSInterop/src/Properties/AssemblyInfo.cs diff --git a/src/JSInterop/src/Microsoft.JSInterop/TaskGenericsUtil.cs b/src/JSInterop/Microsoft.JSInterop/src/TaskGenericsUtil.cs similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop/TaskGenericsUtil.cs rename to src/JSInterop/Microsoft.JSInterop/src/TaskGenericsUtil.cs diff --git a/src/JSInterop/test/Microsoft.JSInterop.Test/DotNetDispatcherTest.cs b/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs similarity index 100% rename from src/JSInterop/test/Microsoft.JSInterop.Test/DotNetDispatcherTest.cs rename to src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs diff --git a/src/JSInterop/test/Microsoft.JSInterop.Test/DotNetObjectRefTest.cs b/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectRefTest.cs similarity index 100% rename from src/JSInterop/test/Microsoft.JSInterop.Test/DotNetObjectRefTest.cs rename to src/JSInterop/Microsoft.JSInterop/test/DotNetObjectRefTest.cs diff --git a/src/JSInterop/test/Microsoft.JSInterop.Test/JSInProcessRuntimeBaseTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeBaseTest.cs similarity index 100% rename from src/JSInterop/test/Microsoft.JSInterop.Test/JSInProcessRuntimeBaseTest.cs rename to src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeBaseTest.cs diff --git a/src/JSInterop/test/Microsoft.JSInterop.Test/JSRuntimeBaseTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs similarity index 100% rename from src/JSInterop/test/Microsoft.JSInterop.Test/JSRuntimeBaseTest.cs rename to src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs diff --git a/src/JSInterop/test/Microsoft.JSInterop.Test/JSRuntimeTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeTest.cs similarity index 100% rename from src/JSInterop/test/Microsoft.JSInterop.Test/JSRuntimeTest.cs rename to src/JSInterop/Microsoft.JSInterop/test/JSRuntimeTest.cs diff --git a/src/JSInterop/test/Microsoft.JSInterop.Test/JsonUtilTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JsonUtilTest.cs similarity index 100% rename from src/JSInterop/test/Microsoft.JSInterop.Test/JsonUtilTest.cs rename to src/JSInterop/Microsoft.JSInterop/test/JsonUtilTest.cs diff --git a/src/JSInterop/Microsoft.JSInterop/test/Microsoft.JSInterop.Test.csproj b/src/JSInterop/Microsoft.JSInterop/test/Microsoft.JSInterop.Test.csproj new file mode 100644 index 000000000000..5e88f44a3a4d --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop/test/Microsoft.JSInterop.Test.csproj @@ -0,0 +1,11 @@ + + + + netcoreapp3.0 + + + + + + + diff --git a/src/JSInterop/src/Mono.WebAssembly.Interop/InternalCalls.cs b/src/JSInterop/Mono.WebAssembly.Interop/src/InternalCalls.cs similarity index 100% rename from src/JSInterop/src/Mono.WebAssembly.Interop/InternalCalls.cs rename to src/JSInterop/Mono.WebAssembly.Interop/src/InternalCalls.cs diff --git a/src/JSInterop/Mono.WebAssembly.Interop/src/Mono.WebAssembly.Interop.csproj b/src/JSInterop/Mono.WebAssembly.Interop/src/Mono.WebAssembly.Interop.csproj new file mode 100644 index 000000000000..75c8272e95b7 --- /dev/null +++ b/src/JSInterop/Mono.WebAssembly.Interop/src/Mono.WebAssembly.Interop.csproj @@ -0,0 +1,14 @@ + + + + netstandard2.0 + + false + false + + + + + + + diff --git a/src/JSInterop/src/Mono.WebAssembly.Interop/MonoWebAssemblyJSRuntime.cs b/src/JSInterop/Mono.WebAssembly.Interop/src/MonoWebAssemblyJSRuntime.cs similarity index 100% rename from src/JSInterop/src/Mono.WebAssembly.Interop/MonoWebAssemblyJSRuntime.cs rename to src/JSInterop/Mono.WebAssembly.Interop/src/MonoWebAssemblyJSRuntime.cs diff --git a/src/JSInterop/src/Microsoft.JSInterop.JS/.gitignore b/src/JSInterop/src/Microsoft.JSInterop.JS/.gitignore deleted file mode 100644 index 849ddff3b7ec..000000000000 --- a/src/JSInterop/src/Microsoft.JSInterop.JS/.gitignore +++ /dev/null @@ -1 +0,0 @@ -dist/ diff --git a/src/JSInterop/src/Microsoft.JSInterop/Microsoft.JSInterop.csproj b/src/JSInterop/src/Microsoft.JSInterop/Microsoft.JSInterop.csproj deleted file mode 100644 index 9f5c4f4abb61..000000000000 --- a/src/JSInterop/src/Microsoft.JSInterop/Microsoft.JSInterop.csproj +++ /dev/null @@ -1,7 +0,0 @@ - - - - netstandard2.0 - - - diff --git a/src/JSInterop/src/Mono.WebAssembly.Interop/Mono.WebAssembly.Interop.csproj b/src/JSInterop/src/Mono.WebAssembly.Interop/Mono.WebAssembly.Interop.csproj deleted file mode 100644 index 81f1173b5585..000000000000 --- a/src/JSInterop/src/Mono.WebAssembly.Interop/Mono.WebAssembly.Interop.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - - netstandard2.0 - - - - - - - diff --git a/src/JSInterop/test/Microsoft.JSInterop.Test/Microsoft.JSInterop.Test.csproj b/src/JSInterop/test/Microsoft.JSInterop.Test/Microsoft.JSInterop.Test.csproj deleted file mode 100644 index 893a11d33a81..000000000000 --- a/src/JSInterop/test/Microsoft.JSInterop.Test/Microsoft.JSInterop.Test.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - - netcoreapp2.1 - false - 7.3 - - - - - - - - - - - - - - From 66773c57bcbcccdd63c993105b57de978eebeb39 Mon Sep 17 00:00:00 2001 From: Ryan Brandenburg Date: Tue, 29 Jan 2019 13:26:53 -0800 Subject: [PATCH 043/183] Use Arcade (dotnet/extensions#586) Use arcade \n\nCommit migrated from https://github.com/dotnet/extensions/commit/f0458995c625dba132a74733e041b97c0e10e025 --- .../Directory.Build.props | 2 +- ...Extensions.Configuration.KeyPerFile.csproj | 1 + src/FileProviders/Directory.Build.props | 2 +- ...t.Extensions.FileProviders.Embedded.csproj | 11 +++++---- .../Embedded/src/Properties/AssemblyInfo.cs | 4 ---- ...iders.Embedded.Manifest.Task.Tests.csproj} | 0 ...agnostics.HealthChecks.Abstractions.csproj | 1 + ...Extensions.Diagnostics.HealthChecks.csproj | 5 ++++ .../src/Properties/AssemblyInfo.cs | 3 --- .../src/Microsoft.JSInterop.csproj | 4 ++++ .../src/Properties/AssemblyInfo.cs | 3 --- .../test/DotNetDispatcherTest.cs | 17 +++++++------- .../test/DotNetObjectRefTest.cs | 2 +- .../test/JSInProcessRuntimeBaseTest.cs | 4 ++-- .../test/JSRuntimeBaseTest.cs | 4 ++-- .../Microsoft.JSInterop/test/JSRuntimeTest.cs | 2 +- .../Microsoft.JSInterop/test/JsonUtilTest.cs | 12 +++++----- .../test/Microsoft.JSInterop.Test.csproj | 11 --------- .../test/Microsoft.JSInterop.Tests.csproj | 15 ++++++++++++ .../test/xunit.runner.json | 3 +++ ...xtensions.Localization.Abstractions.csproj | 1 + .../Microsoft.Extensions.Localization.csproj | 5 ++++ .../src/Properties/AssemblyInfo.cs | 6 ----- .../Microsoft.Extensions.ObjectPool.csproj | 1 + .../ActivatorUtilities/sharedsources.props | 4 ++++ .../BenchmarkRunner/Directory.Build.props | 8 ------- ...Extensions.CommandLineUtils.Sources.shproj | 13 ----------- .../src/Microsoft.AspNetCore.Testing.csproj | 5 ++++ src/Testing/src/Properties/AssemblyInfo.cs | 6 ----- src/Testing/src/TestPathUtilities.cs | 7 +++++- .../src/xunit/ConditionalFactDiscoverer.cs | 4 ++-- .../src/xunit/ConditionalTheoryAttribute.cs | 2 +- .../src/xunit/ConditionalTheoryDiscoverer.cs | 23 +++++++++++++++++-- src/Testing/src/xunit/SkippedTestCase.cs | 14 +++++++---- .../Microsoft.AspNetCore.Testing.Tests.csproj | 1 - src/Testing/test/TestPathUtilitiesTest.cs | 2 +- .../Microsoft.Extensions.WebEncoders.csproj | 1 + 37 files changed, 117 insertions(+), 92 deletions(-) delete mode 100644 src/FileProviders/Embedded/src/Properties/AssemblyInfo.cs rename src/FileProviders/Manifest.MSBuildTask/test/{Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Test.csproj => Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Tests.csproj} (100%) delete mode 100644 src/HealthChecks/HealthChecks/src/Properties/AssemblyInfo.cs delete mode 100644 src/JSInterop/Microsoft.JSInterop/src/Properties/AssemblyInfo.cs delete mode 100644 src/JSInterop/Microsoft.JSInterop/test/Microsoft.JSInterop.Test.csproj create mode 100644 src/JSInterop/Microsoft.JSInterop/test/Microsoft.JSInterop.Tests.csproj create mode 100644 src/JSInterop/Microsoft.JSInterop/test/xunit.runner.json delete mode 100644 src/Localization/Localization/src/Properties/AssemblyInfo.cs delete mode 100644 src/Shared/BenchmarkRunner/Directory.Build.props delete mode 100644 src/Shared/CommandLineUtils/Microsoft.Extensions.CommandLineUtils.Sources.shproj delete mode 100644 src/Testing/src/Properties/AssemblyInfo.cs diff --git a/src/Configuration.KeyPerFile/Directory.Build.props b/src/Configuration.KeyPerFile/Directory.Build.props index 2082380096b1..fe35a9faec6a 100644 --- a/src/Configuration.KeyPerFile/Directory.Build.props +++ b/src/Configuration.KeyPerFile/Directory.Build.props @@ -2,7 +2,7 @@ - true + true configuration diff --git a/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj b/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj index 4eb19f329375..82784d0eed89 100644 --- a/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj +++ b/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj @@ -4,6 +4,7 @@ Configuration provider that uses files in a directory for Microsoft.Extensions.Configuration. netstandard2.0 false + true diff --git a/src/FileProviders/Directory.Build.props b/src/FileProviders/Directory.Build.props index bf4410dcb77e..709c47ddbd7a 100644 --- a/src/FileProviders/Directory.Build.props +++ b/src/FileProviders/Directory.Build.props @@ -2,7 +2,7 @@ - true + true files;filesystem diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj index e8dd5d85dd08..90cb2e4cc336 100644 --- a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj @@ -5,8 +5,13 @@ File provider for files in embedded resources for Microsoft.Extensions.FileProviders. netstandard2.0 $(MSBuildProjectName).nuspec + true + + + + @@ -18,7 +23,6 @@ - id=$(PackageId); @@ -40,10 +44,9 @@ OutputDocumentation=@(DocumentationProjectOutputGroupOutput); - TaskAssemblyNetStandard=..\..\Manifest.MSBuildTask\src\bin\$(Configuration)\netstandard2.0\$(AssemblyName).Manifest.Task.dll; - TaskSymbolNetStandard=..\..\Manifest.MSBuildTask\src\bin\$(Configuration)\netstandard2.0\$(AssemblyName).Manifest.Task.pdb; + TaskAssemblyNetStandard=$(ArtifactsDir)bin\$(AssemblyName).Manifest.Task\$(Configuration)\netstandard2.0\$(AssemblyName).Manifest.Task.dll; + TaskSymbolNetStandard=$(ArtifactsDir)bin\$(AssemblyName).Manifest.Task\$(Configuration)\netstandard2.0\$(AssemblyName).Manifest.Task.pdb; - diff --git a/src/FileProviders/Embedded/src/Properties/AssemblyInfo.cs b/src/FileProviders/Embedded/src/Properties/AssemblyInfo.cs deleted file mode 100644 index 610a7fa706f1..000000000000 --- a/src/FileProviders/Embedded/src/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,4 +0,0 @@ - -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Microsoft.Extensions.FileProviders.Embedded.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] \ No newline at end of file diff --git a/src/FileProviders/Manifest.MSBuildTask/test/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Test.csproj b/src/FileProviders/Manifest.MSBuildTask/test/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Tests.csproj similarity index 100% rename from src/FileProviders/Manifest.MSBuildTask/test/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Test.csproj rename to src/FileProviders/Manifest.MSBuildTask/test/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Tests.csproj diff --git a/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj b/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj index b95d66f7b38a..69298be027b7 100644 --- a/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj +++ b/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj @@ -11,6 +11,7 @@ Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck $(NoWarn);CS1591 true diagnostics;healthchecks + true diff --git a/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj b/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj index d0b1c97ef05a..b49f194d0c0a 100644 --- a/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj +++ b/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj @@ -10,8 +10,13 @@ Microsoft.Extensions.Diagnostics.HealthChecks.IHealthChecksBuilder $(NoWarn);CS1591 true diagnostics;healthchecks + true + + + + diff --git a/src/HealthChecks/HealthChecks/src/Properties/AssemblyInfo.cs b/src/HealthChecks/HealthChecks/src/Properties/AssemblyInfo.cs deleted file mode 100644 index 13e969bfad29..000000000000 --- a/src/HealthChecks/HealthChecks/src/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Microsoft.Extensions.Diagnostics.HealthChecks.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] \ No newline at end of file diff --git a/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj b/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj index bdfea26ad505..d91e8c2524e7 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj +++ b/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj @@ -7,4 +7,8 @@ true + + + + diff --git a/src/JSInterop/Microsoft.JSInterop/src/Properties/AssemblyInfo.cs b/src/JSInterop/Microsoft.JSInterop/src/Properties/AssemblyInfo.cs deleted file mode 100644 index d65c89dc7f12..000000000000 --- a/src/JSInterop/Microsoft.JSInterop/src/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Microsoft.JSInterop.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] diff --git a/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs b/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs index eec537f987d2..93ef9a249850 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; using Xunit; -namespace Microsoft.JSInterop.Test +namespace Microsoft.JSInterop.Tests { public class DotNetDispatcherTest { @@ -73,7 +73,7 @@ public void CannotInvokeUnsuitableMethods(string methodIdentifier) Assert.Equal($"The assembly '{thisAssemblyName}' does not contain a public method with [JSInvokableAttribute(\"{methodIdentifier}\")].", ex.Message); } - [Fact] + [Fact(Skip = "https://github.com/aspnet/AspNetCore-Internal/issues/1733")] public Task CanInvokeStaticVoidMethod() => WithJSRuntime(jsRuntime => { // Arrange/Act @@ -109,7 +109,7 @@ public Task CanInvokeStaticNonVoidMethodWithoutCustomIdentifier() => WithJSRunti Assert.Equal(456, result.IntVal); }); - [Fact] + [Fact(Skip = "https://github.com/aspnet/AspNetCore-Internal/issues/1733")] public Task CanInvokeStaticWithParams() => WithJSRuntime(jsRuntime => { // Arrange: Track a .NET object to use as an arg @@ -140,7 +140,7 @@ public Task CanInvokeStaticWithParams() => WithJSRuntime(jsRuntime => Assert.Equal(1299, resultDto2.IntVal); }); - [Fact] + [Fact(Skip = "https://github.com/aspnet/AspNetCore-Internal/issues/1733")] public Task CanInvokeInstanceVoidMethod() => WithJSRuntime(jsRuntime => { // Arrange: Track some instance @@ -155,7 +155,7 @@ public Task CanInvokeInstanceVoidMethod() => WithJSRuntime(jsRuntime => Assert.True(targetInstance.DidInvokeMyInvocableInstanceVoid); }); - [Fact] + [Fact(Skip = "https://github.com/aspnet/AspNetCore-Internal/issues/1733")] public Task CanInvokeBaseInstanceVoidMethod() => WithJSRuntime(jsRuntime => { // Arrange: Track some instance @@ -206,7 +206,7 @@ public Task CannotUseDotNetObjectRefAfterReleaseDotNetObject() => WithJSRuntime( Assert.StartsWith("There is no tracked object with id '1'.", ex.Message); }); - [Fact] + [Fact(Skip = "https://github.com/aspnet/AspNetCore-Internal/issues/1733")] public Task CanInvokeInstanceMethodWithParams() => WithJSRuntime(jsRuntime => { // Arrange: Track some instance plus another object we'll pass as a param @@ -242,7 +242,7 @@ public void CannotInvokeWithIncorrectNumberOfParams() Assert.Equal("In call to 'InvocableStaticWithParams', expected 3 parameters but received 4.", ex.Message); } - [Fact] + [Fact(Skip = "https://github.com/aspnet/AspNetCore-Internal/issues/1733")] public Task CanInvokeAsyncMethod() => WithJSRuntime(async jsRuntime => { // Arrange: Track some instance plus another object we'll pass as a param @@ -320,7 +320,8 @@ public class SomePublicType protected static void StaticMethodWithoutAttribute() { } protected static void InstanceMethodWithoutAttribute() { } - [JSInvokable("InvocableStaticVoid")] public static void MyInvocableVoid() + [JSInvokable("InvocableStaticVoid")] + public static void MyInvocableVoid() { DidInvokeMyInvocableStaticVoid = true; } diff --git a/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectRefTest.cs b/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectRefTest.cs index 969dcae79d91..1bdec6d46521 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectRefTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectRefTest.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; using Xunit; -namespace Microsoft.JSInterop.Test +namespace Microsoft.JSInterop.Tests { public class DotNetObjectRefTest { diff --git a/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeBaseTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeBaseTest.cs index d2e71f6eb217..36474fe40717 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeBaseTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeBaseTest.cs @@ -6,7 +6,7 @@ using System.Linq; using Xunit; -namespace Microsoft.JSInterop.Test +namespace Microsoft.JSInterop.Tests { public class JSInProcessRuntimeBaseTest { @@ -21,7 +21,7 @@ public void DispatchesSyncCallsAndDeserializesResults() }; // Act - var syncResult = runtime.Invoke("test identifier 1", "arg1", 123, true ); + var syncResult = runtime.Invoke("test identifier 1", "arg1", 123, true); var call = runtime.InvokeCalls.Single(); // Assert diff --git a/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs index 9193d6deb8d1..ab048e812fe7 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs @@ -7,7 +7,7 @@ using System.Linq; using Xunit; -namespace Microsoft.JSInterop.Test +namespace Microsoft.JSInterop.Tests { public class JSRuntimeBaseTest { @@ -18,7 +18,7 @@ public void DispatchesAsyncCallsWithDistinctAsyncHandles() var runtime = new TestJSRuntime(); // Act - runtime.InvokeAsync("test identifier 1", "arg1", 123, true ); + runtime.InvokeAsync("test identifier 1", "arg1", 123, true); runtime.InvokeAsync("test identifier 2", "some other arg"); // Assert diff --git a/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeTest.cs index d5fed45ea4ff..b8a1c363dc99 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeTest.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; using Xunit; -namespace Microsoft.JSInterop.Test +namespace Microsoft.JSInterop.Tests { public class JSRuntimeTest { diff --git a/src/JSInterop/Microsoft.JSInterop/test/JsonUtilTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JsonUtilTest.cs index 1be98b681e19..2b239faab9c2 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/JsonUtilTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/JsonUtilTest.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; using Xunit; -namespace Microsoft.JSInterop.Test +namespace Microsoft.JSInterop.Tests { public class JsonUtilTest { @@ -124,10 +124,10 @@ public void CanSerializeStructToJson() BoolProperty = true, NullableIntProperty = 1 }; - + // Act var result = Json.Serialize(commandResult); - + // Assert Assert.Equal("{\"stringProperty\":\"Test\",\"boolProperty\":true,\"nullableIntProperty\":1}", result); } @@ -222,12 +222,12 @@ public void NonEmptyConstructorThrowsUsefulException() // Act var exception = Assert.Throws(() => { - Json.Deserialize(json); + Json.Deserialize(json); }); // Assert Assert.Equal( - $"Cannot deserialize JSON into type '{type.FullName}' because it does not have a public parameterless constructor.", + $"Cannot deserialize JSON into type '{type.FullName}' because it does not have a public parameterless constructor.", exception.Message); } @@ -277,7 +277,7 @@ public void MemberNameToCamelCase_Invalid(string input) class NonEmptyConstructorPoco { - public NonEmptyConstructorPoco(int parameter) {} + public NonEmptyConstructorPoco(int parameter) { } public int Property { get; set; } } diff --git a/src/JSInterop/Microsoft.JSInterop/test/Microsoft.JSInterop.Test.csproj b/src/JSInterop/Microsoft.JSInterop/test/Microsoft.JSInterop.Test.csproj deleted file mode 100644 index 5e88f44a3a4d..000000000000 --- a/src/JSInterop/Microsoft.JSInterop/test/Microsoft.JSInterop.Test.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - - netcoreapp3.0 - - - - - - - diff --git a/src/JSInterop/Microsoft.JSInterop/test/Microsoft.JSInterop.Tests.csproj b/src/JSInterop/Microsoft.JSInterop/test/Microsoft.JSInterop.Tests.csproj new file mode 100644 index 000000000000..a39b08218068 --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop/test/Microsoft.JSInterop.Tests.csproj @@ -0,0 +1,15 @@ + + + + netcoreapp3.0 + + + + + + + + + + + diff --git a/src/JSInterop/Microsoft.JSInterop/test/xunit.runner.json b/src/JSInterop/Microsoft.JSInterop/test/xunit.runner.json new file mode 100644 index 000000000000..0f2ad9f7698f --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop/test/xunit.runner.json @@ -0,0 +1,3 @@ +{ + "shadowCopy": true +} diff --git a/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj b/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj index 8508eb071aad..636072724fce 100644 --- a/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj +++ b/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj @@ -10,6 +10,7 @@ Microsoft.Extensions.Localization.IStringLocalizer<T> $(NoWarn);CS1591 true localization + true diff --git a/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj b/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj index 73365a15eb47..8a820ac7b933 100644 --- a/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj +++ b/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj @@ -7,6 +7,7 @@ $(NoWarn);CS1591 true localization + true @@ -16,4 +17,8 @@ + + + + diff --git a/src/Localization/Localization/src/Properties/AssemblyInfo.cs b/src/Localization/Localization/src/Properties/AssemblyInfo.cs deleted file mode 100644 index 3e297b801e9b..000000000000 --- a/src/Localization/Localization/src/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Microsoft.Extensions.Localization.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] diff --git a/src/ObjectPool/src/Microsoft.Extensions.ObjectPool.csproj b/src/ObjectPool/src/Microsoft.Extensions.ObjectPool.csproj index cb42c5615a51..646d18a8b7f5 100644 --- a/src/ObjectPool/src/Microsoft.Extensions.ObjectPool.csproj +++ b/src/ObjectPool/src/Microsoft.Extensions.ObjectPool.csproj @@ -6,6 +6,7 @@ $(NoWarn);CS1591 true pooling + true diff --git a/src/Shared/ActivatorUtilities/sharedsources.props b/src/Shared/ActivatorUtilities/sharedsources.props index b35fe34b10f6..f75467753195 100644 --- a/src/Shared/ActivatorUtilities/sharedsources.props +++ b/src/Shared/ActivatorUtilities/sharedsources.props @@ -1,4 +1,8 @@ + + false + + true diff --git a/src/Shared/BenchmarkRunner/Directory.Build.props b/src/Shared/BenchmarkRunner/Directory.Build.props deleted file mode 100644 index d2f65e8d3dc4..000000000000 --- a/src/Shared/BenchmarkRunner/Directory.Build.props +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - Microsoft.AspNetCore.BenchmarkRunner.Sources - - diff --git a/src/Shared/CommandLineUtils/Microsoft.Extensions.CommandLineUtils.Sources.shproj b/src/Shared/CommandLineUtils/Microsoft.Extensions.CommandLineUtils.Sources.shproj deleted file mode 100644 index c728fe1012ce..000000000000 --- a/src/Shared/CommandLineUtils/Microsoft.Extensions.CommandLineUtils.Sources.shproj +++ /dev/null @@ -1,13 +0,0 @@ - - - - 00947d4a-c20e-46e3-90c3-6cd6bc87ee72 - 14.0 - - - - - - - - diff --git a/src/Testing/src/Microsoft.AspNetCore.Testing.csproj b/src/Testing/src/Microsoft.AspNetCore.Testing.csproj index 64e0b3c4e1d6..0e33852b62d7 100644 --- a/src/Testing/src/Microsoft.AspNetCore.Testing.csproj +++ b/src/Testing/src/Microsoft.AspNetCore.Testing.csproj @@ -8,10 +8,15 @@ aspnetcore false true + false true + + + + diff --git a/src/Testing/src/Properties/AssemblyInfo.cs b/src/Testing/src/Properties/AssemblyInfo.cs deleted file mode 100644 index 0212e111ee08..000000000000 --- a/src/Testing/src/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Testing.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] diff --git a/src/Testing/src/TestPathUtilities.cs b/src/Testing/src/TestPathUtilities.cs index ebd10897c382..f982471f39b2 100644 --- a/src/Testing/src/TestPathUtilities.cs +++ b/src/Testing/src/TestPathUtilities.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; @@ -8,6 +8,11 @@ namespace Microsoft.AspNetCore.Testing { public class TestPathUtilities { + public static string GetRepoRootDirectory() + { + return GetSolutionRootDirectory("Extensions"); + } + public static string GetSolutionRootDirectory(string solution) { var applicationBasePath = AppContext.BaseDirectory; diff --git a/src/Testing/src/xunit/ConditionalFactDiscoverer.cs b/src/Testing/src/xunit/ConditionalFactDiscoverer.cs index 819373fa3136..cf49b29e5a7f 100644 --- a/src/Testing/src/xunit/ConditionalFactDiscoverer.cs +++ b/src/Testing/src/xunit/ConditionalFactDiscoverer.cs @@ -20,8 +20,8 @@ protected override IXunitTestCase CreateTestCase(ITestFrameworkDiscoveryOptions { var skipReason = testMethod.EvaluateSkipConditions(); return skipReason != null - ? new SkippedTestCase(skipReason, _diagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), testMethod) + ? new SkippedTestCase(skipReason, _diagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), TestMethodDisplayOptions.None, testMethod) : base.CreateTestCase(discoveryOptions, testMethod, factAttribute); } } -} \ No newline at end of file +} diff --git a/src/Testing/src/xunit/ConditionalTheoryAttribute.cs b/src/Testing/src/xunit/ConditionalTheoryAttribute.cs index 9249078cc5c3..fe45f2ffc666 100644 --- a/src/Testing/src/xunit/ConditionalTheoryAttribute.cs +++ b/src/Testing/src/xunit/ConditionalTheoryAttribute.cs @@ -12,4 +12,4 @@ namespace Microsoft.AspNetCore.Testing.xunit public class ConditionalTheoryAttribute : TheoryAttribute { } -} \ No newline at end of file +} diff --git a/src/Testing/src/xunit/ConditionalTheoryDiscoverer.cs b/src/Testing/src/xunit/ConditionalTheoryDiscoverer.cs index d24421f5cd32..9e413cd5801a 100644 --- a/src/Testing/src/xunit/ConditionalTheoryDiscoverer.cs +++ b/src/Testing/src/xunit/ConditionalTheoryDiscoverer.cs @@ -14,11 +14,30 @@ public ConditionalTheoryDiscoverer(IMessageSink diagnosticMessageSink) { } + private sealed class OptionsWithPreEnumerationEnabled : ITestFrameworkDiscoveryOptions + { + private const string PreEnumerateTheories = "xunit.discovery.PreEnumerateTheories"; + + private readonly ITestFrameworkDiscoveryOptions _original; + + public OptionsWithPreEnumerationEnabled(ITestFrameworkDiscoveryOptions original) + => _original = original; + + public TValue GetValue(string name) + => (name == PreEnumerateTheories) ? (TValue)(object)true : _original.GetValue(name); + + public void SetValue(string name, TValue value) + => _original.SetValue(name, value); + } + + public override IEnumerable Discover(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo theoryAttribute) + => base.Discover(new OptionsWithPreEnumerationEnabled(discoveryOptions), testMethod, theoryAttribute); + protected override IEnumerable CreateTestCasesForTheory(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo theoryAttribute) { var skipReason = testMethod.EvaluateSkipConditions(); return skipReason != null - ? new[] { new SkippedTestCase(skipReason, DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), testMethod) } + ? new[] { new SkippedTestCase(skipReason, DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), TestMethodDisplayOptions.None, testMethod) } : base.CreateTestCasesForTheory(discoveryOptions, testMethod, theoryAttribute); } @@ -44,4 +63,4 @@ protected override IEnumerable CreateTestCasesForDataRow(ITestFr : base.CreateTestCasesForDataRow(discoveryOptions, testMethod, theoryAttribute, dataRow); } } -} \ No newline at end of file +} diff --git a/src/Testing/src/xunit/SkippedTestCase.cs b/src/Testing/src/xunit/SkippedTestCase.cs index c2e15fa640c4..1c25c507b972 100644 --- a/src/Testing/src/xunit/SkippedTestCase.cs +++ b/src/Testing/src/xunit/SkippedTestCase.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; @@ -16,8 +16,14 @@ public SkippedTestCase() : base() { } - public SkippedTestCase(string skipReason, IMessageSink diagnosticMessageSink, TestMethodDisplay defaultMethodDisplay, ITestMethod testMethod, object[] testMethodArguments = null) - : base(diagnosticMessageSink, defaultMethodDisplay, testMethod, testMethodArguments) + public SkippedTestCase( + string skipReason, + IMessageSink diagnosticMessageSink, + TestMethodDisplay defaultMethodDisplay, + TestMethodDisplayOptions defaultMethodDisplayOptions, + ITestMethod testMethod, + object[] testMethodArguments = null) + : base(diagnosticMessageSink, defaultMethodDisplay, defaultMethodDisplayOptions, testMethod, testMethodArguments) { _skipReason = skipReason; } @@ -37,4 +43,4 @@ public override void Serialize(IXunitSerializationInfo data) data.AddValue(nameof(_skipReason), _skipReason); } } -} \ No newline at end of file +} diff --git a/src/Testing/test/Microsoft.AspNetCore.Testing.Tests.csproj b/src/Testing/test/Microsoft.AspNetCore.Testing.Tests.csproj index 691c9ccd769d..acfb34b32028 100644 --- a/src/Testing/test/Microsoft.AspNetCore.Testing.Tests.csproj +++ b/src/Testing/test/Microsoft.AspNetCore.Testing.Tests.csproj @@ -24,5 +24,4 @@ - diff --git a/src/Testing/test/TestPathUtilitiesTest.cs b/src/Testing/test/TestPathUtilitiesTest.cs index 0c9a7c5ee4a8..c77194a548ce 100644 --- a/src/Testing/test/TestPathUtilitiesTest.cs +++ b/src/Testing/test/TestPathUtilitiesTest.cs @@ -16,7 +16,7 @@ public void GetSolutionRootDirectory_ResolvesSolutionRoot() // Testing\test\Microsoft.AspNetCore.Testing.Tests\bin\Debug\netcoreapp2.0 // Testing\test\Microsoft.AspNetCore.Testing.Tests\bin\Debug\net461 // Testing\test\Microsoft.AspNetCore.Testing.Tests\bin\Debug\net46 - var expectedPath = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), "..", "..", "..", "..", "..", "..", "..")); + var expectedPath = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), "..", "..", "..", "..", "..")); Assert.Equal(expectedPath, TestPathUtilities.GetSolutionRootDirectory("Extensions")); } diff --git a/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj b/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj index 18f96d9412b0..bc73592b3f5b 100644 --- a/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj +++ b/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj @@ -7,6 +7,7 @@ true true aspnetcore + true From fb64cd68876a1218faf6f13639a4d7610a7a90f3 Mon Sep 17 00:00:00 2001 From: Ryan Brandenburg Date: Tue, 29 Jan 2019 17:05:25 -0800 Subject: [PATCH 044/183] Reduce build times (dotnet/extensions#1016) * Fix package artifacts taking too long to upload * Make sure new packages are packaged \n\nCommit migrated from https://github.com/dotnet/extensions/commit/12613bac075ddd60e2dc8d3eea6510df55ee4e7c --- src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj b/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj index d91e8c2524e7..096b5d5bdd03 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj +++ b/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj @@ -5,6 +5,7 @@ Abstractions and features for interop between .NET and JavaScript code. javascript;interop true + true From 154ab28e451898d023e9c3cca97c2c6fb3590a1b Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Tue, 29 Jan 2019 18:34:54 -0800 Subject: [PATCH 045/183] Cleanup conversion to Arcade (dotnet/extensions#1014) * Remove obsolete targets, properties, and scripts * Replace IsProductComponent with IsShipping * Undo bad merge to version.props * Update documentation, and put workarounds into a common file * Replace usages of RepositoryRoot with RepoRoot * Remove API baselines * Remove unnecessary restore feeds and split workarounds into two files * Enable PR checks on all branches, and disable autocancel\n\nCommit migrated from https://github.com/dotnet/extensions/commit/f41cfded3c12eec0efea89ece1dafe43afa9c6b8 --- ...Extensions.Configuration.KeyPerFile.csproj | 3 +- .../Embedded/Directory.Build.props | 7 - ...t.Extensions.FileProviders.Embedded.csproj | 2 +- .../Embedded/src/baseline.netcore.json | 343 --------- ...ileProviders.Embedded.Manifest.Task.csproj | 2 +- .../Abstractions/Directory.Build.props | 7 - ...agnostics.HealthChecks.Abstractions.csproj | 2 +- .../Abstractions/src/baseline.netcore.json | 5 - .../HealthChecks/Directory.Build.props | 7 - ...Extensions.Diagnostics.HealthChecks.csproj | 2 +- .../HealthChecks/src/baseline.netcore.json | 5 - ...ions.Diagnostics.HealthChecks.Tests.csproj | 2 +- src/JSInterop/Directory.Build.props | 7 - .../src/Microsoft.JSInterop.csproj | 2 +- .../src/Mono.WebAssembly.Interop.csproj | 2 - ...xtensions.Localization.Abstractions.csproj | 2 +- .../Abstractions/src/baseline.netcore.json | 413 ----------- .../Microsoft.Extensions.Localization.csproj | 2 +- .../Localization/src/baseline.netcore.json | 687 ------------------ src/ObjectPool/Directory.Build.props | 7 - .../Microsoft.Extensions.ObjectPool.csproj | 2 +- src/ObjectPool/src/baseline.netcore.json | 612 ---------------- .../src/Microsoft.AspNetCore.Testing.csproj | 1 - src/WebEncoders/Directory.Build.props | 7 - .../Microsoft.Extensions.WebEncoders.csproj | 2 +- src/WebEncoders/src/baseline.netcore.json | 564 -------------- 26 files changed, 11 insertions(+), 2686 deletions(-) delete mode 100644 src/FileProviders/Embedded/Directory.Build.props delete mode 100644 src/FileProviders/Embedded/src/baseline.netcore.json delete mode 100644 src/HealthChecks/Abstractions/Directory.Build.props delete mode 100644 src/HealthChecks/Abstractions/src/baseline.netcore.json delete mode 100644 src/HealthChecks/HealthChecks/Directory.Build.props delete mode 100644 src/HealthChecks/HealthChecks/src/baseline.netcore.json delete mode 100644 src/JSInterop/Directory.Build.props delete mode 100644 src/Localization/Abstractions/src/baseline.netcore.json delete mode 100644 src/Localization/Localization/src/baseline.netcore.json delete mode 100644 src/ObjectPool/Directory.Build.props delete mode 100644 src/ObjectPool/src/baseline.netcore.json delete mode 100644 src/WebEncoders/Directory.Build.props delete mode 100644 src/WebEncoders/src/baseline.netcore.json diff --git a/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj b/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj index 82784d0eed89..5bd7b2c7ef0f 100644 --- a/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj +++ b/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj @@ -3,8 +3,7 @@ Configuration provider that uses files in a directory for Microsoft.Extensions.Configuration. netstandard2.0 - false - true + true diff --git a/src/FileProviders/Embedded/Directory.Build.props b/src/FileProviders/Embedded/Directory.Build.props deleted file mode 100644 index f25c1d90ce80..000000000000 --- a/src/FileProviders/Embedded/Directory.Build.props +++ /dev/null @@ -1,7 +0,0 @@ - - - - - true - - diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj index 90cb2e4cc336..792b9ff5b3f2 100644 --- a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj @@ -5,7 +5,7 @@ File provider for files in embedded resources for Microsoft.Extensions.FileProviders. netstandard2.0 $(MSBuildProjectName).nuspec - true + true diff --git a/src/FileProviders/Embedded/src/baseline.netcore.json b/src/FileProviders/Embedded/src/baseline.netcore.json deleted file mode 100644 index 821969ea0b51..000000000000 --- a/src/FileProviders/Embedded/src/baseline.netcore.json +++ /dev/null @@ -1,343 +0,0 @@ -{ - "AssemblyIdentity": "Microsoft.Extensions.FileProviders.Embedded, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", - "Types": [ - { - "Name": "Microsoft.Extensions.FileProviders.EmbeddedFileProvider", - "Visibility": "Public", - "Kind": "Class", - "ImplementedInterfaces": [ - "Microsoft.Extensions.FileProviders.IFileProvider" - ], - "Members": [ - { - "Kind": "Method", - "Name": "GetFileInfo", - "Parameters": [ - { - "Name": "subpath", - "Type": "System.String" - } - ], - "ReturnType": "Microsoft.Extensions.FileProviders.IFileInfo", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileProvider", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "GetDirectoryContents", - "Parameters": [ - { - "Name": "subpath", - "Type": "System.String" - } - ], - "ReturnType": "Microsoft.Extensions.FileProviders.IDirectoryContents", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileProvider", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Watch", - "Parameters": [ - { - "Name": "pattern", - "Type": "System.String" - } - ], - "ReturnType": "Microsoft.Extensions.Primitives.IChangeToken", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileProvider", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "assembly", - "Type": "System.Reflection.Assembly" - } - ], - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "assembly", - "Type": "System.Reflection.Assembly" - }, - { - "Name": "baseNamespace", - "Type": "System.String" - } - ], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.FileProviders.ManifestEmbeddedFileProvider", - "Visibility": "Public", - "Kind": "Class", - "ImplementedInterfaces": [ - "Microsoft.Extensions.FileProviders.IFileProvider" - ], - "Members": [ - { - "Kind": "Method", - "Name": "get_Assembly", - "Parameters": [], - "ReturnType": "System.Reflection.Assembly", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "GetDirectoryContents", - "Parameters": [ - { - "Name": "subpath", - "Type": "System.String" - } - ], - "ReturnType": "Microsoft.Extensions.FileProviders.IDirectoryContents", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileProvider", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "GetFileInfo", - "Parameters": [ - { - "Name": "subpath", - "Type": "System.String" - } - ], - "ReturnType": "Microsoft.Extensions.FileProviders.IFileInfo", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileProvider", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Watch", - "Parameters": [ - { - "Name": "filter", - "Type": "System.String" - } - ], - "ReturnType": "Microsoft.Extensions.Primitives.IChangeToken", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileProvider", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "assembly", - "Type": "System.Reflection.Assembly" - } - ], - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "assembly", - "Type": "System.Reflection.Assembly" - }, - { - "Name": "root", - "Type": "System.String" - } - ], - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "assembly", - "Type": "System.Reflection.Assembly" - }, - { - "Name": "root", - "Type": "System.String" - }, - { - "Name": "lastModified", - "Type": "System.DateTimeOffset" - } - ], - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "assembly", - "Type": "System.Reflection.Assembly" - }, - { - "Name": "root", - "Type": "System.String" - }, - { - "Name": "manifestName", - "Type": "System.String" - }, - { - "Name": "lastModified", - "Type": "System.DateTimeOffset" - } - ], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.FileProviders.Embedded.EmbeddedResourceFileInfo", - "Visibility": "Public", - "Kind": "Class", - "ImplementedInterfaces": [ - "Microsoft.Extensions.FileProviders.IFileInfo" - ], - "Members": [ - { - "Kind": "Method", - "Name": "get_Exists", - "Parameters": [], - "ReturnType": "System.Boolean", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileInfo", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_Length", - "Parameters": [], - "ReturnType": "System.Int64", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileInfo", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_PhysicalPath", - "Parameters": [], - "ReturnType": "System.String", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileInfo", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_Name", - "Parameters": [], - "ReturnType": "System.String", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileInfo", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_LastModified", - "Parameters": [], - "ReturnType": "System.DateTimeOffset", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileInfo", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_IsDirectory", - "Parameters": [], - "ReturnType": "System.Boolean", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileInfo", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "CreateReadStream", - "Parameters": [], - "ReturnType": "System.IO.Stream", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileInfo", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "assembly", - "Type": "System.Reflection.Assembly" - }, - { - "Name": "resourcePath", - "Type": "System.String" - }, - { - "Name": "name", - "Type": "System.String" - }, - { - "Name": "lastModified", - "Type": "System.DateTimeOffset" - } - ], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - } - ] -} \ No newline at end of file diff --git a/src/FileProviders/Manifest.MSBuildTask/src/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.csproj b/src/FileProviders/Manifest.MSBuildTask/src/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.csproj index 018cc98a8e65..f8ae098eab51 100644 --- a/src/FileProviders/Manifest.MSBuildTask/src/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.csproj +++ b/src/FileProviders/Manifest.MSBuildTask/src/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.csproj @@ -5,7 +5,7 @@ metadata of the files embedded in the assembly at compilation time. netstandard2.0 false - false + true false false diff --git a/src/HealthChecks/Abstractions/Directory.Build.props b/src/HealthChecks/Abstractions/Directory.Build.props deleted file mode 100644 index f25c1d90ce80..000000000000 --- a/src/HealthChecks/Abstractions/Directory.Build.props +++ /dev/null @@ -1,7 +0,0 @@ - - - - - true - - diff --git a/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj b/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj index 69298be027b7..2bba5959a32f 100644 --- a/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj +++ b/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj @@ -11,7 +11,7 @@ Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck $(NoWarn);CS1591 true diagnostics;healthchecks - true + true diff --git a/src/HealthChecks/Abstractions/src/baseline.netcore.json b/src/HealthChecks/Abstractions/src/baseline.netcore.json deleted file mode 100644 index 871db4c089ea..000000000000 --- a/src/HealthChecks/Abstractions/src/baseline.netcore.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "AssemblyIdentity": "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", - "Types": [ - ] -} \ No newline at end of file diff --git a/src/HealthChecks/HealthChecks/Directory.Build.props b/src/HealthChecks/HealthChecks/Directory.Build.props deleted file mode 100644 index f25c1d90ce80..000000000000 --- a/src/HealthChecks/HealthChecks/Directory.Build.props +++ /dev/null @@ -1,7 +0,0 @@ - - - - - true - - diff --git a/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj b/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj index b49f194d0c0a..463e5b36325e 100644 --- a/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj +++ b/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj @@ -10,7 +10,7 @@ Microsoft.Extensions.Diagnostics.HealthChecks.IHealthChecksBuilder $(NoWarn);CS1591 true diagnostics;healthchecks - true + true diff --git a/src/HealthChecks/HealthChecks/src/baseline.netcore.json b/src/HealthChecks/HealthChecks/src/baseline.netcore.json deleted file mode 100644 index cb2fe053f133..000000000000 --- a/src/HealthChecks/HealthChecks/src/baseline.netcore.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "AssemblyIdentity": "Microsoft.Extensions.Diagnostics.HealthChecks, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", - "Types": [ - ] -} \ No newline at end of file diff --git a/src/HealthChecks/HealthChecks/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests.csproj b/src/HealthChecks/HealthChecks/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests.csproj index 56b7e7e81ba7..163b6189004b 100644 --- a/src/HealthChecks/HealthChecks/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests.csproj +++ b/src/HealthChecks/HealthChecks/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests.csproj @@ -1,6 +1,6 @@  - + netcoreapp3.0;net472 diff --git a/src/JSInterop/Directory.Build.props b/src/JSInterop/Directory.Build.props deleted file mode 100644 index f25c1d90ce80..000000000000 --- a/src/JSInterop/Directory.Build.props +++ /dev/null @@ -1,7 +0,0 @@ - - - - - true - - diff --git a/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj b/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj index 096b5d5bdd03..f92b8d457d5f 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj +++ b/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj @@ -5,7 +5,7 @@ Abstractions and features for interop between .NET and JavaScript code. javascript;interop true - true + true diff --git a/src/JSInterop/Mono.WebAssembly.Interop/src/Mono.WebAssembly.Interop.csproj b/src/JSInterop/Mono.WebAssembly.Interop/src/Mono.WebAssembly.Interop.csproj index 75c8272e95b7..0ad8effd801f 100644 --- a/src/JSInterop/Mono.WebAssembly.Interop/src/Mono.WebAssembly.Interop.csproj +++ b/src/JSInterop/Mono.WebAssembly.Interop/src/Mono.WebAssembly.Interop.csproj @@ -2,8 +2,6 @@ netstandard2.0 - - false false diff --git a/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj b/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj index 636072724fce..33f58b63587b 100644 --- a/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj +++ b/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj @@ -10,7 +10,7 @@ Microsoft.Extensions.Localization.IStringLocalizer<T> $(NoWarn);CS1591 true localization - true + true diff --git a/src/Localization/Abstractions/src/baseline.netcore.json b/src/Localization/Abstractions/src/baseline.netcore.json deleted file mode 100644 index 02ba71db8e72..000000000000 --- a/src/Localization/Abstractions/src/baseline.netcore.json +++ /dev/null @@ -1,413 +0,0 @@ -{ - "AssemblyIdentity": "Microsoft.Extensions.Localization.Abstractions, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", - "Types": [ - { - "Name": "Microsoft.Extensions.Localization.IStringLocalizer", - "Visibility": "Public", - "Kind": "Interface", - "Abstract": true, - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "get_Item", - "Parameters": [ - { - "Name": "name", - "Type": "System.String" - } - ], - "ReturnType": "Microsoft.Extensions.Localization.LocalizedString", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_Item", - "Parameters": [ - { - "Name": "name", - "Type": "System.String" - }, - { - "Name": "arguments", - "Type": "System.Object[]", - "IsParams": true - } - ], - "ReturnType": "Microsoft.Extensions.Localization.LocalizedString", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "GetAllStrings", - "Parameters": [ - { - "Name": "includeParentCultures", - "Type": "System.Boolean" - } - ], - "ReturnType": "System.Collections.Generic.IEnumerable", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "WithCulture", - "Parameters": [ - { - "Name": "culture", - "Type": "System.Globalization.CultureInfo" - } - ], - "ReturnType": "Microsoft.Extensions.Localization.IStringLocalizer", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.Localization.IStringLocalizerFactory", - "Visibility": "Public", - "Kind": "Interface", - "Abstract": true, - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "Create", - "Parameters": [ - { - "Name": "resourceSource", - "Type": "System.Type" - } - ], - "ReturnType": "Microsoft.Extensions.Localization.IStringLocalizer", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Create", - "Parameters": [ - { - "Name": "baseName", - "Type": "System.String" - }, - { - "Name": "location", - "Type": "System.String" - } - ], - "ReturnType": "Microsoft.Extensions.Localization.IStringLocalizer", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.Localization.IStringLocalizer", - "Visibility": "Public", - "Kind": "Interface", - "Abstract": true, - "ImplementedInterfaces": [ - "Microsoft.Extensions.Localization.IStringLocalizer" - ], - "Members": [], - "GenericParameters": [ - { - "ParameterName": "T", - "ParameterPosition": 0, - "BaseTypeOrInterfaces": [] - } - ] - }, - { - "Name": "Microsoft.Extensions.Localization.LocalizedString", - "Visibility": "Public", - "Kind": "Class", - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "op_Implicit", - "Parameters": [ - { - "Name": "localizedString", - "Type": "Microsoft.Extensions.Localization.LocalizedString" - } - ], - "ReturnType": "System.String", - "Static": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_Name", - "Parameters": [], - "ReturnType": "System.String", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_Value", - "Parameters": [], - "ReturnType": "System.String", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_ResourceNotFound", - "Parameters": [], - "ReturnType": "System.Boolean", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_SearchedLocation", - "Parameters": [], - "ReturnType": "System.String", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "ToString", - "Parameters": [], - "ReturnType": "System.String", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "name", - "Type": "System.String" - }, - { - "Name": "value", - "Type": "System.String" - } - ], - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "name", - "Type": "System.String" - }, - { - "Name": "value", - "Type": "System.String" - }, - { - "Name": "resourceNotFound", - "Type": "System.Boolean" - } - ], - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "name", - "Type": "System.String" - }, - { - "Name": "value", - "Type": "System.String" - }, - { - "Name": "resourceNotFound", - "Type": "System.Boolean" - }, - { - "Name": "searchedLocation", - "Type": "System.String" - } - ], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.Localization.StringLocalizerExtensions", - "Visibility": "Public", - "Kind": "Class", - "Abstract": true, - "Static": true, - "Sealed": true, - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "GetString", - "Parameters": [ - { - "Name": "stringLocalizer", - "Type": "Microsoft.Extensions.Localization.IStringLocalizer" - }, - { - "Name": "name", - "Type": "System.String" - } - ], - "ReturnType": "Microsoft.Extensions.Localization.LocalizedString", - "Static": true, - "Extension": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "GetString", - "Parameters": [ - { - "Name": "stringLocalizer", - "Type": "Microsoft.Extensions.Localization.IStringLocalizer" - }, - { - "Name": "name", - "Type": "System.String" - }, - { - "Name": "arguments", - "Type": "System.Object[]", - "IsParams": true - } - ], - "ReturnType": "Microsoft.Extensions.Localization.LocalizedString", - "Static": true, - "Extension": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "GetAllStrings", - "Parameters": [ - { - "Name": "stringLocalizer", - "Type": "Microsoft.Extensions.Localization.IStringLocalizer" - } - ], - "ReturnType": "System.Collections.Generic.IEnumerable", - "Static": true, - "Extension": true, - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.Localization.StringLocalizer", - "Visibility": "Public", - "Kind": "Class", - "ImplementedInterfaces": [ - "Microsoft.Extensions.Localization.IStringLocalizer" - ], - "Members": [ - { - "Kind": "Method", - "Name": "get_Item", - "Parameters": [ - { - "Name": "name", - "Type": "System.String" - } - ], - "ReturnType": "Microsoft.Extensions.Localization.LocalizedString", - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_Item", - "Parameters": [ - { - "Name": "name", - "Type": "System.String" - }, - { - "Name": "arguments", - "Type": "System.Object[]", - "IsParams": true - } - ], - "ReturnType": "Microsoft.Extensions.Localization.LocalizedString", - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "GetAllStrings", - "Parameters": [ - { - "Name": "includeParentCultures", - "Type": "System.Boolean" - } - ], - "ReturnType": "System.Collections.Generic.IEnumerable", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "WithCulture", - "Parameters": [ - { - "Name": "culture", - "Type": "System.Globalization.CultureInfo" - } - ], - "ReturnType": "Microsoft.Extensions.Localization.IStringLocalizer", - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "factory", - "Type": "Microsoft.Extensions.Localization.IStringLocalizerFactory" - } - ], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [ - { - "ParameterName": "TResourceSource", - "ParameterPosition": 0, - "BaseTypeOrInterfaces": [] - } - ] - } - ] -} \ No newline at end of file diff --git a/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj b/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj index 8a820ac7b933..f16cbd9dab3f 100644 --- a/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj +++ b/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj @@ -7,7 +7,7 @@ $(NoWarn);CS1591 true localization - true + true diff --git a/src/Localization/Localization/src/baseline.netcore.json b/src/Localization/Localization/src/baseline.netcore.json deleted file mode 100644 index 860db76899cb..000000000000 --- a/src/Localization/Localization/src/baseline.netcore.json +++ /dev/null @@ -1,687 +0,0 @@ -{ - "AssemblyIdentity": "Microsoft.Extensions.Localization, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", - "Types": [ - { - "Name": "Microsoft.Extensions.DependencyInjection.LocalizationServiceCollectionExtensions", - "Visibility": "Public", - "Kind": "Class", - "Abstract": true, - "Static": true, - "Sealed": true, - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "AddLocalization", - "Parameters": [ - { - "Name": "services", - "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" - } - ], - "ReturnType": "Microsoft.Extensions.DependencyInjection.IServiceCollection", - "Static": true, - "Extension": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "AddLocalization", - "Parameters": [ - { - "Name": "services", - "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" - }, - { - "Name": "setupAction", - "Type": "System.Action" - } - ], - "ReturnType": "Microsoft.Extensions.DependencyInjection.IServiceCollection", - "Static": true, - "Extension": true, - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.Localization.IResourceNamesCache", - "Visibility": "Public", - "Kind": "Interface", - "Abstract": true, - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "GetOrAdd", - "Parameters": [ - { - "Name": "name", - "Type": "System.String" - }, - { - "Name": "valueFactory", - "Type": "System.Func>" - } - ], - "ReturnType": "System.Collections.Generic.IList", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.Localization.LocalizationOptions", - "Visibility": "Public", - "Kind": "Class", - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "get_ResourcesPath", - "Parameters": [], - "ReturnType": "System.String", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "set_ResourcesPath", - "Parameters": [ - { - "Name": "value", - "Type": "System.String" - } - ], - "ReturnType": "System.Void", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.Localization.ResourceLocationAttribute", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "System.Attribute", - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "get_ResourceLocation", - "Parameters": [], - "ReturnType": "System.String", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "resourceLocation", - "Type": "System.String" - } - ], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.Localization.ResourceManagerStringLocalizer", - "Visibility": "Public", - "Kind": "Class", - "ImplementedInterfaces": [ - "Microsoft.Extensions.Localization.IStringLocalizer" - ], - "Members": [ - { - "Kind": "Method", - "Name": "get_Item", - "Parameters": [ - { - "Name": "name", - "Type": "System.String" - } - ], - "ReturnType": "Microsoft.Extensions.Localization.LocalizedString", - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_Item", - "Parameters": [ - { - "Name": "name", - "Type": "System.String" - }, - { - "Name": "arguments", - "Type": "System.Object[]", - "IsParams": true - } - ], - "ReturnType": "Microsoft.Extensions.Localization.LocalizedString", - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "WithCulture", - "Parameters": [ - { - "Name": "culture", - "Type": "System.Globalization.CultureInfo" - } - ], - "ReturnType": "Microsoft.Extensions.Localization.IStringLocalizer", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "GetAllStrings", - "Parameters": [ - { - "Name": "includeParentCultures", - "Type": "System.Boolean" - } - ], - "ReturnType": "System.Collections.Generic.IEnumerable", - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "GetAllStrings", - "Parameters": [ - { - "Name": "includeParentCultures", - "Type": "System.Boolean" - }, - { - "Name": "culture", - "Type": "System.Globalization.CultureInfo" - } - ], - "ReturnType": "System.Collections.Generic.IEnumerable", - "Visibility": "Protected", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "GetStringSafely", - "Parameters": [ - { - "Name": "name", - "Type": "System.String" - }, - { - "Name": "culture", - "Type": "System.Globalization.CultureInfo" - } - ], - "ReturnType": "System.String", - "Visibility": "Protected", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "resourceManager", - "Type": "System.Resources.ResourceManager" - }, - { - "Name": "resourceAssembly", - "Type": "System.Reflection.Assembly" - }, - { - "Name": "baseName", - "Type": "System.String" - }, - { - "Name": "resourceNamesCache", - "Type": "Microsoft.Extensions.Localization.IResourceNamesCache" - }, - { - "Name": "logger", - "Type": "Microsoft.Extensions.Logging.ILogger" - } - ], - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "resourceManager", - "Type": "System.Resources.ResourceManager" - }, - { - "Name": "resourceAssemblyWrapper", - "Type": "Microsoft.Extensions.Localization.Internal.AssemblyWrapper" - }, - { - "Name": "baseName", - "Type": "System.String" - }, - { - "Name": "resourceNamesCache", - "Type": "Microsoft.Extensions.Localization.IResourceNamesCache" - }, - { - "Name": "logger", - "Type": "Microsoft.Extensions.Logging.ILogger" - } - ], - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "resourceManager", - "Type": "System.Resources.ResourceManager" - }, - { - "Name": "resourceStringProvider", - "Type": "Microsoft.Extensions.Localization.Internal.IResourceStringProvider" - }, - { - "Name": "baseName", - "Type": "System.String" - }, - { - "Name": "resourceNamesCache", - "Type": "Microsoft.Extensions.Localization.IResourceNamesCache" - }, - { - "Name": "logger", - "Type": "Microsoft.Extensions.Logging.ILogger" - } - ], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.Localization.ResourceManagerStringLocalizerFactory", - "Visibility": "Public", - "Kind": "Class", - "ImplementedInterfaces": [ - "Microsoft.Extensions.Localization.IStringLocalizerFactory" - ], - "Members": [ - { - "Kind": "Method", - "Name": "GetResourcePrefix", - "Parameters": [ - { - "Name": "typeInfo", - "Type": "System.Reflection.TypeInfo" - } - ], - "ReturnType": "System.String", - "Virtual": true, - "Visibility": "Protected", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "GetResourcePrefix", - "Parameters": [ - { - "Name": "typeInfo", - "Type": "System.Reflection.TypeInfo" - }, - { - "Name": "baseNamespace", - "Type": "System.String" - }, - { - "Name": "resourcesRelativePath", - "Type": "System.String" - } - ], - "ReturnType": "System.String", - "Virtual": true, - "Visibility": "Protected", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "GetResourcePrefix", - "Parameters": [ - { - "Name": "baseResourceName", - "Type": "System.String" - }, - { - "Name": "baseNamespace", - "Type": "System.String" - } - ], - "ReturnType": "System.String", - "Virtual": true, - "Visibility": "Protected", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Create", - "Parameters": [ - { - "Name": "resourceSource", - "Type": "System.Type" - } - ], - "ReturnType": "Microsoft.Extensions.Localization.IStringLocalizer", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizerFactory", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Create", - "Parameters": [ - { - "Name": "baseName", - "Type": "System.String" - }, - { - "Name": "location", - "Type": "System.String" - } - ], - "ReturnType": "Microsoft.Extensions.Localization.IStringLocalizer", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizerFactory", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "CreateResourceManagerStringLocalizer", - "Parameters": [ - { - "Name": "assembly", - "Type": "System.Reflection.Assembly" - }, - { - "Name": "baseName", - "Type": "System.String" - } - ], - "ReturnType": "Microsoft.Extensions.Localization.ResourceManagerStringLocalizer", - "Virtual": true, - "Visibility": "Protected", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "GetResourcePrefix", - "Parameters": [ - { - "Name": "location", - "Type": "System.String" - }, - { - "Name": "baseName", - "Type": "System.String" - }, - { - "Name": "resourceLocation", - "Type": "System.String" - } - ], - "ReturnType": "System.String", - "Virtual": true, - "Visibility": "Protected", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "GetResourceLocationAttribute", - "Parameters": [ - { - "Name": "assembly", - "Type": "System.Reflection.Assembly" - } - ], - "ReturnType": "Microsoft.Extensions.Localization.ResourceLocationAttribute", - "Virtual": true, - "Visibility": "Protected", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "GetRootNamespaceAttribute", - "Parameters": [ - { - "Name": "assembly", - "Type": "System.Reflection.Assembly" - } - ], - "ReturnType": "Microsoft.Extensions.Localization.RootNamespaceAttribute", - "Virtual": true, - "Visibility": "Protected", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "localizationOptions", - "Type": "Microsoft.Extensions.Options.IOptions" - }, - { - "Name": "loggerFactory", - "Type": "Microsoft.Extensions.Logging.ILoggerFactory" - } - ], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.Localization.ResourceManagerWithCultureStringLocalizer", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "Microsoft.Extensions.Localization.ResourceManagerStringLocalizer", - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "get_Item", - "Parameters": [ - { - "Name": "name", - "Type": "System.String" - } - ], - "ReturnType": "Microsoft.Extensions.Localization.LocalizedString", - "Virtual": true, - "Override": true, - "ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_Item", - "Parameters": [ - { - "Name": "name", - "Type": "System.String" - }, - { - "Name": "arguments", - "Type": "System.Object[]", - "IsParams": true - } - ], - "ReturnType": "Microsoft.Extensions.Localization.LocalizedString", - "Virtual": true, - "Override": true, - "ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "GetAllStrings", - "Parameters": [ - { - "Name": "includeParentCultures", - "Type": "System.Boolean" - } - ], - "ReturnType": "System.Collections.Generic.IEnumerable", - "Virtual": true, - "Override": true, - "ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "resourceManager", - "Type": "System.Resources.ResourceManager" - }, - { - "Name": "resourceAssembly", - "Type": "System.Reflection.Assembly" - }, - { - "Name": "baseName", - "Type": "System.String" - }, - { - "Name": "resourceNamesCache", - "Type": "Microsoft.Extensions.Localization.IResourceNamesCache" - }, - { - "Name": "culture", - "Type": "System.Globalization.CultureInfo" - }, - { - "Name": "logger", - "Type": "Microsoft.Extensions.Logging.ILogger" - } - ], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.Localization.ResourceNamesCache", - "Visibility": "Public", - "Kind": "Class", - "ImplementedInterfaces": [ - "Microsoft.Extensions.Localization.IResourceNamesCache" - ], - "Members": [ - { - "Kind": "Method", - "Name": "GetOrAdd", - "Parameters": [ - { - "Name": "name", - "Type": "System.String" - }, - { - "Name": "valueFactory", - "Type": "System.Func>" - } - ], - "ReturnType": "System.Collections.Generic.IList", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.Localization.IResourceNamesCache", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.Localization.RootNamespaceAttribute", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "System.Attribute", - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "get_RootNamespace", - "Parameters": [], - "ReturnType": "System.String", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "rootNamespace", - "Type": "System.String" - } - ], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - } - ] -} \ No newline at end of file diff --git a/src/ObjectPool/Directory.Build.props b/src/ObjectPool/Directory.Build.props deleted file mode 100644 index f25c1d90ce80..000000000000 --- a/src/ObjectPool/Directory.Build.props +++ /dev/null @@ -1,7 +0,0 @@ - - - - - true - - diff --git a/src/ObjectPool/src/Microsoft.Extensions.ObjectPool.csproj b/src/ObjectPool/src/Microsoft.Extensions.ObjectPool.csproj index 646d18a8b7f5..71e9abed79c7 100644 --- a/src/ObjectPool/src/Microsoft.Extensions.ObjectPool.csproj +++ b/src/ObjectPool/src/Microsoft.Extensions.ObjectPool.csproj @@ -6,7 +6,7 @@ $(NoWarn);CS1591 true pooling - true + true diff --git a/src/ObjectPool/src/baseline.netcore.json b/src/ObjectPool/src/baseline.netcore.json deleted file mode 100644 index 253c1f6b6641..000000000000 --- a/src/ObjectPool/src/baseline.netcore.json +++ /dev/null @@ -1,612 +0,0 @@ -{ - "AssemblyIdentity": "Microsoft.Extensions.ObjectPool, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", - "Types": [ - { - "Name": "Microsoft.Extensions.ObjectPool.DefaultObjectPool", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "Microsoft.Extensions.ObjectPool.ObjectPool", - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "Get", - "Parameters": [], - "ReturnType": "T0", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Return", - "Parameters": [ - { - "Name": "obj", - "Type": "T0" - } - ], - "ReturnType": "System.Void", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "policy", - "Type": "Microsoft.Extensions.ObjectPool.IPooledObjectPolicy" - } - ], - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "policy", - "Type": "Microsoft.Extensions.ObjectPool.IPooledObjectPolicy" - }, - { - "Name": "maximumRetained", - "Type": "System.Int32" - } - ], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [ - { - "ParameterName": "T", - "ParameterPosition": 0, - "Class": true, - "BaseTypeOrInterfaces": [] - } - ] - }, - { - "Name": "Microsoft.Extensions.ObjectPool.DefaultObjectPoolProvider", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "Microsoft.Extensions.ObjectPool.ObjectPoolProvider", - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "get_MaximumRetained", - "Parameters": [], - "ReturnType": "System.Int32", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "set_MaximumRetained", - "Parameters": [ - { - "Name": "value", - "Type": "System.Int32" - } - ], - "ReturnType": "System.Void", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Create", - "Parameters": [ - { - "Name": "policy", - "Type": "Microsoft.Extensions.ObjectPool.IPooledObjectPolicy" - } - ], - "ReturnType": "Microsoft.Extensions.ObjectPool.ObjectPool", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [ - { - "ParameterName": "T", - "ParameterPosition": 0, - "Class": true, - "BaseTypeOrInterfaces": [] - } - ] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.ObjectPool.DefaultPooledObjectPolicy", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "Microsoft.Extensions.ObjectPool.PooledObjectPolicy", - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "Create", - "Parameters": [], - "ReturnType": "T0", - "Virtual": true, - "Override": true, - "ImplementedInterface": "Microsoft.Extensions.ObjectPool.IPooledObjectPolicy", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Return", - "Parameters": [ - { - "Name": "obj", - "Type": "T0" - } - ], - "ReturnType": "System.Boolean", - "Virtual": true, - "Override": true, - "ImplementedInterface": "Microsoft.Extensions.ObjectPool.IPooledObjectPolicy", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [ - { - "ParameterName": "T", - "ParameterPosition": 0, - "New": true, - "Class": true, - "BaseTypeOrInterfaces": [] - } - ] - }, - { - "Name": "Microsoft.Extensions.ObjectPool.IPooledObjectPolicy", - "Visibility": "Public", - "Kind": "Interface", - "Abstract": true, - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "Create", - "Parameters": [], - "ReturnType": "T0", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Return", - "Parameters": [ - { - "Name": "obj", - "Type": "T0" - } - ], - "ReturnType": "System.Boolean", - "GenericParameter": [] - } - ], - "GenericParameters": [ - { - "ParameterName": "T", - "ParameterPosition": 0, - "BaseTypeOrInterfaces": [] - } - ] - }, - { - "Name": "Microsoft.Extensions.ObjectPool.LeakTrackingObjectPool", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "Microsoft.Extensions.ObjectPool.ObjectPool", - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "Get", - "Parameters": [], - "ReturnType": "T0", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Return", - "Parameters": [ - { - "Name": "obj", - "Type": "T0" - } - ], - "ReturnType": "System.Void", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "inner", - "Type": "Microsoft.Extensions.ObjectPool.ObjectPool" - } - ], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [ - { - "ParameterName": "T", - "ParameterPosition": 0, - "Class": true, - "BaseTypeOrInterfaces": [] - } - ] - }, - { - "Name": "Microsoft.Extensions.ObjectPool.LeakTrackingObjectPoolProvider", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "Microsoft.Extensions.ObjectPool.ObjectPoolProvider", - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "Create", - "Parameters": [ - { - "Name": "policy", - "Type": "Microsoft.Extensions.ObjectPool.IPooledObjectPolicy" - } - ], - "ReturnType": "Microsoft.Extensions.ObjectPool.ObjectPool", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [ - { - "ParameterName": "T", - "ParameterPosition": 0, - "Class": true, - "BaseTypeOrInterfaces": [] - } - ] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "inner", - "Type": "Microsoft.Extensions.ObjectPool.ObjectPoolProvider" - } - ], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.ObjectPool.ObjectPool", - "Visibility": "Public", - "Kind": "Class", - "Abstract": true, - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "Get", - "Parameters": [], - "ReturnType": "T0", - "Virtual": true, - "Abstract": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Return", - "Parameters": [ - { - "Name": "obj", - "Type": "T0" - } - ], - "ReturnType": "System.Void", - "Virtual": true, - "Abstract": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [], - "Visibility": "Protected", - "GenericParameter": [] - } - ], - "GenericParameters": [ - { - "ParameterName": "T", - "ParameterPosition": 0, - "Class": true, - "BaseTypeOrInterfaces": [] - } - ] - }, - { - "Name": "Microsoft.Extensions.ObjectPool.ObjectPoolProvider", - "Visibility": "Public", - "Kind": "Class", - "Abstract": true, - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "Create", - "Parameters": [ - { - "Name": "policy", - "Type": "Microsoft.Extensions.ObjectPool.IPooledObjectPolicy" - } - ], - "ReturnType": "Microsoft.Extensions.ObjectPool.ObjectPool", - "Virtual": true, - "Abstract": true, - "Visibility": "Public", - "GenericParameter": [ - { - "ParameterName": "T", - "ParameterPosition": 0, - "Class": true, - "BaseTypeOrInterfaces": [] - } - ] - }, - { - "Kind": "Method", - "Name": "Create", - "Parameters": [], - "ReturnType": "Microsoft.Extensions.ObjectPool.ObjectPool", - "Visibility": "Public", - "GenericParameter": [ - { - "ParameterName": "T", - "ParameterPosition": 0, - "New": true, - "Class": true, - "BaseTypeOrInterfaces": [] - } - ] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [], - "Visibility": "Protected", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.ObjectPool.ObjectPoolProviderExtensions", - "Visibility": "Public", - "Kind": "Class", - "Abstract": true, - "Static": true, - "Sealed": true, - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "CreateStringBuilderPool", - "Parameters": [ - { - "Name": "provider", - "Type": "Microsoft.Extensions.ObjectPool.ObjectPoolProvider" - } - ], - "ReturnType": "Microsoft.Extensions.ObjectPool.ObjectPool", - "Static": true, - "Extension": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "CreateStringBuilderPool", - "Parameters": [ - { - "Name": "provider", - "Type": "Microsoft.Extensions.ObjectPool.ObjectPoolProvider" - }, - { - "Name": "initialCapacity", - "Type": "System.Int32" - }, - { - "Name": "maximumRetainedCapacity", - "Type": "System.Int32" - } - ], - "ReturnType": "Microsoft.Extensions.ObjectPool.ObjectPool", - "Static": true, - "Extension": true, - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.ObjectPool.PooledObjectPolicy", - "Visibility": "Public", - "Kind": "Class", - "Abstract": true, - "ImplementedInterfaces": [ - "Microsoft.Extensions.ObjectPool.IPooledObjectPolicy" - ], - "Members": [ - { - "Kind": "Method", - "Name": "Create", - "Parameters": [], - "ReturnType": "T0", - "Virtual": true, - "Abstract": true, - "ImplementedInterface": "Microsoft.Extensions.ObjectPool.IPooledObjectPolicy", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Return", - "Parameters": [ - { - "Name": "obj", - "Type": "T0" - } - ], - "ReturnType": "System.Boolean", - "Virtual": true, - "Abstract": true, - "ImplementedInterface": "Microsoft.Extensions.ObjectPool.IPooledObjectPolicy", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [], - "Visibility": "Protected", - "GenericParameter": [] - } - ], - "GenericParameters": [ - { - "ParameterName": "T", - "ParameterPosition": 0, - "BaseTypeOrInterfaces": [] - } - ] - }, - { - "Name": "Microsoft.Extensions.ObjectPool.StringBuilderPooledObjectPolicy", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "Microsoft.Extensions.ObjectPool.PooledObjectPolicy", - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "Create", - "Parameters": [], - "ReturnType": "System.Text.StringBuilder", - "Virtual": true, - "Override": true, - "ImplementedInterface": "Microsoft.Extensions.ObjectPool.IPooledObjectPolicy", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Return", - "Parameters": [ - { - "Name": "obj", - "Type": "System.Text.StringBuilder" - } - ], - "ReturnType": "System.Boolean", - "Virtual": true, - "Override": true, - "ImplementedInterface": "Microsoft.Extensions.ObjectPool.IPooledObjectPolicy", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_InitialCapacity", - "Parameters": [], - "ReturnType": "System.Int32", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "set_InitialCapacity", - "Parameters": [ - { - "Name": "value", - "Type": "System.Int32" - } - ], - "ReturnType": "System.Void", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_MaximumRetainedCapacity", - "Parameters": [], - "ReturnType": "System.Int32", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "set_MaximumRetainedCapacity", - "Parameters": [ - { - "Name": "value", - "Type": "System.Int32" - } - ], - "ReturnType": "System.Void", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - } - ] -} \ No newline at end of file diff --git a/src/Testing/src/Microsoft.AspNetCore.Testing.csproj b/src/Testing/src/Microsoft.AspNetCore.Testing.csproj index 0e33852b62d7..b3b8046ccb78 100644 --- a/src/Testing/src/Microsoft.AspNetCore.Testing.csproj +++ b/src/Testing/src/Microsoft.AspNetCore.Testing.csproj @@ -6,7 +6,6 @@ $(NoWarn);CS1591 true aspnetcore - false true false diff --git a/src/WebEncoders/Directory.Build.props b/src/WebEncoders/Directory.Build.props deleted file mode 100644 index f25c1d90ce80..000000000000 --- a/src/WebEncoders/Directory.Build.props +++ /dev/null @@ -1,7 +0,0 @@ - - - - - true - - diff --git a/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj b/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj index bc73592b3f5b..dfc575c73fec 100644 --- a/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj +++ b/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj @@ -7,7 +7,7 @@ true true aspnetcore - true + true diff --git a/src/WebEncoders/src/baseline.netcore.json b/src/WebEncoders/src/baseline.netcore.json deleted file mode 100644 index 6da0ae07547e..000000000000 --- a/src/WebEncoders/src/baseline.netcore.json +++ /dev/null @@ -1,564 +0,0 @@ -{ - "AssemblyIdentity": "Microsoft.Extensions.WebEncoders, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", - "Types": [ - { - "Name": "Microsoft.Extensions.WebEncoders.WebEncoderOptions", - "Visibility": "Public", - "Kind": "Class", - "Sealed": true, - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "get_TextEncoderSettings", - "Parameters": [], - "ReturnType": "System.Text.Encodings.Web.TextEncoderSettings", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "set_TextEncoderSettings", - "Parameters": [ - { - "Name": "value", - "Type": "System.Text.Encodings.Web.TextEncoderSettings" - } - ], - "ReturnType": "System.Void", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.WebEncoders.Testing.HtmlTestEncoder", - "Visibility": "Public", - "Kind": "Class", - "Sealed": true, - "BaseType": "System.Text.Encodings.Web.HtmlEncoder", - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "get_MaxOutputCharactersPerInputCharacter", - "Parameters": [], - "ReturnType": "System.Int32", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Encode", - "Parameters": [ - { - "Name": "value", - "Type": "System.String" - } - ], - "ReturnType": "System.String", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Encode", - "Parameters": [ - { - "Name": "output", - "Type": "System.IO.TextWriter" - }, - { - "Name": "value", - "Type": "System.Char[]" - }, - { - "Name": "startIndex", - "Type": "System.Int32" - }, - { - "Name": "characterCount", - "Type": "System.Int32" - } - ], - "ReturnType": "System.Void", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Encode", - "Parameters": [ - { - "Name": "output", - "Type": "System.IO.TextWriter" - }, - { - "Name": "value", - "Type": "System.String" - }, - { - "Name": "startIndex", - "Type": "System.Int32" - }, - { - "Name": "characterCount", - "Type": "System.Int32" - } - ], - "ReturnType": "System.Void", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "WillEncode", - "Parameters": [ - { - "Name": "unicodeScalar", - "Type": "System.Int32" - } - ], - "ReturnType": "System.Boolean", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "FindFirstCharacterToEncode", - "Parameters": [ - { - "Name": "text", - "Type": "System.Char*" - }, - { - "Name": "textLength", - "Type": "System.Int32" - } - ], - "ReturnType": "System.Int32", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "TryEncodeUnicodeScalar", - "Parameters": [ - { - "Name": "unicodeScalar", - "Type": "System.Int32" - }, - { - "Name": "buffer", - "Type": "System.Char*" - }, - { - "Name": "bufferLength", - "Type": "System.Int32" - }, - { - "Name": "numberOfCharactersWritten", - "Type": "System.Int32", - "Direction": "Out" - } - ], - "ReturnType": "System.Boolean", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.WebEncoders.Testing.JavaScriptTestEncoder", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "System.Text.Encodings.Web.JavaScriptEncoder", - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "get_MaxOutputCharactersPerInputCharacter", - "Parameters": [], - "ReturnType": "System.Int32", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Encode", - "Parameters": [ - { - "Name": "value", - "Type": "System.String" - } - ], - "ReturnType": "System.String", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Encode", - "Parameters": [ - { - "Name": "output", - "Type": "System.IO.TextWriter" - }, - { - "Name": "value", - "Type": "System.Char[]" - }, - { - "Name": "startIndex", - "Type": "System.Int32" - }, - { - "Name": "characterCount", - "Type": "System.Int32" - } - ], - "ReturnType": "System.Void", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Encode", - "Parameters": [ - { - "Name": "output", - "Type": "System.IO.TextWriter" - }, - { - "Name": "value", - "Type": "System.String" - }, - { - "Name": "startIndex", - "Type": "System.Int32" - }, - { - "Name": "characterCount", - "Type": "System.Int32" - } - ], - "ReturnType": "System.Void", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "WillEncode", - "Parameters": [ - { - "Name": "unicodeScalar", - "Type": "System.Int32" - } - ], - "ReturnType": "System.Boolean", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "FindFirstCharacterToEncode", - "Parameters": [ - { - "Name": "text", - "Type": "System.Char*" - }, - { - "Name": "textLength", - "Type": "System.Int32" - } - ], - "ReturnType": "System.Int32", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "TryEncodeUnicodeScalar", - "Parameters": [ - { - "Name": "unicodeScalar", - "Type": "System.Int32" - }, - { - "Name": "buffer", - "Type": "System.Char*" - }, - { - "Name": "bufferLength", - "Type": "System.Int32" - }, - { - "Name": "numberOfCharactersWritten", - "Type": "System.Int32", - "Direction": "Out" - } - ], - "ReturnType": "System.Boolean", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.WebEncoders.Testing.UrlTestEncoder", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "System.Text.Encodings.Web.UrlEncoder", - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "get_MaxOutputCharactersPerInputCharacter", - "Parameters": [], - "ReturnType": "System.Int32", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Encode", - "Parameters": [ - { - "Name": "value", - "Type": "System.String" - } - ], - "ReturnType": "System.String", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Encode", - "Parameters": [ - { - "Name": "output", - "Type": "System.IO.TextWriter" - }, - { - "Name": "value", - "Type": "System.Char[]" - }, - { - "Name": "startIndex", - "Type": "System.Int32" - }, - { - "Name": "characterCount", - "Type": "System.Int32" - } - ], - "ReturnType": "System.Void", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Encode", - "Parameters": [ - { - "Name": "output", - "Type": "System.IO.TextWriter" - }, - { - "Name": "value", - "Type": "System.String" - }, - { - "Name": "startIndex", - "Type": "System.Int32" - }, - { - "Name": "characterCount", - "Type": "System.Int32" - } - ], - "ReturnType": "System.Void", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "WillEncode", - "Parameters": [ - { - "Name": "unicodeScalar", - "Type": "System.Int32" - } - ], - "ReturnType": "System.Boolean", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "FindFirstCharacterToEncode", - "Parameters": [ - { - "Name": "text", - "Type": "System.Char*" - }, - { - "Name": "textLength", - "Type": "System.Int32" - } - ], - "ReturnType": "System.Int32", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "TryEncodeUnicodeScalar", - "Parameters": [ - { - "Name": "unicodeScalar", - "Type": "System.Int32" - }, - { - "Name": "buffer", - "Type": "System.Char*" - }, - { - "Name": "bufferLength", - "Type": "System.Int32" - }, - { - "Name": "numberOfCharactersWritten", - "Type": "System.Int32", - "Direction": "Out" - } - ], - "ReturnType": "System.Boolean", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.DependencyInjection.EncoderServiceCollectionExtensions", - "Visibility": "Public", - "Kind": "Class", - "Abstract": true, - "Static": true, - "Sealed": true, - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "AddWebEncoders", - "Parameters": [ - { - "Name": "services", - "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" - } - ], - "ReturnType": "Microsoft.Extensions.DependencyInjection.IServiceCollection", - "Static": true, - "Extension": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "AddWebEncoders", - "Parameters": [ - { - "Name": "services", - "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" - }, - { - "Name": "setupAction", - "Type": "System.Action" - } - ], - "ReturnType": "Microsoft.Extensions.DependencyInjection.IServiceCollection", - "Static": true, - "Extension": true, - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - } - ] -} \ No newline at end of file From 070bba8ad4dfae6dc0a0e1df438592960365c35e Mon Sep 17 00:00:00 2001 From: Kahbazi Date: Wed, 9 Jan 2019 08:16:28 +0330 Subject: [PATCH 046/183] Add event name in localization\n\nCommit migrated from https://github.com/dotnet/extensions/commit/d2b1ad3d233a0750647ed2c73343a293f08c5709 --- .../Internal/ResourceManagerStringLocalizerLoggerExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Localization/Localization/src/Internal/ResourceManagerStringLocalizerLoggerExtensions.cs b/src/Localization/Localization/src/Internal/ResourceManagerStringLocalizerLoggerExtensions.cs index 456e07009e38..63f40536ca42 100644 --- a/src/Localization/Localization/src/Internal/ResourceManagerStringLocalizerLoggerExtensions.cs +++ b/src/Localization/Localization/src/Internal/ResourceManagerStringLocalizerLoggerExtensions.cs @@ -15,7 +15,7 @@ static ResourceManagerStringLocalizerLoggerExtensions() { _searchedLocation = LoggerMessage.Define( LogLevel.Debug, - 1, + new EventId(1, "SearchedLocation"), $"{nameof(ResourceManagerStringLocalizer)} searched for '{{Key}}' in '{{LocationSearched}}' with culture '{{Culture}}'."); } From bbca5f30e729c957e3a89fd717a014c18fd5cc1f Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Thu, 31 Jan 2019 13:20:34 -0800 Subject: [PATCH 047/183] Remove implicit references for non-test projects (dotnet/extensions#1037) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/e504b4ee74bba3a7df2be5612e8d509ad61b0c24 --- src/Testing/src/Microsoft.AspNetCore.Testing.csproj | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Testing/src/Microsoft.AspNetCore.Testing.csproj b/src/Testing/src/Microsoft.AspNetCore.Testing.csproj index b3b8046ccb78..9da267c4198a 100644 --- a/src/Testing/src/Microsoft.AspNetCore.Testing.csproj +++ b/src/Testing/src/Microsoft.AspNetCore.Testing.csproj @@ -6,6 +6,8 @@ $(NoWarn);CS1591 true aspnetcore + + false true false @@ -19,6 +21,15 @@ + + From a663bcf605510df7c7114fccaf9943e4595a2b00 Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Fri, 1 Feb 2019 10:33:04 -0800 Subject: [PATCH 048/183] Add code check CI tests, add docs about ReferenceResolution, and other cleanups (dotnet/extensions#1044) Changes: * Add a step which checks that generated code is up to date * Copy over the documentation on how to work with `` * Fixup a broken reference in the .sln * Fix the casing on Microsoft.Extensions.ValueStopwatch.Sources * Remove some unused references and variables\n\nCommit migrated from https://github.com/dotnet/extensions/commit/92acd8b95901f1e8f47d3d128cb5f7c38b684644 --- ...rosoft.Extensions.FileProviders.Embedded.Manifest.Task.csproj | 1 - ....Extensions.FileProviders.Embedded.Manifest.Task.Tests.csproj | 1 - 2 files changed, 2 deletions(-) diff --git a/src/FileProviders/Manifest.MSBuildTask/src/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.csproj b/src/FileProviders/Manifest.MSBuildTask/src/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.csproj index f8ae098eab51..cdc4ffdcb0b4 100644 --- a/src/FileProviders/Manifest.MSBuildTask/src/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.csproj +++ b/src/FileProviders/Manifest.MSBuildTask/src/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.csproj @@ -11,7 +11,6 @@ - diff --git a/src/FileProviders/Manifest.MSBuildTask/test/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Tests.csproj b/src/FileProviders/Manifest.MSBuildTask/test/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Tests.csproj index 3f7647a4f18f..ed68958fe871 100644 --- a/src/FileProviders/Manifest.MSBuildTask/test/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Tests.csproj +++ b/src/FileProviders/Manifest.MSBuildTask/test/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Tests.csproj @@ -9,7 +9,6 @@ - From b47bd1789403016d7a778a78402ae4e330e7da60 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Fri, 1 Feb 2019 14:12:49 -0800 Subject: [PATCH 049/183] Publish Microsoft.Interop.Js and Mono.WebAssembly.Interop (dotnet/extensions#1048) * Publish Microsoft.Interop.Js and Mono.WebAssembly.Interop \n\nCommit migrated from https://github.com/dotnet/extensions/commit/bc2e7150f00198a160cb5b0452a294f30b78af76 --- .../src/Microsoft.JSInterop.JS.csproj | 13 - .../src/Microsoft.JSInterop.JS.npmproj | 12 + .../src/package-lock.json | 269 +++++++++++++++++- .../Microsoft.JSInterop.JS/src/package.json | 28 +- .../Microsoft.JSInterop.JS/src/tslint.json | 14 + .../src/Mono.WebAssembly.Interop.csproj | 5 +- 6 files changed, 303 insertions(+), 38 deletions(-) delete mode 100644 src/JSInterop/Microsoft.JSInterop.JS/src/Microsoft.JSInterop.JS.csproj create mode 100644 src/JSInterop/Microsoft.JSInterop.JS/src/Microsoft.JSInterop.JS.npmproj create mode 100644 src/JSInterop/Microsoft.JSInterop.JS/src/tslint.json diff --git a/src/JSInterop/Microsoft.JSInterop.JS/src/Microsoft.JSInterop.JS.csproj b/src/JSInterop/Microsoft.JSInterop.JS/src/Microsoft.JSInterop.JS.csproj deleted file mode 100644 index 7f806d0afa3b..000000000000 --- a/src/JSInterop/Microsoft.JSInterop.JS/src/Microsoft.JSInterop.JS.csproj +++ /dev/null @@ -1,13 +0,0 @@ - - - - netstandard2.0 - Latest - false - - - - - - - diff --git a/src/JSInterop/Microsoft.JSInterop.JS/src/Microsoft.JSInterop.JS.npmproj b/src/JSInterop/Microsoft.JSInterop.JS/src/Microsoft.JSInterop.JS.npmproj new file mode 100644 index 000000000000..e8d0554ff701 --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop.JS/src/Microsoft.JSInterop.JS.npmproj @@ -0,0 +1,12 @@ + + + + + @dotnet/jsinterop + true + false + true + + + + diff --git a/src/JSInterop/Microsoft.JSInterop.JS/src/package-lock.json b/src/JSInterop/Microsoft.JSInterop.JS/src/package-lock.json index a7f31ead4fb0..4c82321255da 100644 --- a/src/JSInterop/Microsoft.JSInterop.JS/src/package-lock.json +++ b/src/JSInterop/Microsoft.JSInterop.JS/src/package-lock.json @@ -1,9 +1,56 @@ { "name": "@dotnet/jsinterop", - "version": "0.1.1", + "version": "3.0.0-dev", "lockfileVersion": 1, "requires": true, "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + }, + "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + } + } + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -16,16 +63,98 @@ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { - "balanced-match": "1.0.0", + "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "commander": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", + "dev": true + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -38,22 +167,37 @@ "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", "dev": true, "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" } }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" + "once": "^1.3.0", + "wrappy": "1" } }, "inherits": { @@ -62,13 +206,29 @@ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "js-yaml": { + "version": "3.12.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.1.tgz", + "integrity": "sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { - "brace-expansion": "1.1.11" + "brace-expansion": "^1.1.7" } }, "once": { @@ -77,7 +237,7 @@ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { - "wrappy": "1.0.2" + "wrappy": "1" } }, "path-is-absolute": { @@ -86,15 +246,98 @@ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "resolve": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", + "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, "rimraf": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", "dev": true, "requires": { - "glob": "7.1.3" + "glob": "^7.0.5" } }, + "semver": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", + "dev": true + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "tslib": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", + "dev": true + }, + "tslint": { + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.12.1.tgz", + "integrity": "sha512-sfodBHOucFg6egff8d1BvuofoOQ/nOeYNfbp7LDlKBcLNrL3lmS5zoiDGyOMdT7YsEXAwWpTdAHwOGOc8eRZAw==", + "dev": true, + "requires": { + "babel-code-frame": "^6.22.0", + "builtin-modules": "^1.1.1", + "chalk": "^2.3.0", + "commander": "^2.12.1", + "diff": "^3.2.0", + "glob": "^7.1.1", + "js-yaml": "^3.7.0", + "minimatch": "^3.0.4", + "resolve": "^1.3.2", + "semver": "^5.3.0", + "tslib": "^1.8.0", + "tsutils": "^2.27.2" + } + }, + "tsutils": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", + "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "typescript": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz", + "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==", + "dev": true + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/src/JSInterop/Microsoft.JSInterop.JS/src/package.json b/src/JSInterop/Microsoft.JSInterop.JS/src/package.json index a84734bc464b..8be950e16fef 100644 --- a/src/JSInterop/Microsoft.JSInterop.JS/src/package.json +++ b/src/JSInterop/Microsoft.JSInterop.JS/src/package.json @@ -1,25 +1,31 @@ { "name": "@dotnet/jsinterop", - "version": "0.1.1", + "version": "3.0.0-dev", "description": "Provides abstractions and features for interop between .NET and JavaScript code.", "main": "dist/Microsoft.JSInterop.js", "types": "dist/Microsoft.JSInterop.d.js", "scripts": { - "prepublish": "rimraf dist && dotnet build && echo 'Finished building NPM package \"@dotnet/jsinterop\"'" + "clean": "node node_modules/rimraf/bin.js ./dist", + "build": "npm run clean && npm run build:esm", + "build:lint": "node node_modules/tslint/bin/tslint -p ./tsconfig.json", + "build:esm": "node node_modules/typescript/bin/tsc --project ./tsconfig.json" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/aspnet/AspNetCore.git" }, - "files": [ - "dist/**" - ], "author": "Microsoft", "license": "Apache-2.0", "bugs": { - "url": "https://github.com/dotnet/jsinterop/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/dotnet/jsinterop.git" + "url": "https://github.com/aspnet/AspNetCore/issues" }, + "homepage": "https://github.com/aspnet/Extensions/tree/master/src/JSInterop#readme", + "files": [ + "dist/**" + ], "devDependencies": { - "rimraf": "^2.5.4" + "rimraf": "^2.5.4", + "tslint": "^5.9.1", + "typescript": "^2.7.1" } } diff --git a/src/JSInterop/Microsoft.JSInterop.JS/src/tslint.json b/src/JSInterop/Microsoft.JSInterop.JS/src/tslint.json new file mode 100644 index 000000000000..5c38bef9900e --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop.JS/src/tslint.json @@ -0,0 +1,14 @@ +{ + "extends": "tslint:recommended", + "rules": { + "max-line-length": { "options": [300] }, + "member-ordering": false, + "interface-name": false, + "unified-signatures": false, + "max-classes-per-file": false, + "no-floating-promises": true, + "no-empty": false, + "no-bitwise": false, + "no-console": false + } +} diff --git a/src/JSInterop/Mono.WebAssembly.Interop/src/Mono.WebAssembly.Interop.csproj b/src/JSInterop/Mono.WebAssembly.Interop/src/Mono.WebAssembly.Interop.csproj index 0ad8effd801f..413d084e482f 100644 --- a/src/JSInterop/Mono.WebAssembly.Interop/src/Mono.WebAssembly.Interop.csproj +++ b/src/JSInterop/Mono.WebAssembly.Interop/src/Mono.WebAssembly.Interop.csproj @@ -2,7 +2,10 @@ netstandard2.0 - false + Abstractions and features for interop between Mono WebAssembly and JavaScript code. + wasm;javascript;interop + true + true From 2c0287d686dc7becb6aa20d097d78505e0280f8d Mon Sep 17 00:00:00 2001 From: marciomyst Date: Fri, 8 Feb 2019 15:35:13 -0200 Subject: [PATCH 050/183] Fix ignored exception parameter on Degraded method The 'exception' parameter was ignored and a hardcoded null value was used on HealthCheckResult.Degraded\n\nCommit migrated from https://github.com/dotnet/extensions/commit/dbfc2dee3a582b581f91386cff9a356fb111ca37 --- src/HealthChecks/Abstractions/src/HealthCheckResult.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HealthChecks/Abstractions/src/HealthCheckResult.cs b/src/HealthChecks/Abstractions/src/HealthCheckResult.cs index e01cb5aceba4..7f4522da19c9 100644 --- a/src/HealthChecks/Abstractions/src/HealthCheckResult.cs +++ b/src/HealthChecks/Abstractions/src/HealthCheckResult.cs @@ -70,7 +70,7 @@ public static HealthCheckResult Healthy(string description = null, IReadOnlyDict /// A representing a degraged component. public static HealthCheckResult Degraded(string description = null, Exception exception = null, IReadOnlyDictionary data = null) { - return new HealthCheckResult(status: HealthStatus.Degraded, description, exception: null, data); + return new HealthCheckResult(status: HealthStatus.Degraded, description, exception: exception, data); } /// From 39b6a19f90b13581717a662654dc5d5ded7bfa06 Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Mon, 11 Feb 2019 12:10:15 -0800 Subject: [PATCH 051/183] Add reference assembly generations support (dotnet/extensions#1093) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/8ad1395c9181c0f370ac8012760984541f557eee --- ...Extensions.Configuration.KeyPerFile.csproj | 12 +++ ...Configuration.KeyPerFile.netstandard2.0.cs | 29 ++++++ ...t.Extensions.FileProviders.Embedded.csproj | 11 +++ ...s.FileProviders.Embedded.netstandard2.0.cs | 39 ++++++++ ...agnostics.HealthChecks.Abstractions.csproj | 11 +++ ...ealthChecks.Abstractions.netstandard2.0.cs | 67 ++++++++++++++ ...Extensions.Diagnostics.HealthChecks.csproj | 13 +++ ...Diagnostics.HealthChecks.netstandard2.0.cs | 52 +++++++++++ .../ref/Microsoft.JSInterop.csproj | 11 +++ .../ref/Microsoft.JSInterop.netstandard2.0.cs | 76 ++++++++++++++++ .../ref/Mono.WebAssembly.Interop.csproj | 11 +++ ...Mono.WebAssembly.Interop.netstandard2.0.cs | 16 ++++ ...xtensions.Localization.Abstractions.csproj | 11 +++ ...ocalization.Abstractions.netstandard2.0.cs | 47 ++++++++++ .../Microsoft.Extensions.Localization.csproj | 14 +++ ....Extensions.Localization.netstandard2.0.cs | 91 +++++++++++++++++++ .../Microsoft.Extensions.ObjectPool.csproj | 11 +++ ...ft.Extensions.ObjectPool.netstandard2.0.cs | 72 +++++++++++++++ src/WebEncoders/Directory.Build.props | 8 ++ .../Microsoft.Extensions.WebEncoders.csproj | 13 +++ ...t.Extensions.WebEncoders.netstandard2.0.cs | 55 +++++++++++ .../Microsoft.Extensions.WebEncoders.csproj | 1 - 22 files changed, 670 insertions(+), 1 deletion(-) create mode 100644 src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj create mode 100644 src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.netstandard2.0.cs create mode 100644 src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.csproj create mode 100644 src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.netstandard2.0.cs create mode 100644 src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj create mode 100644 src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netstandard2.0.cs create mode 100644 src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj create mode 100644 src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netstandard2.0.cs create mode 100644 src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj create mode 100644 src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs create mode 100644 src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.csproj create mode 100644 src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.netstandard2.0.cs create mode 100644 src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.csproj create mode 100644 src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netstandard2.0.cs create mode 100644 src/Localization/Localization/ref/Microsoft.Extensions.Localization.csproj create mode 100644 src/Localization/Localization/ref/Microsoft.Extensions.Localization.netstandard2.0.cs create mode 100644 src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.csproj create mode 100644 src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.netstandard2.0.cs create mode 100644 src/WebEncoders/Directory.Build.props create mode 100644 src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj create mode 100644 src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.netstandard2.0.cs diff --git a/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj b/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj new file mode 100644 index 000000000000..3df73aba0c63 --- /dev/null +++ b/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj @@ -0,0 +1,12 @@ + + + + netstandard2.0 + false + + + + + + + diff --git a/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.netstandard2.0.cs b/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.netstandard2.0.cs new file mode 100644 index 000000000000..e26ca1909d18 --- /dev/null +++ b/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.netstandard2.0.cs @@ -0,0 +1,29 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.Extensions.Configuration +{ + public static partial class KeyPerFileConfigurationBuilderExtensions + { + public static Microsoft.Extensions.Configuration.IConfigurationBuilder AddKeyPerFile(this Microsoft.Extensions.Configuration.IConfigurationBuilder builder, System.Action configureSource) { throw null; } + public static Microsoft.Extensions.Configuration.IConfigurationBuilder AddKeyPerFile(this Microsoft.Extensions.Configuration.IConfigurationBuilder builder, string directoryPath, bool optional) { throw null; } + } +} +namespace Microsoft.Extensions.Configuration.KeyPerFile +{ + public partial class KeyPerFileConfigurationProvider : Microsoft.Extensions.Configuration.ConfigurationProvider + { + public KeyPerFileConfigurationProvider(Microsoft.Extensions.Configuration.KeyPerFile.KeyPerFileConfigurationSource source) { } + public override void Load() { } + public override string ToString() { throw null; } + } + public partial class KeyPerFileConfigurationSource : Microsoft.Extensions.Configuration.IConfigurationSource + { + public KeyPerFileConfigurationSource() { } + public Microsoft.Extensions.FileProviders.IFileProvider FileProvider { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public System.Func IgnoreCondition { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public string IgnorePrefix { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public bool Optional { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public Microsoft.Extensions.Configuration.IConfigurationProvider Build(Microsoft.Extensions.Configuration.IConfigurationBuilder builder) { throw null; } + } +} diff --git a/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.csproj b/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.csproj new file mode 100644 index 000000000000..ba6fddca2b35 --- /dev/null +++ b/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + false + + + + + + diff --git a/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.netstandard2.0.cs b/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.netstandard2.0.cs new file mode 100644 index 000000000000..1596f191fdd4 --- /dev/null +++ b/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.netstandard2.0.cs @@ -0,0 +1,39 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.Extensions.FileProviders +{ + public partial class EmbeddedFileProvider : Microsoft.Extensions.FileProviders.IFileProvider + { + public EmbeddedFileProvider(System.Reflection.Assembly assembly) { } + public EmbeddedFileProvider(System.Reflection.Assembly assembly, string baseNamespace) { } + public Microsoft.Extensions.FileProviders.IDirectoryContents GetDirectoryContents(string subpath) { throw null; } + public Microsoft.Extensions.FileProviders.IFileInfo GetFileInfo(string subpath) { throw null; } + public Microsoft.Extensions.Primitives.IChangeToken Watch(string pattern) { throw null; } + } + public partial class ManifestEmbeddedFileProvider : Microsoft.Extensions.FileProviders.IFileProvider + { + public ManifestEmbeddedFileProvider(System.Reflection.Assembly assembly) { } + public ManifestEmbeddedFileProvider(System.Reflection.Assembly assembly, string root) { } + public ManifestEmbeddedFileProvider(System.Reflection.Assembly assembly, string root, System.DateTimeOffset lastModified) { } + public ManifestEmbeddedFileProvider(System.Reflection.Assembly assembly, string root, string manifestName, System.DateTimeOffset lastModified) { } + public System.Reflection.Assembly Assembly { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public Microsoft.Extensions.FileProviders.IDirectoryContents GetDirectoryContents(string subpath) { throw null; } + public Microsoft.Extensions.FileProviders.IFileInfo GetFileInfo(string subpath) { throw null; } + public Microsoft.Extensions.Primitives.IChangeToken Watch(string filter) { throw null; } + } +} +namespace Microsoft.Extensions.FileProviders.Embedded +{ + public partial class EmbeddedResourceFileInfo : Microsoft.Extensions.FileProviders.IFileInfo + { + public EmbeddedResourceFileInfo(System.Reflection.Assembly assembly, string resourcePath, string name, System.DateTimeOffset lastModified) { } + public bool Exists { get { throw null; } } + public bool IsDirectory { get { throw null; } } + public System.DateTimeOffset LastModified { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public long Length { get { throw null; } } + public string Name { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string PhysicalPath { get { throw null; } } + public System.IO.Stream CreateReadStream() { throw null; } + } +} diff --git a/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj new file mode 100644 index 000000000000..201f8d515b8c --- /dev/null +++ b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + false + + + + + + diff --git a/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netstandard2.0.cs b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netstandard2.0.cs new file mode 100644 index 000000000000..c0117ef47366 --- /dev/null +++ b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netstandard2.0.cs @@ -0,0 +1,67 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.Extensions.Diagnostics.HealthChecks +{ + public sealed partial class HealthCheckContext + { + public HealthCheckContext() { } + public Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckRegistration Registration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + } + public sealed partial class HealthCheckRegistration + { + public HealthCheckRegistration(string name, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck instance, System.Nullable failureStatus, System.Collections.Generic.IEnumerable tags) { } + public HealthCheckRegistration(string name, System.Func factory, System.Nullable failureStatus, System.Collections.Generic.IEnumerable tags) { } + public System.Func Factory { get { throw null; } set { } } + public Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus FailureStatus { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public string Name { get { throw null; } set { } } + public System.Collections.Generic.ISet Tags { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] + public partial struct HealthCheckResult + { + private object _dummy; + private int _dummyPrimitive; + public HealthCheckResult(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus status, string description = null, System.Exception exception = null, System.Collections.Generic.IReadOnlyDictionary data = null) { throw null; } + public System.Collections.Generic.IReadOnlyDictionary Data { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string Description { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Exception Exception { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus Status { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public static Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult Degraded(string description = null, System.Exception exception = null, System.Collections.Generic.IReadOnlyDictionary data = null) { throw null; } + public static Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult Healthy(string description = null, System.Collections.Generic.IReadOnlyDictionary data = null) { throw null; } + public static Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult Unhealthy(string description = null, System.Exception exception = null, System.Collections.Generic.IReadOnlyDictionary data = null) { throw null; } + } + public sealed partial class HealthReport + { + public HealthReport(System.Collections.Generic.IReadOnlyDictionary entries, System.TimeSpan totalDuration) { } + public System.Collections.Generic.IReadOnlyDictionary Entries { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus Status { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.TimeSpan TotalDuration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] + public partial struct HealthReportEntry + { + private object _dummy; + private int _dummyPrimitive; + public HealthReportEntry(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus status, string description, System.TimeSpan duration, System.Exception exception, System.Collections.Generic.IReadOnlyDictionary data) { throw null; } + public System.Collections.Generic.IReadOnlyDictionary Data { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string Description { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.TimeSpan Duration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Exception Exception { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus Status { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } + public enum HealthStatus + { + Degraded = 1, + Healthy = 2, + Unhealthy = 0, + } + public partial interface IHealthCheck + { + System.Threading.Tasks.Task CheckHealthAsync(Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckContext context, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + } + public partial interface IHealthCheckPublisher + { + System.Threading.Tasks.Task PublishAsync(Microsoft.Extensions.Diagnostics.HealthChecks.HealthReport report, System.Threading.CancellationToken cancellationToken); + } +} diff --git a/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj b/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj new file mode 100644 index 000000000000..925e89cfa087 --- /dev/null +++ b/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj @@ -0,0 +1,13 @@ + + + + netstandard2.0 + false + + + + + + + + diff --git a/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netstandard2.0.cs b/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netstandard2.0.cs new file mode 100644 index 000000000000..89281584e2f4 --- /dev/null +++ b/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netstandard2.0.cs @@ -0,0 +1,52 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.Extensions.DependencyInjection +{ + public static partial class HealthChecksBuilderAddCheckExtensions + { + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck instance, System.Nullable failureStatus = default(System.Nullable), System.Collections.Generic.IEnumerable tags = null) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Nullable failureStatus = default(System.Nullable), System.Collections.Generic.IEnumerable tags = null) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddTypeActivatedCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Nullable failureStatus, System.Collections.Generic.IEnumerable tags, params object[] args) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddTypeActivatedCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Nullable failureStatus, params object[] args) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddTypeActivatedCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, params object[] args) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; } + } + public static partial class HealthChecksBuilderDelegateExtensions + { + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddAsyncCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func> check, System.Collections.Generic.IEnumerable tags = null) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddAsyncCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func> check, System.Collections.Generic.IEnumerable tags = null) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func check, System.Collections.Generic.IEnumerable tags = null) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func check, System.Collections.Generic.IEnumerable tags = null) { throw null; } + } + public static partial class HealthCheckServiceCollectionExtensions + { + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddHealthChecks(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) { throw null; } + } + public partial interface IHealthChecksBuilder + { + Microsoft.Extensions.DependencyInjection.IServiceCollection Services { get; } + Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder Add(Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckRegistration registration); + } +} +namespace Microsoft.Extensions.Diagnostics.HealthChecks +{ + public sealed partial class HealthCheckPublisherOptions + { + public HealthCheckPublisherOptions() { } + public System.TimeSpan Delay { get { throw null; } set { } } + public System.TimeSpan Period { get { throw null; } set { } } + public System.Func Predicate { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public System.TimeSpan Timeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + } + public abstract partial class HealthCheckService + { + protected HealthCheckService() { } + public abstract System.Threading.Tasks.Task CheckHealthAsync(System.Func predicate, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + public System.Threading.Tasks.Task CheckHealthAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + } + public sealed partial class HealthCheckServiceOptions + { + public HealthCheckServiceOptions() { } + public System.Collections.Generic.ICollection Registrations { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } +} diff --git a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj new file mode 100644 index 000000000000..a59b30a32e12 --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + false + + + + + + diff --git a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs new file mode 100644 index 000000000000..02146c7bab47 --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs @@ -0,0 +1,76 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.JSInterop +{ + public static partial class DotNetDispatcher + { + public static void BeginInvoke(string callId, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { } + [Microsoft.JSInterop.JSInvokableAttribute("DotNetDispatcher.EndInvoke")] + public static void EndInvoke(long asyncHandle, bool succeeded, Microsoft.JSInterop.Internal.JSAsyncCallResult result) { } + public static string Invoke(string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { throw null; } + [Microsoft.JSInterop.JSInvokableAttribute("DotNetDispatcher.ReleaseDotNetObject")] + public static void ReleaseDotNetObject(long dotNetObjectId) { } + } + public partial class DotNetObjectRef : System.IDisposable + { + public DotNetObjectRef(object value) { } + public object Value { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public void Dispose() { } + public void EnsureAttachedToJsRuntime(Microsoft.JSInterop.IJSRuntime runtime) { } + } + public partial interface IJSInProcessRuntime : Microsoft.JSInterop.IJSRuntime + { + T Invoke(string identifier, params object[] args); + } + public partial interface IJSRuntime + { + System.Threading.Tasks.Task InvokeAsync(string identifier, params object[] args); + void UntrackObjectRef(Microsoft.JSInterop.DotNetObjectRef dotNetObjectRef); + } + public partial class JSException : System.Exception + { + public JSException(string message) { } + } + public abstract partial class JSInProcessRuntimeBase : Microsoft.JSInterop.JSRuntimeBase, Microsoft.JSInterop.IJSInProcessRuntime, Microsoft.JSInterop.IJSRuntime + { + protected JSInProcessRuntimeBase() { } + protected abstract string InvokeJS(string identifier, string argsJson); + public T Invoke(string identifier, params object[] args) { throw null; } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Method, AllowMultiple=true)] + public partial class JSInvokableAttribute : System.Attribute + { + public JSInvokableAttribute() { } + public JSInvokableAttribute(string identifier) { } + public string Identifier { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } + public static partial class Json + { + public static T Deserialize(string json) { throw null; } + public static string Serialize(object value) { throw null; } + } + public static partial class JSRuntime + { + public static Microsoft.JSInterop.IJSRuntime Current { get { throw null; } } + public static void SetCurrentJSRuntime(Microsoft.JSInterop.IJSRuntime instance) { } + } + public abstract partial class JSRuntimeBase : Microsoft.JSInterop.IJSRuntime + { + public JSRuntimeBase() { } + protected abstract void BeginInvokeJS(long asyncHandle, string identifier, string argsJson); + public System.Threading.Tasks.Task InvokeAsync(string identifier, params object[] args) { throw null; } + public void UntrackObjectRef(Microsoft.JSInterop.DotNetObjectRef dotNetObjectRef) { } + } +} +namespace Microsoft.JSInterop.Internal +{ + public partial interface ICustomArgSerializer + { + object ToJsonPrimitive(); + } + public partial class JSAsyncCallResult + { + internal JSAsyncCallResult() { } + } +} diff --git a/src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.csproj b/src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.csproj new file mode 100644 index 000000000000..49f8622c830d --- /dev/null +++ b/src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + false + + + + + + diff --git a/src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.netstandard2.0.cs b/src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.netstandard2.0.cs new file mode 100644 index 000000000000..033e97237a0d --- /dev/null +++ b/src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.netstandard2.0.cs @@ -0,0 +1,16 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Mono.WebAssembly.Interop +{ + public partial class MonoWebAssemblyJSRuntime : Microsoft.JSInterop.JSInProcessRuntimeBase + { + public MonoWebAssemblyJSRuntime() { } + protected override void BeginInvokeJS(long asyncHandle, string identifier, string argsJson) { } + protected override string InvokeJS(string identifier, string argsJson) { throw null; } + public TRes InvokeUnmarshalled(string identifier) { throw null; } + public TRes InvokeUnmarshalled(string identifier, T0 arg0) { throw null; } + public TRes InvokeUnmarshalled(string identifier, T0 arg0, T1 arg1) { throw null; } + public TRes InvokeUnmarshalled(string identifier, T0 arg0, T1 arg1, T2 arg2) { throw null; } + } +} diff --git a/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.csproj b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.csproj new file mode 100644 index 000000000000..e526fd4fb84a --- /dev/null +++ b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + false + + + + + + diff --git a/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netstandard2.0.cs b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netstandard2.0.cs new file mode 100644 index 000000000000..b18cec35f880 --- /dev/null +++ b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netstandard2.0.cs @@ -0,0 +1,47 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.Extensions.Localization +{ + public partial interface IStringLocalizer + { + Microsoft.Extensions.Localization.LocalizedString this[string name] { get; } + Microsoft.Extensions.Localization.LocalizedString this[string name, params object[] arguments] { get; } + System.Collections.Generic.IEnumerable GetAllStrings(bool includeParentCultures); + Microsoft.Extensions.Localization.IStringLocalizer WithCulture(System.Globalization.CultureInfo culture); + } + public partial interface IStringLocalizerFactory + { + Microsoft.Extensions.Localization.IStringLocalizer Create(string baseName, string location); + Microsoft.Extensions.Localization.IStringLocalizer Create(System.Type resourceSource); + } + public partial interface IStringLocalizer : Microsoft.Extensions.Localization.IStringLocalizer + { + } + public partial class LocalizedString + { + public LocalizedString(string name, string value) { } + public LocalizedString(string name, string value, bool resourceNotFound) { } + public LocalizedString(string name, string value, bool resourceNotFound, string searchedLocation) { } + public string Name { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public bool ResourceNotFound { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string SearchedLocation { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string Value { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public static implicit operator string (Microsoft.Extensions.Localization.LocalizedString localizedString) { throw null; } + public override string ToString() { throw null; } + } + public static partial class StringLocalizerExtensions + { + public static System.Collections.Generic.IEnumerable GetAllStrings(this Microsoft.Extensions.Localization.IStringLocalizer stringLocalizer) { throw null; } + public static Microsoft.Extensions.Localization.LocalizedString GetString(this Microsoft.Extensions.Localization.IStringLocalizer stringLocalizer, string name) { throw null; } + public static Microsoft.Extensions.Localization.LocalizedString GetString(this Microsoft.Extensions.Localization.IStringLocalizer stringLocalizer, string name, params object[] arguments) { throw null; } + } + public partial class StringLocalizer : Microsoft.Extensions.Localization.IStringLocalizer, Microsoft.Extensions.Localization.IStringLocalizer + { + public StringLocalizer(Microsoft.Extensions.Localization.IStringLocalizerFactory factory) { } + public virtual Microsoft.Extensions.Localization.LocalizedString this[string name] { get { throw null; } } + public virtual Microsoft.Extensions.Localization.LocalizedString this[string name, params object[] arguments] { get { throw null; } } + public System.Collections.Generic.IEnumerable GetAllStrings(bool includeParentCultures) { throw null; } + public virtual Microsoft.Extensions.Localization.IStringLocalizer WithCulture(System.Globalization.CultureInfo culture) { throw null; } + } +} diff --git a/src/Localization/Localization/ref/Microsoft.Extensions.Localization.csproj b/src/Localization/Localization/ref/Microsoft.Extensions.Localization.csproj new file mode 100644 index 000000000000..d48c510f7d38 --- /dev/null +++ b/src/Localization/Localization/ref/Microsoft.Extensions.Localization.csproj @@ -0,0 +1,14 @@ + + + + netstandard2.0 + false + + + + + + + + + diff --git a/src/Localization/Localization/ref/Microsoft.Extensions.Localization.netstandard2.0.cs b/src/Localization/Localization/ref/Microsoft.Extensions.Localization.netstandard2.0.cs new file mode 100644 index 000000000000..7bbe1797a5b9 --- /dev/null +++ b/src/Localization/Localization/ref/Microsoft.Extensions.Localization.netstandard2.0.cs @@ -0,0 +1,91 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.Extensions.DependencyInjection +{ + public static partial class LocalizationServiceCollectionExtensions + { + public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddLocalization(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddLocalization(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action setupAction) { throw null; } + } +} +namespace Microsoft.Extensions.Localization +{ + public partial interface IResourceNamesCache + { + System.Collections.Generic.IList GetOrAdd(string name, System.Func> valueFactory); + } + public partial class LocalizationOptions + { + public LocalizationOptions() { } + public string ResourcesPath { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly, AllowMultiple=false, Inherited=false)] + public partial class ResourceLocationAttribute : System.Attribute + { + public ResourceLocationAttribute(string resourceLocation) { } + public string ResourceLocation { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } + public partial class ResourceManagerStringLocalizer : Microsoft.Extensions.Localization.IStringLocalizer + { + public ResourceManagerStringLocalizer(System.Resources.ResourceManager resourceManager, Microsoft.Extensions.Localization.Internal.AssemblyWrapper resourceAssemblyWrapper, string baseName, Microsoft.Extensions.Localization.IResourceNamesCache resourceNamesCache, Microsoft.Extensions.Logging.ILogger logger) { } + public ResourceManagerStringLocalizer(System.Resources.ResourceManager resourceManager, Microsoft.Extensions.Localization.Internal.IResourceStringProvider resourceStringProvider, string baseName, Microsoft.Extensions.Localization.IResourceNamesCache resourceNamesCache, Microsoft.Extensions.Logging.ILogger logger) { } + public ResourceManagerStringLocalizer(System.Resources.ResourceManager resourceManager, System.Reflection.Assembly resourceAssembly, string baseName, Microsoft.Extensions.Localization.IResourceNamesCache resourceNamesCache, Microsoft.Extensions.Logging.ILogger logger) { } + public virtual Microsoft.Extensions.Localization.LocalizedString this[string name] { get { throw null; } } + public virtual Microsoft.Extensions.Localization.LocalizedString this[string name, params object[] arguments] { get { throw null; } } + public virtual System.Collections.Generic.IEnumerable GetAllStrings(bool includeParentCultures) { throw null; } + protected System.Collections.Generic.IEnumerable GetAllStrings(bool includeParentCultures, System.Globalization.CultureInfo culture) { throw null; } + protected string GetStringSafely(string name, System.Globalization.CultureInfo culture) { throw null; } + public Microsoft.Extensions.Localization.IStringLocalizer WithCulture(System.Globalization.CultureInfo culture) { throw null; } + } + public partial class ResourceManagerStringLocalizerFactory : Microsoft.Extensions.Localization.IStringLocalizerFactory + { + public ResourceManagerStringLocalizerFactory(Microsoft.Extensions.Options.IOptions localizationOptions, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) { } + public Microsoft.Extensions.Localization.IStringLocalizer Create(string baseName, string location) { throw null; } + public Microsoft.Extensions.Localization.IStringLocalizer Create(System.Type resourceSource) { throw null; } + protected virtual Microsoft.Extensions.Localization.ResourceManagerStringLocalizer CreateResourceManagerStringLocalizer(System.Reflection.Assembly assembly, string baseName) { throw null; } + protected virtual Microsoft.Extensions.Localization.ResourceLocationAttribute GetResourceLocationAttribute(System.Reflection.Assembly assembly) { throw null; } + protected virtual string GetResourcePrefix(System.Reflection.TypeInfo typeInfo) { throw null; } + protected virtual string GetResourcePrefix(System.Reflection.TypeInfo typeInfo, string baseNamespace, string resourcesRelativePath) { throw null; } + protected virtual string GetResourcePrefix(string baseResourceName, string baseNamespace) { throw null; } + protected virtual string GetResourcePrefix(string location, string baseName, string resourceLocation) { throw null; } + protected virtual Microsoft.Extensions.Localization.RootNamespaceAttribute GetRootNamespaceAttribute(System.Reflection.Assembly assembly) { throw null; } + } + public partial class ResourceManagerWithCultureStringLocalizer : Microsoft.Extensions.Localization.ResourceManagerStringLocalizer + { + public ResourceManagerWithCultureStringLocalizer(System.Resources.ResourceManager resourceManager, System.Reflection.Assembly resourceAssembly, string baseName, Microsoft.Extensions.Localization.IResourceNamesCache resourceNamesCache, System.Globalization.CultureInfo culture, Microsoft.Extensions.Logging.ILogger logger) : base (default(System.Resources.ResourceManager), default(System.Reflection.Assembly), default(string), default(Microsoft.Extensions.Localization.IResourceNamesCache), default(Microsoft.Extensions.Logging.ILogger)) { } + public override Microsoft.Extensions.Localization.LocalizedString this[string name] { get { throw null; } } + public override Microsoft.Extensions.Localization.LocalizedString this[string name, params object[] arguments] { get { throw null; } } + public override System.Collections.Generic.IEnumerable GetAllStrings(bool includeParentCultures) { throw null; } + } + public partial class ResourceNamesCache : Microsoft.Extensions.Localization.IResourceNamesCache + { + public ResourceNamesCache() { } + public System.Collections.Generic.IList GetOrAdd(string name, System.Func> valueFactory) { throw null; } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly, AllowMultiple=false, Inherited=false)] + public partial class RootNamespaceAttribute : System.Attribute + { + public RootNamespaceAttribute(string rootNamespace) { } + public string RootNamespace { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } +} +namespace Microsoft.Extensions.Localization.Internal +{ + public partial class AssemblyWrapper + { + public AssemblyWrapper(System.Reflection.Assembly assembly) { } + public System.Reflection.Assembly Assembly { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public virtual string FullName { get { throw null; } } + public virtual System.IO.Stream GetManifestResourceStream(string name) { throw null; } + } + public partial interface IResourceStringProvider + { + System.Collections.Generic.IList GetAllResourceStrings(System.Globalization.CultureInfo culture, bool throwOnMissing); + } + public partial class ResourceManagerStringProvider : Microsoft.Extensions.Localization.Internal.IResourceStringProvider + { + public ResourceManagerStringProvider(Microsoft.Extensions.Localization.IResourceNamesCache resourceCache, System.Resources.ResourceManager resourceManager, System.Reflection.Assembly assembly, string baseName) { } + public System.Collections.Generic.IList GetAllResourceStrings(System.Globalization.CultureInfo culture, bool throwOnMissing) { throw null; } + } +} diff --git a/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.csproj b/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.csproj new file mode 100644 index 000000000000..8ae73841b15d --- /dev/null +++ b/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + false + + + + + + diff --git a/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.netstandard2.0.cs b/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.netstandard2.0.cs new file mode 100644 index 000000000000..16fd8379a649 --- /dev/null +++ b/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.netstandard2.0.cs @@ -0,0 +1,72 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.Extensions.ObjectPool +{ + public partial class DefaultObjectPoolProvider : Microsoft.Extensions.ObjectPool.ObjectPoolProvider + { + public DefaultObjectPoolProvider() { } + public int MaximumRetained { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public override Microsoft.Extensions.ObjectPool.ObjectPool Create(Microsoft.Extensions.ObjectPool.IPooledObjectPolicy policy) { throw null; } + } + public partial class DefaultObjectPool : Microsoft.Extensions.ObjectPool.ObjectPool where T : class + { + public DefaultObjectPool(Microsoft.Extensions.ObjectPool.IPooledObjectPolicy policy) { } + public DefaultObjectPool(Microsoft.Extensions.ObjectPool.IPooledObjectPolicy policy, int maximumRetained) { } + public override T Get() { throw null; } + public override void Return(T obj) { } + } + public partial class DefaultPooledObjectPolicy : Microsoft.Extensions.ObjectPool.PooledObjectPolicy where T : class, new() + { + public DefaultPooledObjectPolicy() { } + public override T Create() { throw null; } + public override bool Return(T obj) { throw null; } + } + public partial interface IPooledObjectPolicy + { + T Create(); + bool Return(T obj); + } + public partial class LeakTrackingObjectPoolProvider : Microsoft.Extensions.ObjectPool.ObjectPoolProvider + { + public LeakTrackingObjectPoolProvider(Microsoft.Extensions.ObjectPool.ObjectPoolProvider inner) { } + public override Microsoft.Extensions.ObjectPool.ObjectPool Create(Microsoft.Extensions.ObjectPool.IPooledObjectPolicy policy) { throw null; } + } + public partial class LeakTrackingObjectPool : Microsoft.Extensions.ObjectPool.ObjectPool where T : class + { + public LeakTrackingObjectPool(Microsoft.Extensions.ObjectPool.ObjectPool inner) { } + public override T Get() { throw null; } + public override void Return(T obj) { } + } + public abstract partial class ObjectPoolProvider + { + protected ObjectPoolProvider() { } + public Microsoft.Extensions.ObjectPool.ObjectPool Create() where T : class, new() { throw null; } + public abstract Microsoft.Extensions.ObjectPool.ObjectPool Create(Microsoft.Extensions.ObjectPool.IPooledObjectPolicy policy) where T : class; + } + public static partial class ObjectPoolProviderExtensions + { + public static Microsoft.Extensions.ObjectPool.ObjectPool CreateStringBuilderPool(this Microsoft.Extensions.ObjectPool.ObjectPoolProvider provider) { throw null; } + public static Microsoft.Extensions.ObjectPool.ObjectPool CreateStringBuilderPool(this Microsoft.Extensions.ObjectPool.ObjectPoolProvider provider, int initialCapacity, int maximumRetainedCapacity) { throw null; } + } + public abstract partial class ObjectPool where T : class + { + protected ObjectPool() { } + public abstract T Get(); + public abstract void Return(T obj); + } + public abstract partial class PooledObjectPolicy : Microsoft.Extensions.ObjectPool.IPooledObjectPolicy + { + protected PooledObjectPolicy() { } + public abstract T Create(); + public abstract bool Return(T obj); + } + public partial class StringBuilderPooledObjectPolicy : Microsoft.Extensions.ObjectPool.PooledObjectPolicy + { + public StringBuilderPooledObjectPolicy() { } + public int InitialCapacity { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public int MaximumRetainedCapacity { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public override System.Text.StringBuilder Create() { throw null; } + public override bool Return(System.Text.StringBuilder obj) { throw null; } + } +} diff --git a/src/WebEncoders/Directory.Build.props b/src/WebEncoders/Directory.Build.props new file mode 100644 index 000000000000..81557e1bcaf6 --- /dev/null +++ b/src/WebEncoders/Directory.Build.props @@ -0,0 +1,8 @@ + + + + + true + + + diff --git a/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj b/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj new file mode 100644 index 000000000000..d102a7d31b1b --- /dev/null +++ b/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj @@ -0,0 +1,13 @@ + + + + netstandard2.0 + false + + + + + + + + diff --git a/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.netstandard2.0.cs b/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.netstandard2.0.cs new file mode 100644 index 000000000000..18cdcbdfa352 --- /dev/null +++ b/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.netstandard2.0.cs @@ -0,0 +1,55 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.Extensions.DependencyInjection +{ + public static partial class EncoderServiceCollectionExtensions + { + public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddWebEncoders(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddWebEncoders(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action setupAction) { throw null; } + } +} +namespace Microsoft.Extensions.WebEncoders +{ + public sealed partial class WebEncoderOptions + { + public WebEncoderOptions() { } + public System.Text.Encodings.Web.TextEncoderSettings TextEncoderSettings { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + } +} +namespace Microsoft.Extensions.WebEncoders.Testing +{ + public sealed partial class HtmlTestEncoder : System.Text.Encodings.Web.HtmlEncoder + { + public HtmlTestEncoder() { } + public override int MaxOutputCharactersPerInputCharacter { get { throw null; } } + public override void Encode(System.IO.TextWriter output, char[] value, int startIndex, int characterCount) { } + public override void Encode(System.IO.TextWriter output, string value, int startIndex, int characterCount) { } + public override string Encode(string value) { throw null; } + public unsafe override int FindFirstCharacterToEncode(char* text, int textLength) { throw null; } + public unsafe override bool TryEncodeUnicodeScalar(int unicodeScalar, char* buffer, int bufferLength, out int numberOfCharactersWritten) { throw null; } + public override bool WillEncode(int unicodeScalar) { throw null; } + } + public partial class JavaScriptTestEncoder : System.Text.Encodings.Web.JavaScriptEncoder + { + public JavaScriptTestEncoder() { } + public override int MaxOutputCharactersPerInputCharacter { get { throw null; } } + public override void Encode(System.IO.TextWriter output, char[] value, int startIndex, int characterCount) { } + public override void Encode(System.IO.TextWriter output, string value, int startIndex, int characterCount) { } + public override string Encode(string value) { throw null; } + public unsafe override int FindFirstCharacterToEncode(char* text, int textLength) { throw null; } + public unsafe override bool TryEncodeUnicodeScalar(int unicodeScalar, char* buffer, int bufferLength, out int numberOfCharactersWritten) { throw null; } + public override bool WillEncode(int unicodeScalar) { throw null; } + } + public partial class UrlTestEncoder : System.Text.Encodings.Web.UrlEncoder + { + public UrlTestEncoder() { } + public override int MaxOutputCharactersPerInputCharacter { get { throw null; } } + public override void Encode(System.IO.TextWriter output, char[] value, int startIndex, int characterCount) { } + public override void Encode(System.IO.TextWriter output, string value, int startIndex, int characterCount) { } + public override string Encode(string value) { throw null; } + public unsafe override int FindFirstCharacterToEncode(char* text, int textLength) { throw null; } + public unsafe override bool TryEncodeUnicodeScalar(int unicodeScalar, char* buffer, int bufferLength, out int numberOfCharactersWritten) { throw null; } + public override bool WillEncode(int unicodeScalar) { throw null; } + } +} diff --git a/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj b/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj index dfc575c73fec..8f60f8f98305 100644 --- a/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj +++ b/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj @@ -4,7 +4,6 @@ Contains registration and configuration APIs to add the core framework encoders to a dependency injection container. netstandard2.0 $(NoWarn);CS1591 - true true aspnetcore true From dfb597732491b3d941b47a23dbeaf41c4eb07009 Mon Sep 17 00:00:00 2001 From: Glenn Condron Date: Mon, 4 Feb 2019 15:14:37 -0800 Subject: [PATCH 052/183] Update HealthCheckPublisherOptions.cs This seems wrong. Presumably fixes: https://github.com/aspnet/Extensions/issues/1041\n\nCommit migrated from https://github.com/dotnet/extensions/commit/b3c88d78fe112bc3b2e272299156857a383edf5d --- .../HealthChecks/src/HealthCheckPublisherOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HealthChecks/HealthChecks/src/HealthCheckPublisherOptions.cs b/src/HealthChecks/HealthChecks/src/HealthCheckPublisherOptions.cs index 1313718af874..6b7c8c3365b5 100644 --- a/src/HealthChecks/HealthChecks/src/HealthCheckPublisherOptions.cs +++ b/src/HealthChecks/HealthChecks/src/HealthCheckPublisherOptions.cs @@ -60,7 +60,7 @@ public TimeSpan Period throw new ArgumentException($"The {nameof(Period)} must not be infinite.", nameof(value)); } - _delay = value; + _period = value; } } From c960040b533ae026b66adceda72dd5203d2bc5dc Mon Sep 17 00:00:00 2001 From: Chris Ross Date: Fri, 15 Feb 2019 13:40:33 -0800 Subject: [PATCH 053/183] Deprecate and replace IHostingEnvronment & IApplicationLifetime (dotnet/extensions#1100) * Deprecate and replace IHostingEnvronment & IApplicationLifetime dotnet/extensions#966 * Fix startvs * Fix ref generation for obosolete\n\nCommit migrated from https://github.com/dotnet/extensions/commit/6991e2b0e8f7a133208d015dc7d0ca4192523ba0 --- .../ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj | 1 - .../ref/Microsoft.Extensions.FileProviders.Embedded.csproj | 1 - ...osoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj | 1 - .../ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj | 1 - src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj | 1 - .../Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.csproj | 1 - .../ref/Microsoft.Extensions.Localization.Abstractions.csproj | 1 - .../Localization/ref/Microsoft.Extensions.Localization.csproj | 1 - src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.csproj | 1 - src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj | 1 - 10 files changed, 10 deletions(-) diff --git a/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj b/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj index 3df73aba0c63..21f0053e59be 100644 --- a/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj +++ b/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj @@ -2,7 +2,6 @@ netstandard2.0 - false diff --git a/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.csproj b/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.csproj index ba6fddca2b35..b8f2f33387f4 100644 --- a/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.csproj +++ b/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.csproj @@ -2,7 +2,6 @@ netstandard2.0 - false diff --git a/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj index 201f8d515b8c..be2385895560 100644 --- a/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj +++ b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj @@ -2,7 +2,6 @@ netstandard2.0 - false diff --git a/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj b/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj index 925e89cfa087..277e60910fbf 100644 --- a/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj +++ b/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj @@ -2,7 +2,6 @@ netstandard2.0 - false diff --git a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj index a59b30a32e12..87fd913427e6 100644 --- a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj +++ b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj @@ -2,7 +2,6 @@ netstandard2.0 - false diff --git a/src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.csproj b/src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.csproj index 49f8622c830d..a1ba60570586 100644 --- a/src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.csproj +++ b/src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.csproj @@ -2,7 +2,6 @@ netstandard2.0 - false diff --git a/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.csproj b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.csproj index e526fd4fb84a..0608c06147cf 100644 --- a/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.csproj +++ b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.csproj @@ -2,7 +2,6 @@ netstandard2.0 - false diff --git a/src/Localization/Localization/ref/Microsoft.Extensions.Localization.csproj b/src/Localization/Localization/ref/Microsoft.Extensions.Localization.csproj index d48c510f7d38..d628c33a54db 100644 --- a/src/Localization/Localization/ref/Microsoft.Extensions.Localization.csproj +++ b/src/Localization/Localization/ref/Microsoft.Extensions.Localization.csproj @@ -2,7 +2,6 @@ netstandard2.0 - false diff --git a/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.csproj b/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.csproj index 8ae73841b15d..1fbb81a9ca62 100644 --- a/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.csproj +++ b/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.csproj @@ -2,7 +2,6 @@ netstandard2.0 - false diff --git a/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj b/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj index d102a7d31b1b..9dea660cb042 100644 --- a/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj +++ b/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj @@ -2,7 +2,6 @@ netstandard2.0 - false From b9912fee6b8082459fc33788c50916316944a2f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Sat, 19 Jan 2019 20:00:52 +0100 Subject: [PATCH 054/183] DisposableObjectPool added \n\nCommit migrated from https://github.com/dotnet/extensions/commit/6bcb1aee83d64225b24416b7c5e8d78f204ae2f0 --- src/ObjectPool/src/DefaultObjectPool.cs | 30 +++++-- src/ObjectPool/src/DisposableObjectPool.cs | 70 ++++++++++++++++ .../test/DisposableObjectPoolTest.cs | 83 +++++++++++++++++++ 3 files changed, 175 insertions(+), 8 deletions(-) create mode 100644 src/ObjectPool/src/DisposableObjectPool.cs create mode 100644 src/ObjectPool/test/DisposableObjectPoolTest.cs diff --git a/src/ObjectPool/src/DefaultObjectPool.cs b/src/ObjectPool/src/DefaultObjectPool.cs index dcd7f1c7152c..6ecc9231402d 100644 --- a/src/ObjectPool/src/DefaultObjectPool.cs +++ b/src/ObjectPool/src/DefaultObjectPool.cs @@ -10,10 +10,10 @@ namespace Microsoft.Extensions.ObjectPool { public class DefaultObjectPool : ObjectPool where T : class { - private readonly ObjectWrapper[] _items; + protected readonly ObjectWrapper[] _items; private readonly IPooledObjectPolicy _policy; private readonly bool _isDefaultPolicy; - private T _firstItem; + protected T _firstItem; // This class was introduced in 2.1 to avoid the interface call where possible private readonly PooledObjectPolicy _fastPolicy; @@ -71,29 +71,43 @@ private T GetViaScan() [MethodImpl(MethodImplOptions.NoInlining)] private T Create() => _fastPolicy?.Create() ?? _policy.Create(); - public override void Return(T obj) + public override void Return(T obj) => ReturnCore(obj); + + protected bool ReturnCore(T obj) { if (_isDefaultPolicy || (_fastPolicy?.Return(obj) ?? _policy.Return(obj))) { - if (_firstItem != null || Interlocked.CompareExchange(ref _firstItem, obj, null) != null) + if (_firstItem == null && Interlocked.CompareExchange(ref _firstItem, obj, null) == null) + { + return true; // returned to pool + } + else { - ReturnViaScan(obj); + return ReturnViaScan(obj); } } + + return false; // not returned to pool } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ReturnViaScan(T obj) + private bool ReturnViaScan(T obj) { var items = _items; - for (var i = 0; i < items.Length && Interlocked.CompareExchange(ref items[i].Element, obj, null) != null; ++i) + for (var i = 0; i < items.Length; i++) { + if (Interlocked.CompareExchange(ref items[i].Element, obj, null) == null) + { + return true; + } } + + return false; } // PERF: the struct wrapper avoids array-covariance-checks from the runtime when assigning to elements of the array. [DebuggerDisplay("{Element}")] - private struct ObjectWrapper + protected struct ObjectWrapper { public T Element; } diff --git a/src/ObjectPool/src/DisposableObjectPool.cs b/src/ObjectPool/src/DisposableObjectPool.cs new file mode 100644 index 000000000000..ca9288a6d689 --- /dev/null +++ b/src/ObjectPool/src/DisposableObjectPool.cs @@ -0,0 +1,70 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Threading; + +namespace Microsoft.Extensions.ObjectPool +{ + public class DisposableObjectPool : DefaultObjectPool, IDisposable where T : class + { + private volatile bool _isDisposed; + + public DisposableObjectPool(IPooledObjectPolicy policy) + : base(policy) + { + } + + public DisposableObjectPool(IPooledObjectPolicy policy, int maximumRetained) + : base(policy, maximumRetained) + { + } + + public override T Get() + { + if (_isDisposed) + { + ThrowObjectDisposedException(); + } + + return base.Get(); + + void ThrowObjectDisposedException() + { + throw new ObjectDisposedException(this.GetType().Name); + } + } + + public override void Return(T obj) + { + // When the pool is disposed or the obj is not returned to the pool, dispose it + if (_isDisposed || !ReturnCore(obj)) + { + DisposeItem(obj); + } + } + + public void Dispose() + { + DisposeItem(_firstItem); + _firstItem = null; + + ObjectWrapper[] items = _items; + for (var i = 0; i < items.Length; i++) + { + DisposeItem(items[i].Element); + items[i].Element = null; + } + + _isDisposed = true; + } + + private void DisposeItem(T item) + { + if (item is IDisposable disposable) + { + disposable.Dispose(); + } + } + } +} diff --git a/src/ObjectPool/test/DisposableObjectPoolTest.cs b/src/ObjectPool/test/DisposableObjectPoolTest.cs new file mode 100644 index 000000000000..4bc9142efb94 --- /dev/null +++ b/src/ObjectPool/test/DisposableObjectPoolTest.cs @@ -0,0 +1,83 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Xunit; + +namespace Microsoft.Extensions.ObjectPool +{ + public class DisposableObjectPoolTest + { + [Fact] + public void DisposableObjectPoolWithOneElement_Dispose_ObjectDisposed() + { + // Arrange + var pool = new DisposableObjectPool(new DefaultPooledObjectPolicy()); + var obj = pool.Get(); + pool.Return(obj); + + // Act + pool.Dispose(); + + // Assert + Assert.True(obj.IsDisposed); + } + + [Fact] + public void DisposableObjectPoolWithTwoElements_Dispose_ObjectsDisposed() + { + // Arrange + var pool = new DisposableObjectPool(new DefaultPooledObjectPolicy()); + var obj1 = pool.Get(); + var obj2 = pool.Get(); + pool.Return(obj1); + pool.Return(obj2); + + // Act + pool.Dispose(); + + // Assert + Assert.True(obj1.IsDisposed); + Assert.True(obj2.IsDisposed); + } + + [Fact] + public void DisposableObjectPool_DisposeAndGet_ThrowsObjectDisposed() + { + // Arrange + var pool = new DisposableObjectPool(new DefaultPooledObjectPolicy()); + var obj1 = pool.Get(); + var obj2 = pool.Get(); + pool.Return(obj1); + pool.Return(obj2); + + // Act + pool.Dispose(); + + // Assert + Assert.Throws(() => pool.Get()); + } + + [Fact] + public void DisposableObjectPool_DisposeAndReturn_DisposesObject() + { + // Arrange + var pool = new DisposableObjectPool(new DefaultPooledObjectPolicy()); + var obj = pool.Get(); + + // Act + pool.Dispose(); + pool.Return(obj); + + // Assert + Assert.True(obj.IsDisposed); + } + + private class DisposableObject : IDisposable + { + public bool IsDisposed { get; private set; } + + public void Dispose() => IsDisposed = true; + } + } +} From 033b118a9623873fddb3b8a6beac7ef03462d695 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Sat, 19 Jan 2019 20:16:41 +0100 Subject: [PATCH 055/183] DefaultObjectPoolProvider chooses on T and added factory method to ObjectPool \n\nCommit migrated from https://github.com/dotnet/extensions/commit/17883d97a9fc1d8bd5e42a126f70338458dd95b8 --- .../src/DefaultObjectPoolProvider.cs | 5 +++ src/ObjectPool/src/ObjectPool.cs | 9 ++++ .../test/DefaultObjectPoolProviderTest.cs | 44 +++++++++++++++++++ 3 files changed, 58 insertions(+) create mode 100644 src/ObjectPool/test/DefaultObjectPoolProviderTest.cs diff --git a/src/ObjectPool/src/DefaultObjectPoolProvider.cs b/src/ObjectPool/src/DefaultObjectPoolProvider.cs index fb3c4bfa7ed8..cedacaedbde8 100644 --- a/src/ObjectPool/src/DefaultObjectPoolProvider.cs +++ b/src/ObjectPool/src/DefaultObjectPoolProvider.cs @@ -11,6 +11,11 @@ public class DefaultObjectPoolProvider : ObjectPoolProvider public override ObjectPool Create(IPooledObjectPolicy policy) { + if (typeof(IDisposable).IsAssignableFrom(typeof(T))) + { + return new DisposableObjectPool(policy, MaximumRetained); + } + return new DefaultObjectPool(policy, MaximumRetained); } } diff --git a/src/ObjectPool/src/ObjectPool.cs b/src/ObjectPool/src/ObjectPool.cs index 8cf52c919595..691beae60cf9 100644 --- a/src/ObjectPool/src/ObjectPool.cs +++ b/src/ObjectPool/src/ObjectPool.cs @@ -9,4 +9,13 @@ public abstract class ObjectPool where T : class public abstract void Return(T obj); } + + public static class ObjectPool + { + public static ObjectPool Create(IPooledObjectPolicy policy = null) where T : class, new() + { + var provider = new DefaultObjectPoolProvider(); + return provider.Create(policy ?? new DefaultPooledObjectPolicy()); + } + } } diff --git a/src/ObjectPool/test/DefaultObjectPoolProviderTest.cs b/src/ObjectPool/test/DefaultObjectPoolProviderTest.cs new file mode 100644 index 000000000000..7096b60b3452 --- /dev/null +++ b/src/ObjectPool/test/DefaultObjectPoolProviderTest.cs @@ -0,0 +1,44 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Xunit; + +namespace Microsoft.Extensions.ObjectPool +{ + public class DefaultObjectPoolProviderTest + { + [Fact] + public void DefaultObjectPoolProvider_CreateForObject_DefaultObjectPoolReturned() + { + // Arrange + var provider = new DefaultObjectPoolProvider(); + + // Act + var pool = provider.Create(); + + // Assert + Assert.IsType>(pool); + } + + [Fact] + public void DefaultObjectPoolProvider_CreateForIDisposable_DisposableObjectPoolReturned() + { + // Arrange + var provider = new DefaultObjectPoolProvider(); + + // Act + var pool = provider.Create(); + + // Assert + Assert.IsType>(pool); + } + + private class DisposableObject : IDisposable + { + public bool IsDisposed { get; private set; } + + public void Dispose() => IsDisposed = true; + } + } +} From 5e19936a9f1a1e6d46878753bc7a8ca14001e125 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Sun, 20 Jan 2019 21:01:59 +0100 Subject: [PATCH 056/183] PR Feedback * DefaultObjectPool left unchanged (expect access modifiers) + manual inlining ob scan-methods * DisposableObjectPool made internal and it handles Return self, thus keeping the DefaultObjectPool fast \n\nCommit migrated from https://github.com/dotnet/extensions/commit/a7dc496507e10fbf56e4b76a156b4d0a0f22bcba --- src/ObjectPool/src/DefaultObjectPool.cs | 70 ++++++------------- .../src/DefaultObjectPoolProvider.cs | 5 ++ src/ObjectPool/src/DisposableObjectPool.cs | 31 ++++++-- src/ObjectPool/src/Properties/AssemblyInfo.cs | 3 + .../test/DisposableObjectPoolTest.cs | 62 ++++++++++++++++ src/ObjectPool/test/ThreadingTest.cs | 18 ++++- 6 files changed, 134 insertions(+), 55 deletions(-) create mode 100644 src/ObjectPool/src/Properties/AssemblyInfo.cs diff --git a/src/ObjectPool/src/DefaultObjectPool.cs b/src/ObjectPool/src/DefaultObjectPool.cs index 6ecc9231402d..070508a014b4 100644 --- a/src/ObjectPool/src/DefaultObjectPool.cs +++ b/src/ObjectPool/src/DefaultObjectPool.cs @@ -10,13 +10,13 @@ namespace Microsoft.Extensions.ObjectPool { public class DefaultObjectPool : ObjectPool where T : class { - protected readonly ObjectWrapper[] _items; - private readonly IPooledObjectPolicy _policy; - private readonly bool _isDefaultPolicy; - protected T _firstItem; + private protected readonly ObjectWrapper[] _items; + private protected readonly IPooledObjectPolicy _policy; + private protected readonly bool _isDefaultPolicy; + private protected T _firstItem; // This class was introduced in 2.1 to avoid the interface call where possible - private readonly PooledObjectPolicy _fastPolicy; + private protected readonly PooledObjectPolicy _fastPolicy; public DefaultObjectPool(IPooledObjectPolicy policy) : this(policy, Environment.ProcessorCount * 2) @@ -45,69 +45,43 @@ public override T Get() var item = _firstItem; if (item == null || Interlocked.CompareExchange(ref _firstItem, null, item) != item) { - item = GetViaScan(); - } - - return item; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private T GetViaScan() - { - var items = _items; - for (var i = 0; i < items.Length; i++) - { - var item = items[i].Element; - if (item != null && Interlocked.CompareExchange(ref items[i].Element, null, item) == item) + var items = _items; + for (var i = 0; i < items.Length; i++) { - return item; + item = items[i].Element; + if (item != null && Interlocked.CompareExchange(ref items[i].Element, null, item) == item) + { + return item; + } } + + item = Create(); } - return Create(); + return item; } // Non-inline to improve its code quality as uncommon path [MethodImpl(MethodImplOptions.NoInlining)] private T Create() => _fastPolicy?.Create() ?? _policy.Create(); - public override void Return(T obj) => ReturnCore(obj); - - protected bool ReturnCore(T obj) + public override void Return(T obj) { if (_isDefaultPolicy || (_fastPolicy?.Return(obj) ?? _policy.Return(obj))) { - if (_firstItem == null && Interlocked.CompareExchange(ref _firstItem, obj, null) == null) + if (_firstItem != null || Interlocked.CompareExchange(ref _firstItem, obj, null) != null) { - return true; // returned to pool - } - else - { - return ReturnViaScan(obj); - } - } - - return false; // not returned to pool - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private bool ReturnViaScan(T obj) - { - var items = _items; - for (var i = 0; i < items.Length; i++) - { - if (Interlocked.CompareExchange(ref items[i].Element, obj, null) == null) - { - return true; + var items = _items; + for (var i = 0; i < items.Length && Interlocked.CompareExchange(ref items[i].Element, obj, null) != null; ++i) + { + } } } - - return false; } // PERF: the struct wrapper avoids array-covariance-checks from the runtime when assigning to elements of the array. [DebuggerDisplay("{Element}")] - protected struct ObjectWrapper + private protected struct ObjectWrapper { public T Element; } diff --git a/src/ObjectPool/src/DefaultObjectPoolProvider.cs b/src/ObjectPool/src/DefaultObjectPoolProvider.cs index cedacaedbde8..2e7767ab356d 100644 --- a/src/ObjectPool/src/DefaultObjectPoolProvider.cs +++ b/src/ObjectPool/src/DefaultObjectPoolProvider.cs @@ -11,6 +11,11 @@ public class DefaultObjectPoolProvider : ObjectPoolProvider public override ObjectPool Create(IPooledObjectPolicy policy) { + if (policy == null) + { + throw new ArgumentNullException(nameof(policy)); + } + if (typeof(IDisposable).IsAssignableFrom(typeof(T))) { return new DisposableObjectPool(policy, MaximumRetained); diff --git a/src/ObjectPool/src/DisposableObjectPool.cs b/src/ObjectPool/src/DisposableObjectPool.cs index ca9288a6d689..17ada443e508 100644 --- a/src/ObjectPool/src/DisposableObjectPool.cs +++ b/src/ObjectPool/src/DisposableObjectPool.cs @@ -2,11 +2,12 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Runtime.CompilerServices; using System.Threading; namespace Microsoft.Extensions.ObjectPool { - public class DisposableObjectPool : DefaultObjectPool, IDisposable where T : class + internal sealed class DisposableObjectPool : DefaultObjectPool, IDisposable where T : class { private volatile bool _isDisposed; @@ -31,7 +32,7 @@ public override T Get() void ThrowObjectDisposedException() { - throw new ObjectDisposedException(this.GetType().Name); + throw new ObjectDisposedException(GetType().Name); } } @@ -44,8 +45,32 @@ public override void Return(T obj) } } + private bool ReturnCore(T obj) + { + bool returnedTooPool = false; + + if (_isDefaultPolicy || (_fastPolicy?.Return(obj) ?? _policy.Return(obj))) + { + if (_firstItem == null && Interlocked.CompareExchange(ref _firstItem, obj, null) == null) + { + returnedTooPool = true; + } + else + { + var items = _items; + for (var i = 0; i < items.Length && !(returnedTooPool = Interlocked.CompareExchange(ref items[i].Element, obj, null) == null); i++) + { + } + } + } + + return returnedTooPool; + } + public void Dispose() { + _isDisposed = true; + DisposeItem(_firstItem); _firstItem = null; @@ -55,8 +80,6 @@ public void Dispose() DisposeItem(items[i].Element); items[i].Element = null; } - - _isDisposed = true; } private void DisposeItem(T item) diff --git a/src/ObjectPool/src/Properties/AssemblyInfo.cs b/src/ObjectPool/src/Properties/AssemblyInfo.cs new file mode 100644 index 000000000000..121f8990b1cc --- /dev/null +++ b/src/ObjectPool/src/Properties/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Microsoft.Extensions.ObjectPool.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] diff --git a/src/ObjectPool/test/DisposableObjectPoolTest.cs b/src/ObjectPool/test/DisposableObjectPoolTest.cs index 4bc9142efb94..3bcefbaf66a1 100644 --- a/src/ObjectPool/test/DisposableObjectPoolTest.cs +++ b/src/ObjectPool/test/DisposableObjectPoolTest.cs @@ -2,12 +2,61 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Generic; using Xunit; namespace Microsoft.Extensions.ObjectPool { public class DisposableObjectPoolTest { + [Fact] + public void DisposableObjectPoolWithDefaultPolicy_GetAnd_ReturnObject_SameInstance() + { + // Arrange + var pool = new DisposableObjectPool(new DefaultPooledObjectPolicy()); + + var obj1 = pool.Get(); + pool.Return(obj1); + + // Act + var obj2 = pool.Get(); + + // Assert + Assert.Same(obj1, obj2); + } + + [Fact] + public void DisposableObjectPool_GetAndReturnObject_SameInstance() + { + // Arrange + var pool = new DisposableObjectPool>(new ListPolicy()); + + var list1 = pool.Get(); + pool.Return(list1); + + // Act + var list2 = pool.Get(); + + // Assert + Assert.Same(list1, list2); + } + + [Fact] + public void DisposableObjectPool_Return_RejectedByPolicy() + { + // Arrange + var pool = new DisposableObjectPool>(new ListPolicy()); + var list1 = pool.Get(); + list1.Capacity = 20; + + // Act + pool.Return(list1); + var list2 = pool.Get(); + + // Assert + Assert.NotSame(list1, list2); + } + [Fact] public void DisposableObjectPoolWithOneElement_Dispose_ObjectDisposed() { @@ -73,6 +122,19 @@ public void DisposableObjectPool_DisposeAndReturn_DisposesObject() Assert.True(obj.IsDisposed); } + private class ListPolicy : IPooledObjectPolicy> + { + public List Create() + { + return new List(17); + } + + public bool Return(List obj) + { + return obj.Capacity == 17; + } + } + private class DisposableObject : IDisposable { public bool IsDisposed { get; private set; } diff --git a/src/ObjectPool/test/ThreadingTest.cs b/src/ObjectPool/test/ThreadingTest.cs index 541bc5ffd41d..dbab7a530133 100644 --- a/src/ObjectPool/test/ThreadingTest.cs +++ b/src/ObjectPool/test/ThreadingTest.cs @@ -13,10 +13,22 @@ public class ThreadingTest private bool _foundError; [Fact] - public void RunThreadingTest() + public void DefaultObjectPool_RunThreadingTest() { - _cts = new CancellationTokenSource(); _pool = new DefaultObjectPool(new DefaultPooledObjectPolicy(), 10); + RunThreadingTest(); + } + + [Fact] + public void DisposableObjectPool_RunThreadingTest() + { + _pool = new DisposableObjectPool(new DefaultPooledObjectPolicy(), 10); + RunThreadingTest(); + } + + private void RunThreadingTest() + { + _cts = new CancellationTokenSource(); var threads = new Thread[8]; for (var i = 0; i < threads.Length; i++) @@ -66,7 +78,7 @@ private void Run() _pool.Return(obj2); } } - + private class Item { public int i = 0; From 491cbdd1194b8006a611272f1e9df7f56461e9d8 Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Sun, 17 Feb 2019 12:44:35 -0800 Subject: [PATCH 057/183] Update ObjectPool reference assemblies \n\nCommit migrated from https://github.com/dotnet/extensions/commit/f13113c06bea25f2d9ab6daae6f5b12ebaf74cdf --- .../ref/Microsoft.Extensions.ObjectPool.netstandard2.0.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.netstandard2.0.cs b/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.netstandard2.0.cs index 16fd8379a649..083aaf14efe4 100644 --- a/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.netstandard2.0.cs +++ b/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.netstandard2.0.cs @@ -38,6 +38,10 @@ public LeakTrackingObjectPool(Microsoft.Extensions.ObjectPool.ObjectPool inne public override T Get() { throw null; } public override void Return(T obj) { } } + public static partial class ObjectPool + { + public static Microsoft.Extensions.ObjectPool.ObjectPool Create(Microsoft.Extensions.ObjectPool.IPooledObjectPolicy policy = null) where T : class, new() { throw null; } + } public abstract partial class ObjectPoolProvider { protected ObjectPoolProvider() { } From a4cd6152b674aabcfecf392ff3e1bcb580b7163b Mon Sep 17 00:00:00 2001 From: Pranav K Date: Tue, 19 Feb 2019 13:01:13 -0800 Subject: [PATCH 058/183] Make JSRuntime.Current non-public (dotnet/extensions#1118) Fixes https://github.com/aspnet/AspNetCore/issues/6828\n\nCommit migrated from https://github.com/dotnet/extensions/commit/c1d8be8b980de62a299958e1816f06a81fea07bb --- .../ref/Microsoft.JSInterop.netstandard2.0.cs | 1 - src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs | 8 ++------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs index 02146c7bab47..95b9b7956c51 100644 --- a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs +++ b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs @@ -52,7 +52,6 @@ public static partial class Json } public static partial class JSRuntime { - public static Microsoft.JSInterop.IJSRuntime Current { get { throw null; } } public static void SetCurrentJSRuntime(Microsoft.JSInterop.IJSRuntime instance) { } } public abstract partial class JSRuntimeBase : Microsoft.JSInterop.IJSRuntime diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs b/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs index 5a9830fa3526..ae097ca68e87 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs @@ -11,13 +11,9 @@ namespace Microsoft.JSInterop /// public static class JSRuntime { - private static AsyncLocal _currentJSRuntime - = new AsyncLocal(); + private static readonly AsyncLocal _currentJSRuntime = new AsyncLocal(); - /// - /// Gets the current , if any. - /// - public static IJSRuntime Current => _currentJSRuntime.Value; + internal static IJSRuntime Current => _currentJSRuntime.Value; /// /// Sets the current JS runtime to the supplied instance. From ffd47e1bd84cc405de8a0d4eacc398840a07678c Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Tue, 19 Feb 2019 17:25:53 -0800 Subject: [PATCH 059/183] Manualy update tooling dependencies to normalize names (dotnet/extensions#1135) * Manualy update tooling dependencies to normalize names * Nullable \n\nCommit migrated from https://github.com/dotnet/extensions/commit/77db8d2b6bcf2d3b655a1d4852febf0fb47cca82 --- ...iagnostics.HealthChecks.Abstractions.netstandard2.0.cs | 4 ++-- ....Extensions.Diagnostics.HealthChecks.netstandard2.0.cs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netstandard2.0.cs b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netstandard2.0.cs index c0117ef47366..7fb5beb0e988 100644 --- a/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netstandard2.0.cs +++ b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netstandard2.0.cs @@ -10,8 +10,8 @@ public HealthCheckContext() { } } public sealed partial class HealthCheckRegistration { - public HealthCheckRegistration(string name, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck instance, System.Nullable failureStatus, System.Collections.Generic.IEnumerable tags) { } - public HealthCheckRegistration(string name, System.Func factory, System.Nullable failureStatus, System.Collections.Generic.IEnumerable tags) { } + public HealthCheckRegistration(string name, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck instance, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable tags) { } + public HealthCheckRegistration(string name, System.Func factory, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable tags) { } public System.Func Factory { get { throw null; } set { } } public Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus FailureStatus { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public string Name { get { throw null; } set { } } diff --git a/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netstandard2.0.cs b/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netstandard2.0.cs index 89281584e2f4..58bdc806029e 100644 --- a/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netstandard2.0.cs +++ b/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netstandard2.0.cs @@ -5,10 +5,10 @@ namespace Microsoft.Extensions.DependencyInjection { public static partial class HealthChecksBuilderAddCheckExtensions { - public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck instance, System.Nullable failureStatus = default(System.Nullable), System.Collections.Generic.IEnumerable tags = null) { throw null; } - public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Nullable failureStatus = default(System.Nullable), System.Collections.Generic.IEnumerable tags = null) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; } - public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddTypeActivatedCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Nullable failureStatus, System.Collections.Generic.IEnumerable tags, params object[] args) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; } - public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddTypeActivatedCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Nullable failureStatus, params object[] args) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck instance, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus = default(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus?), System.Collections.Generic.IEnumerable tags = null) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus = default(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus?), System.Collections.Generic.IEnumerable tags = null) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddTypeActivatedCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable tags, params object[] args) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddTypeActivatedCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, params object[] args) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; } public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddTypeActivatedCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, params object[] args) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; } } public static partial class HealthChecksBuilderDelegateExtensions From 58ea57e63f33e15049676b62beb595d951b004d1 Mon Sep 17 00:00:00 2001 From: Ryan Brandenburg Date: Wed, 20 Feb 2019 11:43:55 -0800 Subject: [PATCH 060/183] Obsolete ResourceManagerWithCultureStringLocalizer and WithCulture (dotnet/extensions#1133) Obsolete ResourceManagerWithCultureStringLocalizer and WithCulture \n\nCommit migrated from https://github.com/dotnet/extensions/commit/924015e98bc443023a6b0eea2c0016b876e4051f --- ...Extensions.Localization.Abstractions.netstandard2.0.cs | 2 ++ src/Localization/Abstractions/src/IStringLocalizer.cs | 8 +++++--- src/Localization/Abstractions/src/StringLocalizerOfT.cs | 7 ++++--- .../Microsoft.Extensions.Localization.netstandard2.0.cs | 2 ++ .../Localization/src/ResourceManagerStringLocalizer.cs | 7 ++++--- .../src/ResourceManagerWithCultureStringLocalizer.cs | 7 ++++--- .../test/ResourceManagerStringLocalizerTest.cs | 5 ++--- 7 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netstandard2.0.cs b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netstandard2.0.cs index b18cec35f880..3f9f422021a4 100644 --- a/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netstandard2.0.cs +++ b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netstandard2.0.cs @@ -8,6 +8,7 @@ public partial interface IStringLocalizer Microsoft.Extensions.Localization.LocalizedString this[string name] { get; } Microsoft.Extensions.Localization.LocalizedString this[string name, params object[] arguments] { get; } System.Collections.Generic.IEnumerable GetAllStrings(bool includeParentCultures); + [System.ObsoleteAttribute("This method is obsolete. Use `CurrentCulture` and `CurrentUICulture` instead.")] Microsoft.Extensions.Localization.IStringLocalizer WithCulture(System.Globalization.CultureInfo culture); } public partial interface IStringLocalizerFactory @@ -42,6 +43,7 @@ public StringLocalizer(Microsoft.Extensions.Localization.IStringLocalizerFactory public virtual Microsoft.Extensions.Localization.LocalizedString this[string name] { get { throw null; } } public virtual Microsoft.Extensions.Localization.LocalizedString this[string name, params object[] arguments] { get { throw null; } } public System.Collections.Generic.IEnumerable GetAllStrings(bool includeParentCultures) { throw null; } + [System.ObsoleteAttribute("This method is obsolete. Use `CurrentCulture` and `CurrentUICulture` instead.")] public virtual Microsoft.Extensions.Localization.IStringLocalizer WithCulture(System.Globalization.CultureInfo culture) { throw null; } } } diff --git a/src/Localization/Abstractions/src/IStringLocalizer.cs b/src/Localization/Abstractions/src/IStringLocalizer.cs index 0e1145bbcab0..cabdf434ac4d 100644 --- a/src/Localization/Abstractions/src/IStringLocalizer.cs +++ b/src/Localization/Abstractions/src/IStringLocalizer.cs @@ -1,6 +1,7 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Collections.Generic; using System.Globalization; @@ -40,6 +41,7 @@ public interface IStringLocalizer /// /// The to use. /// A culture-specific . + [Obsolete("This method is obsolete. Use `CurrentCulture` and `CurrentUICulture` instead.")] IStringLocalizer WithCulture(CultureInfo culture); } -} \ No newline at end of file +} diff --git a/src/Localization/Abstractions/src/StringLocalizerOfT.cs b/src/Localization/Abstractions/src/StringLocalizerOfT.cs index 131c1126ec3d..4190ca14ffca 100644 --- a/src/Localization/Abstractions/src/StringLocalizerOfT.cs +++ b/src/Localization/Abstractions/src/StringLocalizerOfT.cs @@ -1,5 +1,5 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; @@ -30,6 +30,7 @@ public StringLocalizer(IStringLocalizerFactory factory) } /// + [Obsolete("This method is obsolete. Use `CurrentCulture` and `CurrentUICulture` instead.")] public virtual IStringLocalizer WithCulture(CultureInfo culture) => _localizer.WithCulture(culture); /// @@ -64,4 +65,4 @@ public virtual LocalizedString this[string name] public IEnumerable GetAllStrings(bool includeParentCultures) => _localizer.GetAllStrings(includeParentCultures); } -} \ No newline at end of file +} diff --git a/src/Localization/Localization/ref/Microsoft.Extensions.Localization.netstandard2.0.cs b/src/Localization/Localization/ref/Microsoft.Extensions.Localization.netstandard2.0.cs index 7bbe1797a5b9..80175da71840 100644 --- a/src/Localization/Localization/ref/Microsoft.Extensions.Localization.netstandard2.0.cs +++ b/src/Localization/Localization/ref/Microsoft.Extensions.Localization.netstandard2.0.cs @@ -36,6 +36,7 @@ public ResourceManagerStringLocalizer(System.Resources.ResourceManager resourceM public virtual System.Collections.Generic.IEnumerable GetAllStrings(bool includeParentCultures) { throw null; } protected System.Collections.Generic.IEnumerable GetAllStrings(bool includeParentCultures, System.Globalization.CultureInfo culture) { throw null; } protected string GetStringSafely(string name, System.Globalization.CultureInfo culture) { throw null; } + [System.ObsoleteAttribute("This method is obsolete. Use `CurrentCulture` and `CurrentUICulture` instead.")] public Microsoft.Extensions.Localization.IStringLocalizer WithCulture(System.Globalization.CultureInfo culture) { throw null; } } public partial class ResourceManagerStringLocalizerFactory : Microsoft.Extensions.Localization.IStringLocalizerFactory @@ -51,6 +52,7 @@ public ResourceManagerStringLocalizerFactory(Microsoft.Extensions.Options.IOptio protected virtual string GetResourcePrefix(string location, string baseName, string resourceLocation) { throw null; } protected virtual Microsoft.Extensions.Localization.RootNamespaceAttribute GetRootNamespaceAttribute(System.Reflection.Assembly assembly) { throw null; } } + [System.ObsoleteAttribute("This method is obsolete. Use `CurrentCulture` and `CurrentUICulture` instead.")] public partial class ResourceManagerWithCultureStringLocalizer : Microsoft.Extensions.Localization.ResourceManagerStringLocalizer { public ResourceManagerWithCultureStringLocalizer(System.Resources.ResourceManager resourceManager, System.Reflection.Assembly resourceAssembly, string baseName, Microsoft.Extensions.Localization.IResourceNamesCache resourceNamesCache, System.Globalization.CultureInfo culture, Microsoft.Extensions.Logging.ILogger logger) : base (default(System.Resources.ResourceManager), default(System.Reflection.Assembly), default(string), default(Microsoft.Extensions.Localization.IResourceNamesCache), default(Microsoft.Extensions.Logging.ILogger)) { } diff --git a/src/Localization/Localization/src/ResourceManagerStringLocalizer.cs b/src/Localization/Localization/src/ResourceManagerStringLocalizer.cs index e2e1a3f234fc..90f8e077b429 100644 --- a/src/Localization/Localization/src/ResourceManagerStringLocalizer.cs +++ b/src/Localization/Localization/src/ResourceManagerStringLocalizer.cs @@ -1,5 +1,5 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Concurrent; @@ -151,6 +151,7 @@ public virtual LocalizedString this[string name] /// /// The to use. /// A culture-specific . + [Obsolete("This method is obsolete. Use `CurrentCulture` and `CurrentUICulture` instead.")] public IStringLocalizer WithCulture(CultureInfo culture) { return culture == null @@ -271,4 +272,4 @@ private IEnumerable GetResourceNamesFromCultureHierarchy(CultureInfo sta return resourceNames; } } -} \ No newline at end of file +} diff --git a/src/Localization/Localization/src/ResourceManagerWithCultureStringLocalizer.cs b/src/Localization/Localization/src/ResourceManagerWithCultureStringLocalizer.cs index 65b6ae242c62..2bc51289da14 100644 --- a/src/Localization/Localization/src/ResourceManagerWithCultureStringLocalizer.cs +++ b/src/Localization/Localization/src/ResourceManagerWithCultureStringLocalizer.cs @@ -1,5 +1,5 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; @@ -15,6 +15,7 @@ namespace Microsoft.Extensions.Localization /// An that uses the and /// to provide localized strings for a specific . /// + [Obsolete("This method is obsolete. Use `CurrentCulture` and `CurrentUICulture` instead.")] public class ResourceManagerWithCultureStringLocalizer : ResourceManagerStringLocalizer { private readonly string _resourceBaseName; @@ -161,4 +162,4 @@ public override LocalizedString this[string name] public override IEnumerable GetAllStrings(bool includeParentCultures) => GetAllStrings(includeParentCultures, _culture); } -} \ No newline at end of file +} diff --git a/src/Localization/Localization/test/ResourceManagerStringLocalizerTest.cs b/src/Localization/Localization/test/ResourceManagerStringLocalizerTest.cs index ff7bfa9933dd..a82ce9d1d390 100644 --- a/src/Localization/Localization/test/ResourceManagerStringLocalizerTest.cs +++ b/src/Localization/Localization/test/ResourceManagerStringLocalizerTest.cs @@ -182,12 +182,11 @@ public void ResourceManagerStringLocalizer_GetAllStrings_MissingResourceThrows(b var resourceManager = new TestResourceManager(baseName, resourceAssembly); var logger = Logger; - var localizer = new ResourceManagerWithCultureStringLocalizer( + var localizer = new ResourceManagerStringLocalizer( resourceManager, resourceAssembly.Assembly, baseName, resourceNamesCache, - CultureInfo.CurrentCulture, logger); // Act & Assert @@ -291,7 +290,7 @@ public TestAssemblyWrapper(Type type) public override Stream GetManifestResourceStream(string name) { ManifestResourceStreamCallCount++; - + return HasResources ? MakeResourceStream() : null; } } From d805cf3b2e5507940152361af106b74eb8043f3b Mon Sep 17 00:00:00 2001 From: Nathanael Marchand Date: Mon, 18 Feb 2019 15:48:45 +0100 Subject: [PATCH 061/183] Implement parallel health checks \n\nCommit migrated from https://github.com/dotnet/extensions/commit/f14a45e09670dc998be5750e66f363ca45e6931c --- .../src/DefaultHealthCheckService.cs | 121 +++++++++--------- 1 file changed, 63 insertions(+), 58 deletions(-) diff --git a/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs b/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs index d5d71d9cb49e..b2b99c99b3e1 100644 --- a/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs +++ b/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs @@ -39,76 +39,81 @@ public override async Task CheckHealthAsync( Func predicate, CancellationToken cancellationToken = default) { - var registrations = _options.Value.Registrations; - - using (var scope = _scopeFactory.CreateScope()) + async Task<(string registrationName, HealthReportEntry result)> RunCheckAsync(IServiceScope scope, HealthCheckRegistration registration) { - var context = new HealthCheckContext(); - var entries = new Dictionary(StringComparer.OrdinalIgnoreCase); + cancellationToken.ThrowIfCancellationRequested(); - var totalTime = ValueStopwatch.StartNew(); - Log.HealthCheckProcessingBegin(_logger); + var healthCheck = registration.Factory(scope.ServiceProvider); - foreach (var registration in registrations) + // If the health check does things like make Database queries using EF or backend HTTP calls, + // it may be valuable to know that logs it generates are part of a health check. So we start a scope. + using (_logger.BeginScope(new HealthCheckLogScope(registration.Name))) { - if (predicate != null && !predicate(registration)) - { - continue; - } + var stopwatch = ValueStopwatch.StartNew(); + var context = new HealthCheckContext { Registration = registration }; - cancellationToken.ThrowIfCancellationRequested(); + Log.HealthCheckBegin(_logger, registration); - var healthCheck = registration.Factory(scope.ServiceProvider); + HealthReportEntry entry; + try + { + var result = await healthCheck.CheckHealthAsync(context, cancellationToken); + var duration = stopwatch.GetElapsedTime(); + + entry = new HealthReportEntry( + status: result.Status, + description: result.Description, + duration: duration, + exception: result.Exception, + data: result.Data); + + Log.HealthCheckEnd(_logger, registration, entry, duration); + Log.HealthCheckData(_logger, registration, entry); + } - // If the health check does things like make Database queries using EF or backend HTTP calls, - // it may be valuable to know that logs it generates are part of a health check. So we start a scope. - using (_logger.BeginScope(new HealthCheckLogScope(registration.Name))) + // Allow cancellation to propagate. + catch (Exception ex) when (ex as OperationCanceledException == null) { - var stopwatch = ValueStopwatch.StartNew(); - context.Registration = registration; - - Log.HealthCheckBegin(_logger, registration); - - HealthReportEntry entry; - try - { - var result = await healthCheck.CheckHealthAsync(context, cancellationToken); - var duration = stopwatch.GetElapsedTime(); - - entry = new HealthReportEntry( - status: result.Status, - description: result.Description, - duration: duration, - exception: result.Exception, - data: result.Data); - - Log.HealthCheckEnd(_logger, registration, entry, duration); - Log.HealthCheckData(_logger, registration, entry); - } - - // Allow cancellation to propagate. - catch (Exception ex) when (ex as OperationCanceledException == null) - { - var duration = stopwatch.GetElapsedTime(); - entry = new HealthReportEntry( - status: HealthStatus.Unhealthy, - description: ex.Message, - duration: duration, - exception: ex, - data: null); - - Log.HealthCheckError(_logger, registration, ex, duration); - } - - entries[registration.Name] = entry; + var duration = stopwatch.GetElapsedTime(); + entry = new HealthReportEntry( + status: HealthStatus.Unhealthy, + description: ex.Message, + duration: duration, + exception: ex, + data: null); + + Log.HealthCheckError(_logger, registration, ex, duration); } + + return (registration.Name, entry); } + } + + IEnumerable registrations = _options.Value.Registrations; + if (predicate != null) + { + registrations = registrations.Where(predicate); + } + + var totalTime = ValueStopwatch.StartNew(); + Log.HealthCheckProcessingBegin(_logger); - var totalElapsedTime = totalTime.GetElapsedTime(); - var report = new HealthReport(entries, totalElapsedTime); - Log.HealthCheckProcessingEnd(_logger, report.Status, totalElapsedTime); - return report; + (string registrationName, HealthReportEntry result)[] results; + using (var scope = _scopeFactory.CreateScope()) + { + results = await Task.WhenAll(registrations.Select(r => RunCheckAsync(scope, r))); } + + var entries = new Dictionary(StringComparer.OrdinalIgnoreCase); + foreach (var (registrationName, result) in results) + { + entries[registrationName] = result; + } + + var totalElapsedTime = totalTime.GetElapsedTime(); + var report = new HealthReport(entries, totalElapsedTime); + Log.HealthCheckProcessingEnd(_logger, report.Status, totalElapsedTime); + return report; } private static void ValidateRegistrations(IEnumerable registrations) From 30c6051d3c1bc558f696d917816994200e2eee60 Mon Sep 17 00:00:00 2001 From: Nathanael Marchand Date: Wed, 20 Feb 2019 12:10:04 +0100 Subject: [PATCH 062/183] Take in account code review \n\nCommit migrated from https://github.com/dotnet/extensions/commit/ecea8c802e8dccee184c8cdaf251a5c8b9b80d57 --- .../src/DefaultHealthCheckService.cs | 121 ++++++++++-------- .../test/DefaultHealthCheckServiceTest.cs | 40 +++++- .../HealthCheckPublisherHostedServiceTest.cs | 12 +- 3 files changed, 110 insertions(+), 63 deletions(-) diff --git a/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs b/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs index b2b99c99b3e1..da8c00a5aaf1 100644 --- a/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs +++ b/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs @@ -39,75 +39,32 @@ public override async Task CheckHealthAsync( Func predicate, CancellationToken cancellationToken = default) { - async Task<(string registrationName, HealthReportEntry result)> RunCheckAsync(IServiceScope scope, HealthCheckRegistration registration) - { - cancellationToken.ThrowIfCancellationRequested(); - - var healthCheck = registration.Factory(scope.ServiceProvider); - - // If the health check does things like make Database queries using EF or backend HTTP calls, - // it may be valuable to know that logs it generates are part of a health check. So we start a scope. - using (_logger.BeginScope(new HealthCheckLogScope(registration.Name))) - { - var stopwatch = ValueStopwatch.StartNew(); - var context = new HealthCheckContext { Registration = registration }; - - Log.HealthCheckBegin(_logger, registration); - - HealthReportEntry entry; - try - { - var result = await healthCheck.CheckHealthAsync(context, cancellationToken); - var duration = stopwatch.GetElapsedTime(); - - entry = new HealthReportEntry( - status: result.Status, - description: result.Description, - duration: duration, - exception: result.Exception, - data: result.Data); - - Log.HealthCheckEnd(_logger, registration, entry, duration); - Log.HealthCheckData(_logger, registration, entry); - } - - // Allow cancellation to propagate. - catch (Exception ex) when (ex as OperationCanceledException == null) - { - var duration = stopwatch.GetElapsedTime(); - entry = new HealthReportEntry( - status: HealthStatus.Unhealthy, - description: ex.Message, - duration: duration, - exception: ex, - data: null); - - Log.HealthCheckError(_logger, registration, ex, duration); - } - - return (registration.Name, entry); - } - } - - IEnumerable registrations = _options.Value.Registrations; + var registrations = _options.Value.Registrations; if (predicate != null) { - registrations = registrations.Where(predicate); + registrations = registrations.Where(predicate).ToArray(); } var totalTime = ValueStopwatch.StartNew(); Log.HealthCheckProcessingBegin(_logger); - (string registrationName, HealthReportEntry result)[] results; + var tasks = new Task[registrations.Count]; + var index = 0; using (var scope = _scopeFactory.CreateScope()) { - results = await Task.WhenAll(registrations.Select(r => RunCheckAsync(scope, r))); + foreach (var registration in registrations) + { + tasks[index++] = RunCheckAsync(scope, registration, cancellationToken); + } + + await Task.WhenAll(tasks).ConfigureAwait(false); } + index = 0; var entries = new Dictionary(StringComparer.OrdinalIgnoreCase); - foreach (var (registrationName, result) in results) + foreach (var registration in registrations) { - entries[registrationName] = result; + entries[registration.Name] = tasks[index++].Result; } var totalElapsedTime = totalTime.GetElapsedTime(); @@ -116,6 +73,58 @@ public override async Task CheckHealthAsync( return report; } + private async Task RunCheckAsync(IServiceScope scope, HealthCheckRegistration registration, CancellationToken cancellationToken) + { + await Task.Yield(); + + cancellationToken.ThrowIfCancellationRequested(); + + var healthCheck = registration.Factory(scope.ServiceProvider); + + // If the health check does things like make Database queries using EF or backend HTTP calls, + // it may be valuable to know that logs it generates are part of a health check. So we start a scope. + using (_logger.BeginScope(new HealthCheckLogScope(registration.Name))) + { + var stopwatch = ValueStopwatch.StartNew(); + var context = new HealthCheckContext { Registration = registration }; + + Log.HealthCheckBegin(_logger, registration); + + HealthReportEntry entry; + try + { + var result = await healthCheck.CheckHealthAsync(context, cancellationToken); + var duration = stopwatch.GetElapsedTime(); + + entry = new HealthReportEntry( + status: result.Status, + description: result.Description, + duration: duration, + exception: result.Exception, + data: result.Data); + + Log.HealthCheckEnd(_logger, registration, entry, duration); + Log.HealthCheckData(_logger, registration, entry); + } + + // Allow cancellation to propagate. + catch (Exception ex) when (ex as OperationCanceledException == null) + { + var duration = stopwatch.GetElapsedTime(); + entry = new HealthReportEntry( + status: HealthStatus.Unhealthy, + description: ex.Message, + duration: duration, + exception: ex, + data: null); + + Log.HealthCheckError(_logger, registration, ex, duration); + } + + return entry; + } + } + private static void ValidateRegistrations(IEnumerable registrations) { // Scan the list for duplicate names to provide a better error if there are duplicates. diff --git a/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs b/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs index 9ab991204ee1..cc6d9936a226 100644 --- a/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs +++ b/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs @@ -375,12 +375,50 @@ public async Task CheckHealthAsync_CheckCanDependOnSingletonService() }); } - private static DefaultHealthCheckService CreateHealthChecksService(Action configure) + [Fact] + public async Task CheckHealthAsync_ChecksAreRunInParallel() + { + // Arrange + var sink = new TestSink(); + async Task CheckMethod() + { + await Task.Delay(100); + return HealthCheckResult.Healthy(); + } + var service = CreateHealthChecksService(b => + { + b.AddAsyncCheck("test1", CheckMethod); + b.AddAsyncCheck("test2", CheckMethod); + b.AddAsyncCheck("test3", CheckMethod); + }, sink); + + // Act + _ = await service.CheckHealthAsync(); + + // Assert + Assert.Collection( + sink.Writes, + entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckProcessingBegin, entry.EventId); }, + entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckBegin, entry.EventId); }, + entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckBegin, entry.EventId); }, + entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckBegin, entry.EventId); }, + entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckEnd, entry.EventId); }, + entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckEnd, entry.EventId); }, + entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckEnd, entry.EventId); }, + entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckProcessingEnd, entry.EventId); }); + } + + private static DefaultHealthCheckService CreateHealthChecksService(Action configure, ITestSink sink = null) { var services = new ServiceCollection(); services.AddLogging(); services.AddOptions(); + if (sink != null) + { + services.AddSingleton(new TestLoggerFactory(sink, enabled: true)); + } + var builder = services.AddHealthChecks(); if (configure != null) { diff --git a/src/HealthChecks/HealthChecks/test/HealthCheckPublisherHostedServiceTest.cs b/src/HealthChecks/HealthChecks/test/HealthCheckPublisherHostedServiceTest.cs index 94687efcb878..099944a47330 100644 --- a/src/HealthChecks/HealthChecks/test/HealthCheckPublisherHostedServiceTest.cs +++ b/src/HealthChecks/HealthChecks/test/HealthCheckPublisherHostedServiceTest.cs @@ -211,8 +211,8 @@ public async Task RunAsync_WaitsForCompletion_Single() entry => { Assert.Equal(HealthCheckPublisherHostedService.EventIds.HealthCheckPublisherProcessingBegin, entry.EventId); }, entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckProcessingBegin, entry.EventId); }, entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckBegin, entry.EventId); }, - entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckEnd, entry.EventId); }, - entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckBegin, entry.EventId); }, + entry => { Assert.Contains(entry.EventId, new[] { DefaultHealthCheckService.EventIds.HealthCheckBegin, DefaultHealthCheckService.EventIds.HealthCheckEnd }); }, + entry => { Assert.Contains(entry.EventId, new[] { DefaultHealthCheckService.EventIds.HealthCheckBegin, DefaultHealthCheckService.EventIds.HealthCheckEnd }); }, entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckEnd, entry.EventId); }, entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckProcessingEnd, entry.EventId); }, entry => { Assert.Equal(HealthCheckPublisherHostedService.EventIds.HealthCheckPublisherBegin, entry.EventId); }, @@ -321,8 +321,8 @@ public async Task RunAsync_PublishersCanTimeout() entry => { Assert.Equal(HealthCheckPublisherHostedService.EventIds.HealthCheckPublisherProcessingBegin, entry.EventId); }, entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckProcessingBegin, entry.EventId); }, entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckBegin, entry.EventId); }, - entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckEnd, entry.EventId); }, - entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckBegin, entry.EventId); }, + entry => { Assert.Contains(entry.EventId, new[] { DefaultHealthCheckService.EventIds.HealthCheckBegin, DefaultHealthCheckService.EventIds.HealthCheckEnd }); }, + entry => { Assert.Contains(entry.EventId, new[] { DefaultHealthCheckService.EventIds.HealthCheckBegin, DefaultHealthCheckService.EventIds.HealthCheckEnd }); }, entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckEnd, entry.EventId); }, entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckProcessingEnd, entry.EventId); }, entry => { Assert.Equal(HealthCheckPublisherHostedService.EventIds.HealthCheckPublisherBegin, entry.EventId); }, @@ -399,8 +399,8 @@ public async Task RunAsync_HandlesExceptions() entry => { Assert.Equal(HealthCheckPublisherHostedService.EventIds.HealthCheckPublisherProcessingBegin, entry.EventId); }, entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckProcessingBegin, entry.EventId); }, entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckBegin, entry.EventId); }, - entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckEnd, entry.EventId); }, - entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckBegin, entry.EventId); }, + entry => { Assert.Contains(entry.EventId, new[] { DefaultHealthCheckService.EventIds.HealthCheckBegin, DefaultHealthCheckService.EventIds.HealthCheckEnd }); }, + entry => { Assert.Contains(entry.EventId, new[] { DefaultHealthCheckService.EventIds.HealthCheckBegin, DefaultHealthCheckService.EventIds.HealthCheckEnd }); }, entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckEnd, entry.EventId); }, entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckProcessingEnd, entry.EventId); }, entry => { Assert.Equal(HealthCheckPublisherHostedService.EventIds.HealthCheckPublisherBegin, entry.EventId); }, From 1f05a6666142453397c78dc72021a98f6f7f6a93 Mon Sep 17 00:00:00 2001 From: Nathanael Marchand Date: Thu, 21 Feb 2019 11:12:32 +0100 Subject: [PATCH 063/183] Take in account code review for test \n\nCommit migrated from https://github.com/dotnet/extensions/commit/49bf9920e4e9079b02e2f775b2bc923abf2be4d1 --- .../test/DefaultHealthCheckServiceTest.cs | 65 +++++++++++-------- 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs b/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs index cc6d9936a226..fa08cccd20a5 100644 --- a/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs +++ b/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.AspNetCore.Testing; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Testing; @@ -379,46 +380,56 @@ public async Task CheckHealthAsync_CheckCanDependOnSingletonService() public async Task CheckHealthAsync_ChecksAreRunInParallel() { // Arrange - var sink = new TestSink(); - async Task CheckMethod() - { - await Task.Delay(100); - return HealthCheckResult.Healthy(); - } + var input1 = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var input2 = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var output1 = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var output2 = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var service = CreateHealthChecksService(b => { - b.AddAsyncCheck("test1", CheckMethod); - b.AddAsyncCheck("test2", CheckMethod); - b.AddAsyncCheck("test3", CheckMethod); - }, sink); + b.AddAsyncCheck("test1", + async () => + { + output1.SetResult(null); + await input1.Task; + return HealthCheckResult.Healthy(); + }); + b.AddAsyncCheck("test2", + async () => + { + output2.SetResult(null); + await input2.Task; + return HealthCheckResult.Healthy(); + }); + }); // Act - _ = await service.CheckHealthAsync(); + var checkHealthTask = service.CheckHealthAsync(); + await Task.WhenAll(output1.Task, output2.Task).TimeoutAfter(TimeSpan.FromSeconds(10)); + input1.SetResult(null); + input2.SetResult(null); + await checkHealthTask; // Assert - Assert.Collection( - sink.Writes, - entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckProcessingBegin, entry.EventId); }, - entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckBegin, entry.EventId); }, - entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckBegin, entry.EventId); }, - entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckBegin, entry.EventId); }, - entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckEnd, entry.EventId); }, - entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckEnd, entry.EventId); }, - entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckEnd, entry.EventId); }, - entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckProcessingEnd, entry.EventId); }); + Assert.Collection(checkHealthTask.Result.Entries, + entry => + { + Assert.Equal("test1", entry.Key); + Assert.Equal(HealthStatus.Healthy, entry.Value.Status); + }, + entry => + { + Assert.Equal("test2", entry.Key); + Assert.Equal(HealthStatus.Healthy, entry.Value.Status); + }); } - private static DefaultHealthCheckService CreateHealthChecksService(Action configure, ITestSink sink = null) + private static DefaultHealthCheckService CreateHealthChecksService(Action configure) { var services = new ServiceCollection(); services.AddLogging(); services.AddOptions(); - if (sink != null) - { - services.AddSingleton(new TestLoggerFactory(sink, enabled: true)); - } - var builder = services.AddHealthChecks(); if (configure != null) { From 7960bde7dada91a3c8bd51c971dcd6973d5aadde Mon Sep 17 00:00:00 2001 From: Nathanael Marchand Date: Tue, 19 Feb 2019 10:23:49 +0100 Subject: [PATCH 064/183] Implement Timeout \n\nCommit migrated from https://github.com/dotnet/extensions/commit/50593c9336a6746bcafa6325fc5ac772bb07fc65 --- ...ealthChecks.Abstractions.netstandard2.0.cs | 3 + .../src/HealthCheckRegistration.cs | 71 +++++++++++- ...Diagnostics.HealthChecks.netstandard2.0.cs | 19 +++- .../src/DefaultHealthCheckService.cs | 34 +++++- .../HealthChecksBuilderAddCheckExtensions.cs | 106 +++++++++++++++++- .../HealthChecksBuilderDelegateExtensions.cs | 98 ++++++++++++++-- .../test/DefaultHealthCheckServiceTest.cs | 26 +++++ 7 files changed, 332 insertions(+), 25 deletions(-) diff --git a/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netstandard2.0.cs b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netstandard2.0.cs index 7fb5beb0e988..9ab497257e90 100644 --- a/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netstandard2.0.cs +++ b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netstandard2.0.cs @@ -11,11 +11,14 @@ public HealthCheckContext() { } public sealed partial class HealthCheckRegistration { public HealthCheckRegistration(string name, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck instance, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable tags) { } + public HealthCheckRegistration(string name, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck instance, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable tags, System.TimeSpan? timeout) { } public HealthCheckRegistration(string name, System.Func factory, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable tags) { } + public HealthCheckRegistration(string name, System.Func factory, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable tags, System.TimeSpan? timeout) { } public System.Func Factory { get { throw null; } set { } } public Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus FailureStatus { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public string Name { get { throw null; } set { } } public System.Collections.Generic.ISet Tags { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.TimeSpan Timeout { get { throw null; } set { } } } [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct HealthCheckResult diff --git a/src/HealthChecks/Abstractions/src/HealthCheckRegistration.cs b/src/HealthChecks/Abstractions/src/HealthCheckRegistration.cs index 9291c388462f..8ee11e3195b7 100644 --- a/src/HealthChecks/Abstractions/src/HealthCheckRegistration.cs +++ b/src/HealthChecks/Abstractions/src/HealthCheckRegistration.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; @@ -24,6 +24,22 @@ public sealed class HealthCheckRegistration { private Func _factory; private string _name; + private TimeSpan _timeout; + + /// + /// Creates a new for an existing instance. + /// + /// The health check name. + /// The instance. + /// + /// The that should be reported upon failure of the health check. If the provided value + /// is null, then will be reported. + /// + /// A list of tags that can be used for filtering health checks. + public HealthCheckRegistration(string name, IHealthCheck instance, HealthStatus? failureStatus, IEnumerable tags) + : this(name, instance, failureStatus, tags, default) + { + } /// /// Creates a new for an existing instance. @@ -35,7 +51,8 @@ public sealed class HealthCheckRegistration /// is null, then will be reported. /// /// A list of tags that can be used for filtering health checks. - public HealthCheckRegistration(string name, IHealthCheck instance, HealthStatus? failureStatus, IEnumerable tags) + /// An optional representing the timeout of the check. + public HealthCheckRegistration(string name, IHealthCheck instance, HealthStatus? failureStatus, IEnumerable tags, TimeSpan? timeout) { if (name == null) { @@ -47,10 +64,16 @@ public HealthCheckRegistration(string name, IHealthCheck instance, HealthStatus? throw new ArgumentNullException(nameof(instance)); } + if (timeout <= TimeSpan.Zero && timeout != System.Threading.Timeout.InfiniteTimeSpan) + { + throw new ArgumentOutOfRangeException(nameof(timeout)); + } + Name = name; FailureStatus = failureStatus ?? HealthStatus.Unhealthy; Tags = new HashSet(tags ?? Array.Empty(), StringComparer.OrdinalIgnoreCase); Factory = (_) => instance; + Timeout = timeout ?? System.Threading.Timeout.InfiniteTimeSpan; } /// @@ -68,6 +91,27 @@ public HealthCheckRegistration( Func factory, HealthStatus? failureStatus, IEnumerable tags) + : this(name, factory, failureStatus, tags, default) + { + } + + /// + /// Creates a new for an existing instance. + /// + /// The health check name. + /// A delegate used to create the instance. + /// + /// The that should be reported when the health check reports a failure. If the provided value + /// is null, then will be reported. + /// + /// A list of tags that can be used for filtering health checks. + /// An optional representing the timeout of the check. + public HealthCheckRegistration( + string name, + Func factory, + HealthStatus? failureStatus, + IEnumerable tags, + TimeSpan? timeout) { if (name == null) { @@ -79,10 +123,16 @@ public HealthCheckRegistration( throw new ArgumentNullException(nameof(factory)); } + if (timeout <= TimeSpan.Zero && timeout != System.Threading.Timeout.InfiniteTimeSpan) + { + throw new ArgumentOutOfRangeException(nameof(timeout)); + } + Name = name; FailureStatus = failureStatus ?? HealthStatus.Unhealthy; Tags = new HashSet(tags ?? Array.Empty(), StringComparer.OrdinalIgnoreCase); Factory = factory; + Timeout = timeout ?? System.Threading.Timeout.InfiniteTimeSpan; } /// @@ -107,6 +157,23 @@ public Func Factory /// public HealthStatus FailureStatus { get; set; } + /// + /// Gets or sets the timeout used for the test. + /// + public TimeSpan Timeout + { + get => _timeout; + set + { + if (value <= TimeSpan.Zero && value != System.Threading.Timeout.InfiniteTimeSpan) + { + throw new ArgumentOutOfRangeException(nameof(value)); + } + + _timeout = value; + } + } + /// /// Gets or sets the health check name. /// diff --git a/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netstandard2.0.cs b/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netstandard2.0.cs index 58bdc806029e..a23961efdd26 100644 --- a/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netstandard2.0.cs +++ b/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netstandard2.0.cs @@ -5,18 +5,25 @@ namespace Microsoft.Extensions.DependencyInjection { public static partial class HealthChecksBuilderAddCheckExtensions { - public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck instance, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus = default(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus?), System.Collections.Generic.IEnumerable tags = null) { throw null; } - public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus = default(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus?), System.Collections.Generic.IEnumerable tags = null) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck instance, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable tags) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck instance, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus = default(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus?), System.Collections.Generic.IEnumerable tags = null, System.TimeSpan? timeout = default(System.TimeSpan?)) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable tags) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus = default(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus?), System.Collections.Generic.IEnumerable tags = null, System.TimeSpan? timeout = default(System.TimeSpan?)) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; } public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddTypeActivatedCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable tags, params object[] args) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddTypeActivatedCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable tags, System.TimeSpan timeout, params object[] args) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; } public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddTypeActivatedCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, params object[] args) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; } public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddTypeActivatedCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, params object[] args) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; } } public static partial class HealthChecksBuilderDelegateExtensions { - public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddAsyncCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func> check, System.Collections.Generic.IEnumerable tags = null) { throw null; } - public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddAsyncCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func> check, System.Collections.Generic.IEnumerable tags = null) { throw null; } - public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func check, System.Collections.Generic.IEnumerable tags = null) { throw null; } - public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func check, System.Collections.Generic.IEnumerable tags = null) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddAsyncCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func> check, System.Collections.Generic.IEnumerable tags) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddAsyncCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func> check, System.Collections.Generic.IEnumerable tags = null, System.TimeSpan? timeout = default(System.TimeSpan?)) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddAsyncCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func> check, System.Collections.Generic.IEnumerable tags) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddAsyncCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func> check, System.Collections.Generic.IEnumerable tags = null, System.TimeSpan? timeout = default(System.TimeSpan?)) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func check, System.Collections.Generic.IEnumerable tags) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func check, System.Collections.Generic.IEnumerable tags = null, System.TimeSpan? timeout = default(System.TimeSpan?)) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func check, System.Collections.Generic.IEnumerable tags) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func check, System.Collections.Generic.IEnumerable tags = null, System.TimeSpan? timeout = default(System.TimeSpan?)) { throw null; } } public static partial class HealthCheckServiceCollectionExtensions { diff --git a/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs b/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs index da8c00a5aaf1..56ce966d1819 100644 --- a/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs +++ b/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs @@ -91,9 +91,21 @@ private async Task RunCheckAsync(IServiceScope scope, HealthC Log.HealthCheckBegin(_logger, registration); HealthReportEntry entry; + CancellationTokenSource timeoutCancellationTokenSource = null; try { - var result = await healthCheck.CheckHealthAsync(context, cancellationToken); + HealthCheckResult result; + + var checkCancellationToken = cancellationToken; + if (registration.Timeout > TimeSpan.Zero) + { + timeoutCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + timeoutCancellationTokenSource.CancelAfter(registration.Timeout); + checkCancellationToken = timeoutCancellationTokenSource.Token; + } + + result = await healthCheck.CheckHealthAsync(context, checkCancellationToken); + var duration = stopwatch.GetElapsedTime(); entry = new HealthReportEntry( @@ -107,7 +119,20 @@ private async Task RunCheckAsync(IServiceScope scope, HealthC Log.HealthCheckData(_logger, registration, entry); } - // Allow cancellation to propagate. + catch (OperationCanceledException ex) when (!cancellationToken.IsCancellationRequested) + { + var duration = stopwatch.GetElapsedTime(); + entry = new HealthReportEntry( + status: HealthStatus.Unhealthy, + description: "A timeout occured while running check.", + duration: duration, + exception: ex, + data: null); + + Log.HealthCheckError(_logger, registration, ex, duration); + } + + // Allow cancellation to propagate if it's not a timeout. catch (Exception ex) when (ex as OperationCanceledException == null) { var duration = stopwatch.GetElapsedTime(); @@ -121,6 +146,11 @@ private async Task RunCheckAsync(IServiceScope scope, HealthC Log.HealthCheckError(_logger, registration, ex, duration); } + finally + { + timeoutCancellationTokenSource?.Dispose(); + } + return entry; } } diff --git a/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthChecksBuilderAddCheckExtensions.cs b/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthChecksBuilderAddCheckExtensions.cs index 95088890542c..74ee30290c60 100644 --- a/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthChecksBuilderAddCheckExtensions.cs +++ b/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthChecksBuilderAddCheckExtensions.cs @@ -24,12 +24,37 @@ public static class HealthChecksBuilderAddCheckExtensions /// /// A list of tags that can be used to filter health checks. /// The . + // 2.0 BACKCOMPAT OVERLOAD -- DO NOT TOUCH + public static IHealthChecksBuilder AddCheck( + this IHealthChecksBuilder builder, + string name, + IHealthCheck instance, + HealthStatus? failureStatus, + IEnumerable tags) + { + return AddCheck(builder, name, instance, failureStatus, tags, default); + } + + /// + /// Adds a new health check with the specified name and implementation. + /// + /// The . + /// The name of the health check. + /// An instance. + /// + /// The that should be reported when the health check reports a failure. If the provided value + /// is null, then will be reported. + /// + /// A list of tags that can be used to filter health checks. + /// An optional representing the timeout of the check. + /// The . public static IHealthChecksBuilder AddCheck( this IHealthChecksBuilder builder, string name, IHealthCheck instance, HealthStatus? failureStatus = null, - IEnumerable tags = null) + IEnumerable tags = null, + TimeSpan? timeout = null) { if (builder == null) { @@ -46,7 +71,7 @@ public static IHealthChecksBuilder AddCheck( throw new ArgumentNullException(nameof(instance)); } - return builder.Add(new HealthCheckRegistration(name, instance, failureStatus, tags)); + return builder.Add(new HealthCheckRegistration(name, instance, failureStatus, tags, timeout)); } /// @@ -63,15 +88,45 @@ public static IHealthChecksBuilder AddCheck( /// The . /// /// This method will use to create the health check - /// instance when needed. If a service of type is registred in the dependency injection container - /// with any liftime it will be used. Otherwise an instance of type will be constructed with + /// instance when needed. If a service of type is registered in the dependency injection container + /// with any lifetime it will be used. Otherwise an instance of type will be constructed with + /// access to services from the dependency injection container. + /// + // 2.0 BACKCOMPAT OVERLOAD -- DO NOT TOUCH + public static IHealthChecksBuilder AddCheck( + this IHealthChecksBuilder builder, + string name, + HealthStatus? failureStatus, + IEnumerable tags) where T : class, IHealthCheck + { + return AddCheck(builder, name, failureStatus, tags, default); + } + + /// + /// Adds a new health check with the specified name and implementation. + /// + /// The health check implementation type. + /// The . + /// The name of the health check. + /// + /// The that should be reported when the health check reports a failure. If the provided value + /// is null, then will be reported. + /// + /// A list of tags that can be used to filter health checks. + /// An optional representing the timeout of the check. + /// The . + /// + /// This method will use to create the health check + /// instance when needed. If a service of type is registered in the dependency injection container + /// with any lifetime it will be used. Otherwise an instance of type will be constructed with /// access to services from the dependency injection container. /// public static IHealthChecksBuilder AddCheck( this IHealthChecksBuilder builder, string name, HealthStatus? failureStatus = null, - IEnumerable tags = null) where T : class, IHealthCheck + IEnumerable tags = null, + TimeSpan? timeout = null) where T : class, IHealthCheck { if (builder == null) { @@ -83,7 +138,7 @@ public static IHealthChecksBuilder AddCheck( throw new ArgumentNullException(nameof(name)); } - return builder.Add(new HealthCheckRegistration(name, s => ActivatorUtilities.GetServiceOrCreateInstance(s), failureStatus, tags)); + return builder.Add(new HealthCheckRegistration(name, s => ActivatorUtilities.GetServiceOrCreateInstance(s), failureStatus, tags, timeout)); } // NOTE: AddTypeActivatedCheck has overloads rather than default parameters values, because default parameter values don't @@ -187,5 +242,44 @@ public static IHealthChecksBuilder AddTypeActivatedCheck( return builder.Add(new HealthCheckRegistration(name, s => ActivatorUtilities.CreateInstance(s, args), failureStatus, tags)); } + + /// + /// Adds a new type activated health check with the specified name and implementation. + /// + /// The health check implementation type. + /// The . + /// The name of the health check. + /// + /// The that should be reported when the health check reports a failure. If the provided value + /// is null, then will be reported. + /// + /// A list of tags that can be used to filter health checks. + /// Additional arguments to provide to the constructor. + /// A representing the timeout of the check. + /// The . + /// + /// This method will use to create the health check + /// instance when needed. Additional arguments can be provided to the constructor via . + /// + public static IHealthChecksBuilder AddTypeActivatedCheck( + this IHealthChecksBuilder builder, + string name, + HealthStatus? failureStatus, + IEnumerable tags, + TimeSpan timeout, + params object[] args) where T : class, IHealthCheck + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + + return builder.Add(new HealthCheckRegistration(name, s => ActivatorUtilities.CreateInstance(s, args), failureStatus, tags, timeout)); + } } } diff --git a/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthChecksBuilderDelegateExtensions.cs b/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthChecksBuilderDelegateExtensions.cs index d7dfdd90ae10..ba27ab555451 100644 --- a/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthChecksBuilderDelegateExtensions.cs +++ b/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthChecksBuilderDelegateExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; @@ -22,11 +22,31 @@ public static class HealthChecksBuilderDelegateExtensions /// A list of tags that can be used to filter health checks. /// A delegate that provides the health check implementation. /// The . + // 2.0 BACKCOMPAT OVERLOAD -- DO NOT TOUCH public static IHealthChecksBuilder AddCheck( this IHealthChecksBuilder builder, string name, Func check, - IEnumerable tags = null) + IEnumerable tags) + { + return AddCheck(builder, name, check, tags, default); + } + + /// + /// Adds a new health check with the specified name and implementation. + /// + /// The . + /// The name of the health check. + /// A list of tags that can be used to filter health checks. + /// A delegate that provides the health check implementation. + /// An optional representing the timeout of the check. + /// The . + public static IHealthChecksBuilder AddCheck( + this IHealthChecksBuilder builder, + string name, + Func check, + IEnumerable tags = null, + TimeSpan? timeout = default) { if (builder == null) { @@ -44,7 +64,25 @@ public static IHealthChecksBuilder AddCheck( } var instance = new DelegateHealthCheck((ct) => Task.FromResult(check())); - return builder.Add(new HealthCheckRegistration(name, instance, failureStatus: null, tags)); + return builder.Add(new HealthCheckRegistration(name, instance, failureStatus: null, tags, timeout)); + } + + /// + /// Adds a new health check with the specified name and implementation. + /// + /// The . + /// The name of the health check. + /// A list of tags that can be used to filter health checks. + /// A delegate that provides the health check implementation. + /// The . + // 2.0 BACKCOMPAT OVERLOAD -- DO NOT TOUCH + public static IHealthChecksBuilder AddCheck( + this IHealthChecksBuilder builder, + string name, + Func check, + IEnumerable tags) + { + return AddCheck(builder, name, check, tags, default); } /// @@ -54,12 +92,14 @@ public static IHealthChecksBuilder AddCheck( /// The name of the health check. /// A list of tags that can be used to filter health checks. /// A delegate that provides the health check implementation. + /// An optional representing the timeout of the check. /// The . public static IHealthChecksBuilder AddCheck( this IHealthChecksBuilder builder, string name, Func check, - IEnumerable tags = null) + IEnumerable tags = null, + TimeSpan? timeout = default) { if (builder == null) { @@ -77,7 +117,7 @@ public static IHealthChecksBuilder AddCheck( } var instance = new DelegateHealthCheck((ct) => Task.FromResult(check(ct))); - return builder.Add(new HealthCheckRegistration(name, instance, failureStatus: null, tags)); + return builder.Add(new HealthCheckRegistration(name, instance, failureStatus: null, tags, timeout)); } /// @@ -88,11 +128,31 @@ public static IHealthChecksBuilder AddCheck( /// A list of tags that can be used to filter health checks. /// A delegate that provides the health check implementation. /// The . + // 2.0 BACKCOMPAT OVERLOAD -- DO NOT TOUCH public static IHealthChecksBuilder AddAsyncCheck( this IHealthChecksBuilder builder, string name, Func> check, - IEnumerable tags = null) + IEnumerable tags) + { + return AddAsyncCheck(builder, name, check, tags, default); + } + + /// + /// Adds a new health check with the specified name and implementation. + /// + /// The . + /// The name of the health check. + /// A list of tags that can be used to filter health checks. + /// A delegate that provides the health check implementation. + /// An optional representing the timeout of the check. + /// The . + public static IHealthChecksBuilder AddAsyncCheck( + this IHealthChecksBuilder builder, + string name, + Func> check, + IEnumerable tags = null, + TimeSpan? timeout = default) { if (builder == null) { @@ -110,7 +170,25 @@ public static IHealthChecksBuilder AddAsyncCheck( } var instance = new DelegateHealthCheck((ct) => check()); - return builder.Add(new HealthCheckRegistration(name, instance, failureStatus: null, tags)); + return builder.Add(new HealthCheckRegistration(name, instance, failureStatus: null, tags, timeout)); + } + + /// + /// Adds a new health check with the specified name and implementation. + /// + /// The . + /// The name of the health check. + /// A list of tags that can be used to filter health checks. + /// A delegate that provides the health check implementation. + /// The . + // 2.0 BACKCOMPAT OVERLOAD -- DO NOT TOUCH + public static IHealthChecksBuilder AddAsyncCheck( + this IHealthChecksBuilder builder, + string name, + Func> check, + IEnumerable tags) + { + return AddAsyncCheck(builder, name, check, tags, default); } /// @@ -120,12 +198,14 @@ public static IHealthChecksBuilder AddAsyncCheck( /// The name of the health check. /// A list of tags that can be used to filter health checks. /// A delegate that provides the health check implementation. + /// An optional representing the timeout of the check. /// The . public static IHealthChecksBuilder AddAsyncCheck( this IHealthChecksBuilder builder, string name, Func> check, - IEnumerable tags = null) + IEnumerable tags = null, + TimeSpan? timeout = default) { if (builder == null) { @@ -143,7 +223,7 @@ public static IHealthChecksBuilder AddAsyncCheck( } var instance = new DelegateHealthCheck((ct) => check(ct)); - return builder.Add(new HealthCheckRegistration(name, instance, failureStatus: null, tags)); + return builder.Add(new HealthCheckRegistration(name, instance, failureStatus: null, tags, timeout)); } } } diff --git a/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs b/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs index fa08cccd20a5..ba0a2f32d566 100644 --- a/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs +++ b/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs @@ -424,6 +424,32 @@ public async Task CheckHealthAsync_ChecksAreRunInParallel() }); } + [Fact] + public async Task CheckHealthAsync_TimeoutReturnsUnhealthy() + { + // Arrange + var service = CreateHealthChecksService(b => + { + b.AddAsyncCheck("timeout", async (ct) => + { + await Task.Delay(2000, ct); + return HealthCheckResult.Healthy(); + }, timeout: TimeSpan.FromMilliseconds(100)); + }); + + // Act + var results = await service.CheckHealthAsync(); + + // Assert + Assert.Collection( + results.Entries, + actual => + { + Assert.Equal("timeout", actual.Key); + Assert.Equal(HealthStatus.Unhealthy, actual.Value.Status); + }); + } + private static DefaultHealthCheckService CreateHealthChecksService(Action configure) { var services = new ServiceCollection(); From a623a5c20fa46d4ea5e3b460af8f9934fd93fbd3 Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Fri, 22 Feb 2019 17:49:48 -0800 Subject: [PATCH 065/183] Fix dotnet/extensions#598 and dotnet/extensions#684 Pass args into type activator \n\nCommit migrated from https://github.com/dotnet/extensions/commit/a787ee2870d3cfc416334f37195f0e6f52570474 --- .../HealthChecksBuilderAddCheckExtensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthChecksBuilderAddCheckExtensions.cs b/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthChecksBuilderAddCheckExtensions.cs index 74ee30290c60..51b7815438f1 100644 --- a/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthChecksBuilderAddCheckExtensions.cs +++ b/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthChecksBuilderAddCheckExtensions.cs @@ -168,7 +168,7 @@ public static IHealthChecksBuilder AddTypeActivatedCheck(this IHealthChecksBu throw new ArgumentNullException(nameof(name)); } - return AddTypeActivatedCheck(builder, name, failureStatus: null, tags: null); + return AddTypeActivatedCheck(builder, name, failureStatus: null, tags: null, args); } /// @@ -203,7 +203,7 @@ public static IHealthChecksBuilder AddTypeActivatedCheck( throw new ArgumentNullException(nameof(name)); } - return AddTypeActivatedCheck(builder, name, failureStatus, tags: null); + return AddTypeActivatedCheck(builder, name, failureStatus, tags: null, args); } /// From 09b9a49da6d0e9b06a4fc3a05ae11ecb43149e33 Mon Sep 17 00:00:00 2001 From: Martin Costello Date: Mon, 4 Mar 2019 18:14:16 +0000 Subject: [PATCH 066/183] Atomically swap config data (dotnet/extensions#1202) Swap the configuration providers' data atomically, rather than directly changing the property, so that any enumeration of the dictionary running during the reload operation does not throw an InvalidOperationException due to the collection being modified. Relates to dotnet/extensions#1189.\n\nCommit migrated from https://github.com/dotnet/extensions/commit/192abfdf3e73106e40d7651eecfb621e4f78c344 --- .../src/KeyPerFileConfigurationProvider.cs | 7 ++- .../test/KeyPerFileTests.cs | 43 +++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/Configuration.KeyPerFile/src/KeyPerFileConfigurationProvider.cs b/src/Configuration.KeyPerFile/src/KeyPerFileConfigurationProvider.cs index 13541110e637..2e33b9dfcd1a 100644 --- a/src/Configuration.KeyPerFile/src/KeyPerFileConfigurationProvider.cs +++ b/src/Configuration.KeyPerFile/src/KeyPerFileConfigurationProvider.cs @@ -31,12 +31,13 @@ private static string TrimNewLine(string value) /// public override void Load() { - Data = new Dictionary(StringComparer.OrdinalIgnoreCase); + var data = new Dictionary(StringComparer.OrdinalIgnoreCase); if (Source.FileProvider == null) { if (Source.Optional) { + Data = data; return; } @@ -61,10 +62,12 @@ public override void Load() { if (Source.IgnoreCondition == null || !Source.IgnoreCondition(file.Name)) { - Data.Add(NormalizeKey(file.Name), TrimNewLine(streamReader.ReadToEnd())); + data.Add(NormalizeKey(file.Name), TrimNewLine(streamReader.ReadToEnd())); } } } + + Data = data; } private string GetDirectoryName() diff --git a/src/Configuration.KeyPerFile/test/KeyPerFileTests.cs b/src/Configuration.KeyPerFile/test/KeyPerFileTests.cs index 4de528c01162..838e62222d6b 100644 --- a/src/Configuration.KeyPerFile/test/KeyPerFileTests.cs +++ b/src/Configuration.KeyPerFile/test/KeyPerFileTests.cs @@ -6,6 +6,8 @@ using System.Collections.Generic; using System.IO; using System.Text; +using System.Threading; +using System.Threading.Tasks; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Primitives; using Xunit; @@ -179,6 +181,47 @@ public void CanUnIgnoreDefaultFiles() Assert.Equal("SecretValue1", config["ignore.Secret1"]); Assert.Equal("SecretValue2", config["Secret2"]); } + + [Fact] + public void BindingDoesNotThrowIfReloadedDuringBinding() + { + var testFileProvider = new TestFileProvider( + new TestFile("Number", "-2"), + new TestFile("Text", "Foo")); + + var config = new ConfigurationBuilder() + .AddKeyPerFile(o => o.FileProvider = testFileProvider) + .Build(); + + MyOptions options = null; + + using (var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(250))) + { + void ReloadLoop() + { + while (!cts.IsCancellationRequested) + { + config.Reload(); + } + } + + _ = Task.Run(ReloadLoop); + + while (!cts.IsCancellationRequested) + { + options = config.Get(); + } + } + + Assert.Equal(-2, options.Number); + Assert.Equal("Foo", options.Text); + } + + private sealed class MyOptions + { + public int Number { get; set; } + public string Text { get; set; } + } } class TestFileProvider : IFileProvider From b13ea4cd548e560a5cfe32fedb9fef9a7a32f7bd Mon Sep 17 00:00:00 2001 From: Martin Costello Date: Tue, 5 Mar 2019 17:43:59 +0000 Subject: [PATCH 067/183] Use localhost for HttpListener (dotnet/extensions#1206) Use localhost instead of 127.0.0.1 to fix dotnet/extensions#1205.\n\nCommit migrated from https://github.com/dotnet/extensions/commit/99b4ddbdbc86bffdafa76d7cac568b6b5ca46f1c --- src/Testing/test/HttpClientSlimTest.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Testing/test/HttpClientSlimTest.cs b/src/Testing/test/HttpClientSlimTest.cs index 42b19ece08d7..ede48243e5ab 100644 --- a/src/Testing/test/HttpClientSlimTest.cs +++ b/src/Testing/test/HttpClientSlimTest.cs @@ -4,7 +4,6 @@ using System; using System.Net; using System.Net.Http; -using System.Net.Sockets; using System.Text; using System.Threading.Tasks; using Xunit; @@ -13,7 +12,7 @@ namespace Microsoft.AspNetCore.Testing { public class HttpClientSlimTest { - private static byte[] _defaultResponse = Encoding.ASCII.GetBytes("test"); + private static readonly byte[] _defaultResponse = Encoding.ASCII.GetBytes("test"); [Fact] public async Task GetStringAsyncHttp() @@ -79,7 +78,7 @@ private HttpListener StartHost(out string address, int statusCode = 200, Func Date: Wed, 6 Mar 2019 15:19:11 -0800 Subject: [PATCH 068/183] add FlakyAttribute to mark flaky tests (dotnet/extensions#1222) part of aspnet/AspNetCoredotnet/extensions#8237\n\nCommit migrated from https://github.com/dotnet/extensions/commit/42e9a7d712d1b513c32961dca7ad6d0bd33d0fae --- src/Testing/src/AzurePipelines.cs | 17 ++++ src/Testing/src/HelixQueues.cs | 26 ++++++ src/Testing/src/xunit/FlakyAttribute.cs | 75 +++++++++++++++ src/Testing/src/xunit/FlakyTestDiscoverer.cs | 38 ++++++++ src/Testing/test/FlakyAttributeTest.cs | 97 ++++++++++++++++++++ 5 files changed, 253 insertions(+) create mode 100644 src/Testing/src/AzurePipelines.cs create mode 100644 src/Testing/src/HelixQueues.cs create mode 100644 src/Testing/src/xunit/FlakyAttribute.cs create mode 100644 src/Testing/src/xunit/FlakyTestDiscoverer.cs create mode 100644 src/Testing/test/FlakyAttributeTest.cs diff --git a/src/Testing/src/AzurePipelines.cs b/src/Testing/src/AzurePipelines.cs new file mode 100644 index 000000000000..ae1eac3b905c --- /dev/null +++ b/src/Testing/src/AzurePipelines.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace Microsoft.AspNetCore.Testing +{ + public static class AzurePipelines + { + public const string All = Prefix + "All"; + public const string Windows = OsPrefix + "Windows_NT"; + public const string macOS = OsPrefix + "Darwin"; + public const string Linux = OsPrefix + "Linux"; + + private const string Prefix = "AzP:"; + private const string OsPrefix = Prefix + "OS:"; + } +} diff --git a/src/Testing/src/HelixQueues.cs b/src/Testing/src/HelixQueues.cs new file mode 100644 index 000000000000..84828b6b83ec --- /dev/null +++ b/src/Testing/src/HelixQueues.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace Microsoft.AspNetCore.Testing +{ + public static class HelixQueues + { + public const string All = Prefix + "All"; + + public const string Fedora28Amd64 = QueuePrefix + "Fedora.28." + Amd64Suffix; + public const string Fedora27Amd64 = QueuePrefix + "Fedora.27." + Amd64Suffix; + public const string Redhat7Amd64 = QueuePrefix + "Redhat.7." + Amd64Suffix; + public const string Debian9Amd64 = QueuePrefix + "Debian.9." + Amd64Suffix; + public const string Debian8Amd64 = QueuePrefix + "Debian.8." + Amd64Suffix; + public const string Centos7Amd64 = QueuePrefix + "Centos.7." + Amd64Suffix; + public const string Ubuntu1604Amd64 = QueuePrefix + "Ubuntu.1604." + Amd64Suffix; + public const string Ubuntu1810Amd64 = QueuePrefix + "Ubuntu.1810." + Amd64Suffix; + public const string macOS1012Amd64 = QueuePrefix + "OSX.1012." + Amd64Suffix; + public const string Windows10Amd64 = QueuePrefix + "Windows.10.Amd64.ClientRS4.VS2017.Open"; // Doesn't have the default suffix! + + private const string Prefix = "Helix:"; + private const string QueuePrefix = Prefix + "Queue:"; + private const string Amd64Suffix = "Amd64.Open"; + } +} diff --git a/src/Testing/src/xunit/FlakyAttribute.cs b/src/Testing/src/xunit/FlakyAttribute.cs new file mode 100644 index 000000000000..b613a9bf4dc7 --- /dev/null +++ b/src/Testing/src/xunit/FlakyAttribute.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using Xunit.Sdk; + +namespace Microsoft.AspNetCore.Testing.xunit +{ + /// + /// Marks a test as "Flaky" so that the build will sequester it and ignore failures. + /// + /// + /// + /// This attribute works by applying xUnit.net "Traits" based on the criteria specified in the attribute + /// properties. Once these traits are applied, build scripts can include/exclude tests based on them. + /// + /// + /// All flakiness-related traits start with Flaky: and are grouped first by the process running the tests: Azure Pipelines (AzP) or Helix. + /// Then there is a segment specifying the "selector" which indicates where the test is flaky. Finally a segment specifying the value of that selector. + /// The value of these traits is always either "true" or the trait is not present. We encode the entire selector in the name of the trait because xUnit.net only + /// provides "==" and "!=" operators for traits, there is no way to check if a trait "contains" or "does not contain" a value. VSTest does support "contains" checks + /// but does not appear to support "does not contain" checks. Using this pattern means we can use simple "==" and "!=" checks to either only run flaky tests, or exclude + /// flaky tests. + /// + /// + /// + /// + /// [Fact] + /// [Flaky("...", HelixQueues.Fedora28Amd64, AzurePipelines.macOS)] + /// public void FlakyTest() + /// { + /// // Flakiness + /// } + /// + /// + /// + /// The above example generates the following facets: + /// + /// + /// + /// + /// Flaky:Helix:Queue:Fedora.28.Amd64.Open = true + /// + /// + /// Flaky:AzP:OS:Darwin = true + /// + /// + /// + /// + /// Given the above attribute, the Azure Pipelines macOS run can easily filter this test out by passing -notrait "Flaky:AzP:OS:all=true" -notrait "Flaky:AzP:OS:Darwin=true" + /// to xunit.console.exe. Similarly, it can run only flaky tests using -trait "Flaky:AzP:OS:all=true" -trait "Flaky:AzP:OS:Darwin=true" + /// + /// + [TraitDiscoverer("Microsoft.AspNetCore.Testing.xunit.FlakyTestDiscoverer", "Microsoft.AspNetCore.Testing")] + [AttributeUsage(AttributeTargets.Method)] + public sealed class FlakyAttribute : Attribute, ITraitAttribute + { + /// + /// Gets a URL to a GitHub issue tracking this flaky test. + /// + public string GitHubIssueUrl { get; } + + public IReadOnlyList Filters { get; } + + /// + /// Initializes a new instance of the class with the specified and a list of . If no + /// filters are provided, the test is considered flaky in all environments. + /// + /// The URL to a GitHub issue tracking this flaky test. + /// A list of filters that define where this test is flaky. Use values in and . + public FlakyAttribute(string gitHubIssueUrl, params string[] filters) + { + GitHubIssueUrl = gitHubIssueUrl; + Filters = new List(filters); + } + } +} diff --git a/src/Testing/src/xunit/FlakyTestDiscoverer.cs b/src/Testing/src/xunit/FlakyTestDiscoverer.cs new file mode 100644 index 000000000000..344b9b237803 --- /dev/null +++ b/src/Testing/src/xunit/FlakyTestDiscoverer.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit.Abstractions; +using Xunit.Sdk; + +namespace Microsoft.AspNetCore.Testing.xunit +{ + public class FlakyTestDiscoverer : ITraitDiscoverer + { + public IEnumerable> GetTraits(IAttributeInfo traitAttribute) + { + if (traitAttribute is ReflectionAttributeInfo attribute && attribute.Attribute is FlakyAttribute flakyAttribute) + { + return GetTraitsCore(flakyAttribute); + } + else + { + throw new InvalidOperationException("The 'Flaky' attribute is only supported via reflection."); + } + } + + private IEnumerable> GetTraitsCore(FlakyAttribute attribute) + { + if (attribute.Filters.Count > 0) + { + foreach (var filter in attribute.Filters) + { + yield return new KeyValuePair($"Flaky:{filter}", "true"); + } + } + else + { + yield return new KeyValuePair($"Flaky:All", "true"); + } + } + } +} diff --git a/src/Testing/test/FlakyAttributeTest.cs b/src/Testing/test/FlakyAttributeTest.cs new file mode 100644 index 000000000000..e9accf627418 --- /dev/null +++ b/src/Testing/test/FlakyAttributeTest.cs @@ -0,0 +1,97 @@ +using Microsoft.AspNetCore.Testing.xunit; +using System; +using System.Collections.Generic; +using Xunit; + +namespace Microsoft.AspNetCore.Testing.Tests +{ + public class FlakyAttributeTest + { + [Fact] + [Flaky("http://example.com")] + public void AlwaysFlaky() + { + if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("HELIX")) || !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("AGENT_OS"))) + { + throw new Exception("Flaky!"); + } + } + + [Fact] + [Flaky("http://example.com", HelixQueues.All)] + public void FlakyInHelixOnly() + { + if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("HELIX"))) + { + throw new Exception("Flaky on Helix!"); + } + } + + [Fact] + [Flaky("http://example.com", HelixQueues.macOS1012Amd64, HelixQueues.Fedora28Amd64)] + public void FlakyInSpecificHelixQueue() + { + // Today we don't run Extensions tests on Helix, but this test should light up when we do. + var queueName = Environment.GetEnvironmentVariable("HELIX"); + if (!string.IsNullOrEmpty(queueName)) + { + var failingQueues = new HashSet(StringComparer.OrdinalIgnoreCase) { HelixQueues.macOS1012Amd64, HelixQueues.Fedora28Amd64 }; + if (failingQueues.Contains(queueName)) + { + throw new Exception($"Flaky on Helix Queue '{queueName}' !"); + } + } + } + + [Fact] + [Flaky("http://example.com", AzurePipelines.All)] + public void FlakyInAzPOnly() + { + if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("AGENT_OS"))) + { + throw new Exception("Flaky on AzP!"); + } + } + + [Fact] + [Flaky("http://example.com", AzurePipelines.Windows)] + public void FlakyInAzPWindowsOnly() + { + if (string.Equals(Environment.GetEnvironmentVariable("AGENT_OS"), AzurePipelines.Windows)) + { + throw new Exception("Flaky on AzP Windows!"); + } + } + + [Fact] + [Flaky("http://example.com", AzurePipelines.macOS)] + public void FlakyInAzPmacOSOnly() + { + if (string.Equals(Environment.GetEnvironmentVariable("AGENT_OS"), AzurePipelines.macOS)) + { + throw new Exception("Flaky on AzP macOS!"); + } + } + + [Fact] + [Flaky("http://example.com", AzurePipelines.Linux)] + public void FlakyInAzPLinuxOnly() + { + if (string.Equals(Environment.GetEnvironmentVariable("AGENT_OS"), AzurePipelines.Linux)) + { + throw new Exception("Flaky on AzP Linux!"); + } + } + + [Fact] + [Flaky("http://example.com", AzurePipelines.Linux, AzurePipelines.macOS)] + public void FlakyInAzPNonWindowsOnly() + { + var agentOs = Environment.GetEnvironmentVariable("AGENT_OS"); + if (string.Equals(agentOs, "Linux") || string.Equals(agentOs, AzurePipelines.macOS)) + { + throw new Exception("Flaky on AzP non-Windows!"); + } + } + } +} From d5a386b0a98a97235346c456dabff6cd54bcb241 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Thu, 7 Mar 2019 12:25:22 -0800 Subject: [PATCH 069/183] Remove newtonsoft ref and sharedsource false flag (dotnet/extensions#1219) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/d563ecb057c7b5687be1a2f8aeb949280f0df75f --- src/Configuration.KeyPerFile/Directory.Build.props | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Configuration.KeyPerFile/Directory.Build.props b/src/Configuration.KeyPerFile/Directory.Build.props index fe35a9faec6a..63d0c8b102d7 100644 --- a/src/Configuration.KeyPerFile/Directory.Build.props +++ b/src/Configuration.KeyPerFile/Directory.Build.props @@ -4,5 +4,6 @@ true configuration + $(NoWarn);PKG0001 From 3e9a592422f4fe9a64545b2b0ecc7884b725e7fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Chalet?= Date: Fri, 8 Mar 2019 00:42:29 +0100 Subject: [PATCH 070/183] Make the generic IStringLocalizer interface covariant (dotnet/extensions#1117) Make the generic IStringLocalizer interface covariant \n\nCommit migrated from https://github.com/dotnet/extensions/commit/90020a9608688a259c7e653b4096270568337290 --- ...ocalization.Abstractions.netstandard2.0.cs | 2 +- .../Abstractions/src/IStringLocalizerOfT.cs | 4 +- .../test/StringLocalizerOfTTest.cs | 159 ++++++++++++++++++ 3 files changed, 162 insertions(+), 3 deletions(-) create mode 100644 src/Localization/Localization/test/StringLocalizerOfTTest.cs diff --git a/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netstandard2.0.cs b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netstandard2.0.cs index 3f9f422021a4..174cac28e5cb 100644 --- a/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netstandard2.0.cs +++ b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netstandard2.0.cs @@ -16,7 +16,7 @@ public partial interface IStringLocalizerFactory Microsoft.Extensions.Localization.IStringLocalizer Create(string baseName, string location); Microsoft.Extensions.Localization.IStringLocalizer Create(System.Type resourceSource); } - public partial interface IStringLocalizer : Microsoft.Extensions.Localization.IStringLocalizer + public partial interface IStringLocalizer : Microsoft.Extensions.Localization.IStringLocalizer { } public partial class LocalizedString diff --git a/src/Localization/Abstractions/src/IStringLocalizerOfT.cs b/src/Localization/Abstractions/src/IStringLocalizerOfT.cs index 695678a9008a..bdc2a1c7b72b 100644 --- a/src/Localization/Abstractions/src/IStringLocalizerOfT.cs +++ b/src/Localization/Abstractions/src/IStringLocalizerOfT.cs @@ -7,8 +7,8 @@ namespace Microsoft.Extensions.Localization /// Represents an that provides strings for . /// /// The to provide strings for. - public interface IStringLocalizer : IStringLocalizer + public interface IStringLocalizer : IStringLocalizer { } -} \ No newline at end of file +} diff --git a/src/Localization/Localization/test/StringLocalizerOfTTest.cs b/src/Localization/Localization/test/StringLocalizerOfTTest.cs new file mode 100644 index 000000000000..ce06e74d1c21 --- /dev/null +++ b/src/Localization/Localization/test/StringLocalizerOfTTest.cs @@ -0,0 +1,159 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Globalization; +using Moq; +using Xunit; + +namespace Microsoft.Extensions.Localization +{ + public class StringLocalizerOfTTest + { + [Fact] + public void Constructor_ThrowsAnExceptionForNullFactory() + { + // Arrange, act and assert + var exception = Assert.Throws( + () => new StringLocalizer(factory: null)); + + Assert.Equal("factory", exception.ParamName); + } + + [Fact] + public void Constructor_ResolvesLocalizerFromFactory() + { + // Arrange + var factory = new Mock(); + + // Act + _ = new StringLocalizer(factory.Object); + + // Assert + factory.Verify(mock => mock.Create(typeof(object)), Times.Once()); + } + + [Fact] + public void WithCulture_InvokesWithCultureFromInnerLocalizer() + { + // Arrange + var factory = new Mock(); + var innerLocalizer = new Mock(); + factory.Setup(mock => mock.Create(typeof(object))) + .Returns(innerLocalizer.Object); + + var localizer = new StringLocalizer(factory.Object); + + // Act +#pragma warning disable CS0618 + localizer.WithCulture(CultureInfo.GetCultureInfo("fr-FR")); +#pragma warning restore CS0618 + + // Assert +#pragma warning disable CS0618 + innerLocalizer.Verify(mock => mock.WithCulture(CultureInfo.GetCultureInfo("fr-FR")), Times.Once()); +#pragma warning restore CS0618 + } + + [Fact] + public void Indexer_ThrowsAnExceptionForNullName() + { + // Arrange + var factory = new Mock(); + var innerLocalizer = new Mock(); + factory.Setup(mock => mock.Create(typeof(object))) + .Returns(innerLocalizer.Object); + + var localizer = new StringLocalizer(factory.Object); + + // Act and assert + var exception = Assert.Throws(() => localizer[name: null]); + + Assert.Equal("name", exception.ParamName); + } + + [Fact] + public void Indexer_InvokesIndexerFromInnerLocalizer() + { + // Arrange + var factory = new Mock(); + var innerLocalizer = new Mock(); + factory.Setup(mock => mock.Create(typeof(object))) + .Returns(innerLocalizer.Object); + + var localizer = new StringLocalizer(factory.Object); + + // Act + _ = localizer["Hello world"]; + + // Assert + innerLocalizer.Verify(mock => mock["Hello world"], Times.Once()); + } + + [Fact] + public void Indexer_ThrowsAnExceptionForNullName_WithArguments() + { + // Arrange + var factory = new Mock(); + var innerLocalizer = new Mock(); + factory.Setup(mock => mock.Create(typeof(object))) + .Returns(innerLocalizer.Object); + + var localizer = new StringLocalizer(factory.Object); + + // Act and assert + var exception = Assert.Throws(() => localizer[name: null]); + + Assert.Equal("name", exception.ParamName); + } + + [Fact] + public void Indexer_InvokesIndexerFromInnerLocalizer_WithArguments() + { + // Arrange + var factory = new Mock(); + var innerLocalizer = new Mock(); + factory.Setup(mock => mock.Create(typeof(object))) + .Returns(innerLocalizer.Object); + + var localizer = new StringLocalizer(factory.Object); + + // Act + _ = localizer["Welcome, {0}", "Bob"]; + + // Assert + innerLocalizer.Verify(mock => mock["Welcome, {0}", "Bob"], Times.Once()); + } + + [Fact] + public void GetAllStrings_InvokesGetAllStringsFromInnerLocalizer() + { + // Arrange + var factory = new Mock(); + var innerLocalizer = new Mock(); + factory.Setup(mock => mock.Create(typeof(object))) + .Returns(innerLocalizer.Object); + + var localizer = new StringLocalizer(factory.Object); + + // Act + localizer.GetAllStrings(includeParentCultures: true); + + // Assert + innerLocalizer.Verify(mock => mock.GetAllStrings(true), Times.Once()); + } + + [Fact] + public void StringLocalizer_CanBeCastToBaseType() + { + // Arrange and act + IStringLocalizer localizer = new StringLocalizer(Mock.Of()); + + // Assert + Assert.NotNull(localizer); + } + + private class BaseType { } + private class DerivedType : BaseType { } + } +} From c9ff2af852cfd8f874a8e8bcf9d2971915405942 Mon Sep 17 00:00:00 2001 From: Javier Calvarro Nelson Date: Mon, 11 Mar 2019 18:00:14 +0100 Subject: [PATCH 071/183] Skip test until we move to the new .NET Core implementation (dotnet/extensions#1156) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/4636215c9b813fef51be55bf4853848bed221a6e --- .../Microsoft.JSInterop/test/JSInProcessRuntimeBaseTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeBaseTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeBaseTest.cs index 36474fe40717..a13d53677a32 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeBaseTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeBaseTest.cs @@ -10,7 +10,7 @@ namespace Microsoft.JSInterop.Tests { public class JSInProcessRuntimeBaseTest { - [Fact] + [Fact(Skip = "https://github.com/aspnet/AspNetCore-Internal/issues/1807#issuecomment-470756811")] public void DispatchesSyncCallsAndDeserializesResults() { // Arrange From 1bfa807e48ee04f8837c1e0c3467dc785271fc1b Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Thu, 14 Mar 2019 21:14:45 -0700 Subject: [PATCH 072/183] Port config fix to 2.2 (dotnet/extensions#1221) - port of dotnet/extensions#1202 - with PR tweaks for 2.2 - e.g. adjust Microsoft.Extensions.Configuration.FunctionalTests.csproj to match layout here - update PatchConfig.props and NuGetPackageVerifier.json\n\nCommit migrated from https://github.com/dotnet/extensions/commit/9ebff1a64e5ea460da1e9837fab3e96939e0ad0e --- .../src/KeyPerFileConfigurationProvider.cs | 7 ++-- .../test/KeyPerFileTests.cs | 35 +++++++++++++++++++ ...ions.Configuration.KeyPerFile.Tests.csproj | 1 + 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/Configuration.KeyPerFile/src/KeyPerFileConfigurationProvider.cs b/src/Configuration.KeyPerFile/src/KeyPerFileConfigurationProvider.cs index 474889574411..6e4234ecf3c3 100644 --- a/src/Configuration.KeyPerFile/src/KeyPerFileConfigurationProvider.cs +++ b/src/Configuration.KeyPerFile/src/KeyPerFileConfigurationProvider.cs @@ -31,12 +31,13 @@ private static string TrimNewLine(string value) /// public override void Load() { - Data = new Dictionary(StringComparer.OrdinalIgnoreCase); + var data = new Dictionary(StringComparer.OrdinalIgnoreCase); if (Source.FileProvider == null) { if (Source.Optional) { + Data = data; return; } else @@ -63,10 +64,12 @@ public override void Load() { if (Source.IgnoreCondition == null || !Source.IgnoreCondition(file.Name)) { - Data.Add(NormalizeKey(file.Name), TrimNewLine(streamReader.ReadToEnd())); + data.Add(NormalizeKey(file.Name), TrimNewLine(streamReader.ReadToEnd())); } } } + + Data = data; } } } diff --git a/src/Configuration.KeyPerFile/test/KeyPerFileTests.cs b/src/Configuration.KeyPerFile/test/KeyPerFileTests.cs index d409c0eab087..499c25106c86 100644 --- a/src/Configuration.KeyPerFile/test/KeyPerFileTests.cs +++ b/src/Configuration.KeyPerFile/test/KeyPerFileTests.cs @@ -4,6 +4,8 @@ using System.IO; using System.Linq; using System.Text; +using System.Threading; +using System.Threading.Tasks; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Primitives; using Xunit; @@ -177,6 +179,39 @@ public void CanUnIgnoreDefaultFiles() Assert.Equal("SecretValue1", config["ignore.Secret1"]); Assert.Equal("SecretValue2", config["Secret2"]); } + + [Fact] + public void BindingDoesNotThrowIfReloadedDuringBinding() + { + var testFileProvider = new TestFileProvider( + new TestFile("Number", "-2"), + new TestFile("Text", "Foo")); + + var config = new ConfigurationBuilder() + .AddKeyPerFile(o => o.FileProvider = testFileProvider) + .Build(); + + MyOptions options = null; + + using (var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(250))) + { + _ = Task.Run(() => { while (!cts.IsCancellationRequested) config.Reload(); }); + + while (!cts.IsCancellationRequested) + { + options = config.Get(); + } + } + + Assert.Equal(-2, options.Number); + Assert.Equal("Foo", options.Text); + } + + private sealed class MyOptions + { + public int Number { get; set; } + public string Text { get; set; } + } } class TestFileProvider : IFileProvider diff --git a/src/Configuration.KeyPerFile/test/Microsoft.Extensions.Configuration.KeyPerFile.Tests.csproj b/src/Configuration.KeyPerFile/test/Microsoft.Extensions.Configuration.KeyPerFile.Tests.csproj index 4205f4ae1357..154fd5bb6219 100644 --- a/src/Configuration.KeyPerFile/test/Microsoft.Extensions.Configuration.KeyPerFile.Tests.csproj +++ b/src/Configuration.KeyPerFile/test/Microsoft.Extensions.Configuration.KeyPerFile.Tests.csproj @@ -5,6 +5,7 @@ + From 2d1743a2f672201f3886ea084fde069ca74d3f86 Mon Sep 17 00:00:00 2001 From: Andrew Stanton-Nurse Date: Tue, 19 Mar 2019 18:43:25 -0700 Subject: [PATCH 073/183] clean up flaky filter constants (dotnet/extensions#1248) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/3b3a23eb1a2dc7dbd57ab192da9856ed3ad8e8ea --- src/Testing/src/AzurePipelines.cs | 17 ---------- src/Testing/src/FlakyOn.cs | 41 +++++++++++++++++++++++++ src/Testing/src/HelixQueues.cs | 28 ++++++----------- src/Testing/src/xunit/FlakyAttribute.cs | 23 ++++++++++++-- src/Testing/test/FlakyAttributeTest.cs | 26 ++++++++-------- 5 files changed, 84 insertions(+), 51 deletions(-) delete mode 100644 src/Testing/src/AzurePipelines.cs create mode 100644 src/Testing/src/FlakyOn.cs diff --git a/src/Testing/src/AzurePipelines.cs b/src/Testing/src/AzurePipelines.cs deleted file mode 100644 index ae1eac3b905c..000000000000 --- a/src/Testing/src/AzurePipelines.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Reflection; - -namespace Microsoft.AspNetCore.Testing -{ - public static class AzurePipelines - { - public const string All = Prefix + "All"; - public const string Windows = OsPrefix + "Windows_NT"; - public const string macOS = OsPrefix + "Darwin"; - public const string Linux = OsPrefix + "Linux"; - - private const string Prefix = "AzP:"; - private const string OsPrefix = Prefix + "OS:"; - } -} diff --git a/src/Testing/src/FlakyOn.cs b/src/Testing/src/FlakyOn.cs new file mode 100644 index 000000000000..81d929904390 --- /dev/null +++ b/src/Testing/src/FlakyOn.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.AspNetCore.Testing +{ + public static class FlakyOn + { + public const string All = "All"; + + public static class Helix + { + public const string All = QueuePrefix + "All"; + + public const string Fedora28Amd64 = QueuePrefix + HelixQueues.Fedora28Amd64; + public const string Fedora27Amd64 = QueuePrefix + HelixQueues.Fedora27Amd64; + public const string Redhat7Amd64 = QueuePrefix + HelixQueues.Redhat7Amd64; + public const string Debian9Amd64 = QueuePrefix + HelixQueues.Debian9Amd64; + public const string Debian8Amd64 = QueuePrefix + HelixQueues.Debian8Amd64; + public const string Centos7Amd64 = QueuePrefix + HelixQueues.Centos7Amd64; + public const string Ubuntu1604Amd64 = QueuePrefix + HelixQueues.Ubuntu1604Amd64; + public const string Ubuntu1810Amd64 = QueuePrefix + HelixQueues.Ubuntu1810Amd64; + public const string macOS1012Amd64 = QueuePrefix + HelixQueues.macOS1012Amd64; + public const string Windows10Amd64 = QueuePrefix + HelixQueues.Windows10Amd64; + + private const string Prefix = "Helix:"; + private const string QueuePrefix = Prefix + "Queue:"; + } + + public static class AzP + { + public const string All = Prefix + "All"; + public const string Windows = OsPrefix + "Windows_NT"; + public const string macOS = OsPrefix + "Darwin"; + public const string Linux = OsPrefix + "Linux"; + + private const string Prefix = "AzP:"; + private const string OsPrefix = Prefix + "OS:"; + } + } +} diff --git a/src/Testing/src/HelixQueues.cs b/src/Testing/src/HelixQueues.cs index 84828b6b83ec..ef5e4d1f5a13 100644 --- a/src/Testing/src/HelixQueues.cs +++ b/src/Testing/src/HelixQueues.cs @@ -1,26 +1,18 @@ -using System; -using System.Collections.Generic; -using System.Reflection; - namespace Microsoft.AspNetCore.Testing { public static class HelixQueues { - public const string All = Prefix + "All"; - - public const string Fedora28Amd64 = QueuePrefix + "Fedora.28." + Amd64Suffix; - public const string Fedora27Amd64 = QueuePrefix + "Fedora.27." + Amd64Suffix; - public const string Redhat7Amd64 = QueuePrefix + "Redhat.7." + Amd64Suffix; - public const string Debian9Amd64 = QueuePrefix + "Debian.9." + Amd64Suffix; - public const string Debian8Amd64 = QueuePrefix + "Debian.8." + Amd64Suffix; - public const string Centos7Amd64 = QueuePrefix + "Centos.7." + Amd64Suffix; - public const string Ubuntu1604Amd64 = QueuePrefix + "Ubuntu.1604." + Amd64Suffix; - public const string Ubuntu1810Amd64 = QueuePrefix + "Ubuntu.1810." + Amd64Suffix; - public const string macOS1012Amd64 = QueuePrefix + "OSX.1012." + Amd64Suffix; - public const string Windows10Amd64 = QueuePrefix + "Windows.10.Amd64.ClientRS4.VS2017.Open"; // Doesn't have the default suffix! + public const string Fedora28Amd64 = "Fedora.28." + Amd64Suffix; + public const string Fedora27Amd64 = "Fedora.27." + Amd64Suffix; + public const string Redhat7Amd64 = "Redhat.7." + Amd64Suffix; + public const string Debian9Amd64 = "Debian.9." + Amd64Suffix; + public const string Debian8Amd64 = "Debian.8." + Amd64Suffix; + public const string Centos7Amd64 = "Centos.7." + Amd64Suffix; + public const string Ubuntu1604Amd64 = "Ubuntu.1604." + Amd64Suffix; + public const string Ubuntu1810Amd64 = "Ubuntu.1810." + Amd64Suffix; + public const string macOS1012Amd64 = "OSX.1012." + Amd64Suffix; + public const string Windows10Amd64 = "Windows.10.Amd64.ClientRS4.VS2017.Open"; // Doesn't have the default suffix! - private const string Prefix = "Helix:"; - private const string QueuePrefix = Prefix + "Queue:"; private const string Amd64Suffix = "Amd64.Open"; } } diff --git a/src/Testing/src/xunit/FlakyAttribute.cs b/src/Testing/src/xunit/FlakyAttribute.cs index b613a9bf4dc7..f58026c7ca22 100644 --- a/src/Testing/src/xunit/FlakyAttribute.cs +++ b/src/Testing/src/xunit/FlakyAttribute.cs @@ -64,12 +64,29 @@ public sealed class FlakyAttribute : Attribute, ITraitAttribute /// Initializes a new instance of the class with the specified and a list of . If no /// filters are provided, the test is considered flaky in all environments. /// + /// + /// At least one filter is required. + /// /// The URL to a GitHub issue tracking this flaky test. - /// A list of filters that define where this test is flaky. Use values in and . - public FlakyAttribute(string gitHubIssueUrl, params string[] filters) + /// The first filter that indicates where the test is flaky. Use a value from . + /// A list of additional filters that define where this test is flaky. Use values in . + public FlakyAttribute(string gitHubIssueUrl, string firstFilter, params string[] additionalFilters) { + if(string.IsNullOrEmpty(gitHubIssueUrl)) + { + throw new ArgumentNullException(nameof(gitHubIssueUrl)); + } + + if(string.IsNullOrEmpty(firstFilter)) + { + throw new ArgumentNullException(nameof(firstFilter)); + } + GitHubIssueUrl = gitHubIssueUrl; - Filters = new List(filters); + var filters = new List(); + filters.Add(firstFilter); + filters.AddRange(additionalFilters); + Filters = filters; } } } diff --git a/src/Testing/test/FlakyAttributeTest.cs b/src/Testing/test/FlakyAttributeTest.cs index e9accf627418..7837bd871139 100644 --- a/src/Testing/test/FlakyAttributeTest.cs +++ b/src/Testing/test/FlakyAttributeTest.cs @@ -8,8 +8,8 @@ namespace Microsoft.AspNetCore.Testing.Tests public class FlakyAttributeTest { [Fact] - [Flaky("http://example.com")] - public void AlwaysFlaky() + [Flaky("http://example.com", FlakyOn.All)] + public void AlwaysFlakyInCI() { if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("HELIX")) || !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("AGENT_OS"))) { @@ -18,7 +18,7 @@ public void AlwaysFlaky() } [Fact] - [Flaky("http://example.com", HelixQueues.All)] + [Flaky("http://example.com", FlakyOn.Helix.All)] public void FlakyInHelixOnly() { if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("HELIX"))) @@ -28,7 +28,7 @@ public void FlakyInHelixOnly() } [Fact] - [Flaky("http://example.com", HelixQueues.macOS1012Amd64, HelixQueues.Fedora28Amd64)] + [Flaky("http://example.com", FlakyOn.Helix.macOS1012Amd64, FlakyOn.Helix.Fedora28Amd64)] public void FlakyInSpecificHelixQueue() { // Today we don't run Extensions tests on Helix, but this test should light up when we do. @@ -44,7 +44,7 @@ public void FlakyInSpecificHelixQueue() } [Fact] - [Flaky("http://example.com", AzurePipelines.All)] + [Flaky("http://example.com", FlakyOn.AzP.All)] public void FlakyInAzPOnly() { if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("AGENT_OS"))) @@ -54,41 +54,41 @@ public void FlakyInAzPOnly() } [Fact] - [Flaky("http://example.com", AzurePipelines.Windows)] + [Flaky("http://example.com", FlakyOn.AzP.Windows)] public void FlakyInAzPWindowsOnly() { - if (string.Equals(Environment.GetEnvironmentVariable("AGENT_OS"), AzurePipelines.Windows)) + if (string.Equals(Environment.GetEnvironmentVariable("AGENT_OS"), "Windows_NT")) { throw new Exception("Flaky on AzP Windows!"); } } [Fact] - [Flaky("http://example.com", AzurePipelines.macOS)] + [Flaky("http://example.com", FlakyOn.AzP.macOS)] public void FlakyInAzPmacOSOnly() { - if (string.Equals(Environment.GetEnvironmentVariable("AGENT_OS"), AzurePipelines.macOS)) + if (string.Equals(Environment.GetEnvironmentVariable("AGENT_OS"), "Darwin")) { throw new Exception("Flaky on AzP macOS!"); } } [Fact] - [Flaky("http://example.com", AzurePipelines.Linux)] + [Flaky("http://example.com", FlakyOn.AzP.Linux)] public void FlakyInAzPLinuxOnly() { - if (string.Equals(Environment.GetEnvironmentVariable("AGENT_OS"), AzurePipelines.Linux)) + if (string.Equals(Environment.GetEnvironmentVariable("AGENT_OS"), "Linux")) { throw new Exception("Flaky on AzP Linux!"); } } [Fact] - [Flaky("http://example.com", AzurePipelines.Linux, AzurePipelines.macOS)] + [Flaky("http://example.com", FlakyOn.AzP.Linux, FlakyOn.AzP.macOS)] public void FlakyInAzPNonWindowsOnly() { var agentOs = Environment.GetEnvironmentVariable("AGENT_OS"); - if (string.Equals(agentOs, "Linux") || string.Equals(agentOs, AzurePipelines.macOS)) + if (string.Equals(agentOs, "Linux") || string.Equals(agentOs, "Darwin")) { throw new Exception("Flaky on AzP non-Windows!"); } From ffc4b007645683a85dbb57146ce9caf6e9ce6fdc Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Sun, 17 Feb 2019 16:16:56 -0800 Subject: [PATCH 074/183] Generalize and harden ST-sync-context \n\nCommit migrated from https://github.com/dotnet/extensions/commit/247bb34a63a0ef097396cb939189b70e3a12321a --- .../SingleThreadedSynchronizationContext.cs | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 src/Shared/test/Shared.Tests/SingleThreadedSynchronizationContext.cs diff --git a/src/Shared/test/Shared.Tests/SingleThreadedSynchronizationContext.cs b/src/Shared/test/Shared.Tests/SingleThreadedSynchronizationContext.cs new file mode 100644 index 000000000000..77312e0a0544 --- /dev/null +++ b/src/Shared/test/Shared.Tests/SingleThreadedSynchronizationContext.cs @@ -0,0 +1,45 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Concurrent; +using System.Threading; + +namespace Microsoft.Extensions.Internal +{ + internal class SingleThreadedSynchronizationContext : SynchronizationContext + { + private readonly BlockingCollection<(SendOrPostCallback Callback, object State)> _queue = new BlockingCollection<(SendOrPostCallback Callback, object State)>(); + + public override void Send(SendOrPostCallback d, object state) // Sync operations + { + throw new NotSupportedException($"{nameof(SingleThreadedSynchronizationContext)} does not support synchronous operations."); + } + + public override void Post(SendOrPostCallback d, object state) // Async operations + { + _queue.Add((d, state)); + } + + public static void Run(Action action) + { + var previous = Current; + var context = new SingleThreadedSynchronizationContext(); + SetSynchronizationContext(context); + try + { + action(); + + while (context._queue.TryTake(out var item)) + { + item.Callback(item.State); + } + } + finally + { + context._queue.CompleteAdding(); + SetSynchronizationContext(previous); + } + } + } +} From 2849483895f57bc259ebc1ff16957feb1b3add45 Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Sun, 17 Feb 2019 16:29:24 -0800 Subject: [PATCH 075/183] Make IHealthCheckService work in ST-sync-context \n\nCommit migrated from https://github.com/dotnet/extensions/commit/2b673c89c20193e2d2d0fd8aa200987a4f2505e7 --- .../src/DefaultHealthCheckService.cs | 7 ++-- .../test/DefaultHealthCheckServiceTest.cs | 34 +++++++++++++++++++ ...ions.Diagnostics.HealthChecks.Tests.csproj | 4 +++ 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs b/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs index 56ce966d1819..c2c9084e0a24 100644 --- a/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs +++ b/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs @@ -54,7 +54,7 @@ public override async Task CheckHealthAsync( { foreach (var registration in registrations) { - tasks[index++] = RunCheckAsync(scope, registration, cancellationToken); + tasks[index++] = Task.Run(() => RunCheckAsync(scope, registration, cancellationToken), cancellationToken); } await Task.WhenAll(tasks).ConfigureAwait(false); @@ -75,8 +75,6 @@ public override async Task CheckHealthAsync( private async Task RunCheckAsync(IServiceScope scope, HealthCheckRegistration registration, CancellationToken cancellationToken) { - await Task.Yield(); - cancellationToken.ThrowIfCancellationRequested(); var healthCheck = registration.Factory(scope.ServiceProvider); @@ -104,7 +102,7 @@ private async Task RunCheckAsync(IServiceScope scope, HealthC checkCancellationToken = timeoutCancellationTokenSource.Token; } - result = await healthCheck.CheckHealthAsync(context, checkCancellationToken); + result = await healthCheck.CheckHealthAsync(context, checkCancellationToken).ConfigureAwait(false); var duration = stopwatch.GetElapsedTime(); @@ -118,7 +116,6 @@ private async Task RunCheckAsync(IServiceScope scope, HealthC Log.HealthCheckEnd(_logger, registration, entry, duration); Log.HealthCheckData(_logger, registration, entry); } - catch (OperationCanceledException ex) when (!cancellationToken.IsCancellationRequested) { var duration = stopwatch.GetElapsedTime(); diff --git a/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs b/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs index ba0a2f32d566..38442edb930d 100644 --- a/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs +++ b/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Testing; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Internal; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Testing; using Microsoft.Extensions.Options; @@ -450,6 +451,39 @@ public async Task CheckHealthAsync_TimeoutReturnsUnhealthy() }); } + [Fact] + public void CheckHealthAsync_WorksInSingleThreadedSyncContext() + { + // Arrange + var service = CreateHealthChecksService(b => + { + b.AddAsyncCheck("test", async () => + { + await Task.Delay(1).ConfigureAwait(false); + return HealthCheckResult.Healthy(); + }); + }); + + var hangs = true; + + // Act + using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3))) + { + var token = cts.Token; + token.Register(() => throw new OperationCanceledException(token)); + + SingleThreadedSynchronizationContext.Run(() => + { + // Act + service.CheckHealthAsync(token).GetAwaiter().GetResult(); + hangs = false; + }); + } + + // Assert + Assert.False(hangs); + } + private static DefaultHealthCheckService CreateHealthChecksService(Action configure) { var services = new ServiceCollection(); diff --git a/src/HealthChecks/HealthChecks/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests.csproj b/src/HealthChecks/HealthChecks/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests.csproj index 163b6189004b..08cd6a35f1b2 100644 --- a/src/HealthChecks/HealthChecks/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests.csproj +++ b/src/HealthChecks/HealthChecks/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests.csproj @@ -12,4 +12,8 @@ + + + + From a8a3fbab33cbb863e1cb660a728df21c13adf0c5 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Wed, 27 Mar 2019 14:40:19 +0000 Subject: [PATCH 076/183] [master] Update dependencies from dotnet/arcade (dotnet/extensions#1279) * Update dependencies from https://github.com/dotnet/arcade build 20190321.6 - Microsoft.DotNet.Arcade.Sdk - 1.0.0-beta.19171.6 - Microsoft.DotNet.GenAPI - 1.0.0-beta.19171.6 - Microsoft.DotNet.Helix.Sdk - 2.0.0-beta.19171.6 * Use Arcade NuSpec helpers * Fix Release flaky test leg * Fix Release flaky non-Windows test legs * Update dependencies from https://github.com/dotnet/arcade build 20190325.9 - Microsoft.DotNet.Arcade.Sdk - 1.0.0-beta.19175.9 - Microsoft.DotNet.GenAPI - 1.0.0-beta.19175.9 - Microsoft.DotNet.Helix.Sdk - 2.0.0-beta.19175.9 * Update dependencies from https://github.com/dotnet/arcade build 20190326.14 - Microsoft.DotNet.Arcade.Sdk - 1.0.0-beta.19176.14 - Microsoft.DotNet.GenAPI - 1.0.0-beta.19176.14 - Microsoft.DotNet.Helix.Sdk - 2.0.0-beta.19176.14 \n\nCommit migrated from https://github.com/dotnet/extensions/commit/d353f7b0204136d352423d44ad6df0505d17a376 --- ...t.Extensions.FileProviders.Embedded.csproj | 39 +++++++------------ ...t.Extensions.FileProviders.Embedded.nuspec | 12 +----- 2 files changed, 16 insertions(+), 35 deletions(-) diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj index 792b9ff5b3f2..7d84c19f9ddc 100644 --- a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj @@ -22,31 +22,22 @@ - + - - id=$(PackageId); - version=$(PackageVersion); - authors=$(Authors); - description=$(Description); - tags=$(PackageTags.Replace(';', ' ')); - licenseUrl=$(PackageLicenseUrl); - projectUrl=$(PackageProjectUrl); - iconUrl=$(PackageIconUrl); - repositoryUrl=$(RepositoryUrl); - repositoryCommit=$(RepositoryCommit); - copyright=$(Copyright); - targetframework=$(TargetFramework); - AssemblyName=$(AssemblyName); - - OutputBinary=@(BuiltProjectOutputGroupOutput); - OutputSymbol=@(DebugSymbolsProjectOutputGroupOutput); - OutputDocumentation=@(DocumentationProjectOutputGroupOutput); - - - TaskAssemblyNetStandard=$(ArtifactsDir)bin\$(AssemblyName).Manifest.Task\$(Configuration)\netstandard2.0\$(AssemblyName).Manifest.Task.dll; - TaskSymbolNetStandard=$(ArtifactsDir)bin\$(AssemblyName).Manifest.Task\$(Configuration)\netstandard2.0\$(AssemblyName).Manifest.Task.pdb; - + $(PackageTags.Replace(';',' ')) + <_OutputBinary>@(BuiltProjectOutputGroupOutput) + <_OutputSymbol>@(DebugSymbolsProjectOutputGroupOutput) + <_OutputDocumentation>@(DocumentationProjectOutputGroupOutput) + + + + + + + + + + diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.nuspec b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.nuspec index ff6d385add60..4a33eb6a952a 100644 --- a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.nuspec +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.nuspec @@ -1,17 +1,7 @@ - $id$ - $version$ - $authors$ - true - $licenseUrl$ - $projectUrl$ - $iconUrl$ - $description$ - $copyright$ - $tags$ - + $CommonMetadataElements$ From f5c756c93533023c24dbcaa3c6d423c522cdb0e7 Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Wed, 27 Mar 2019 19:42:23 -0700 Subject: [PATCH 077/183] Fix dotnet/extensions#8612 JSInterop throws truncated call stack The fix for this is to use more ExceptionDispatchInfo! Basically everwhere that we handle an exception is now an EDI. It's easy to pass these around and they do the right thing as far as perserving the stack in. I de-factored this code a little bit to make all of this work, but it's now pretty unambiguously correct upon inspection. I started out wanting to get rid of the continue-with because we've found that pretty error prone overall. However making this method async-void started to ask more questions than it answered. What happens if serialization of the exception fails? \n\nCommit migrated from https://github.com/dotnet/extensions/commit/0a27fd3ad6604ca1f1278db5e01224a29fcab00d --- .../src/DotNetDispatcher.cs | 69 ++++++++----------- .../Microsoft.JSInterop/src/JSRuntimeBase.cs | 5 ++ .../test/DotNetDispatcherTest.cs | 64 +++++++++++++++++ 3 files changed, 97 insertions(+), 41 deletions(-) diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs index 0f346bbfdd21..ac936e670a49 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Runtime.ExceptionServices; using System.Threading.Tasks; namespace Microsoft.JSInterop @@ -72,8 +73,10 @@ public static void BeginInvoke(string callId, string assemblyName, string method ? null : jsRuntimeBaseInstance.ArgSerializerStrategy.FindDotNetObject(dotNetObjectId); + // Using ExceptionDispatchInfo here throughout because we want to always preserve + // original stack traces. object syncResult = null; - Exception syncException = null; + ExceptionDispatchInfo syncException = null; try { @@ -81,45 +84,38 @@ public static void BeginInvoke(string callId, string assemblyName, string method } catch (Exception ex) { - syncException = ex; + syncException = ExceptionDispatchInfo.Capture(ex); } // If there was no callId, the caller does not want to be notified about the result - if (callId != null) + if (callId == null) { - // Invoke and coerce the result to a Task so the caller can use the same async API - // for both synchronous and asynchronous methods - var task = CoerceToTask(syncResult, syncException); - - task.ContinueWith(completedTask => - { - try - { - var result = TaskGenericsUtil.GetTaskResult(completedTask); - jsRuntimeBaseInstance.EndInvokeDotNet(callId, true, result); - } - catch (Exception ex) - { - ex = UnwrapException(ex); - jsRuntimeBaseInstance.EndInvokeDotNet(callId, false, ex); - } - }); + return; } - } - - private static Task CoerceToTask(object syncResult, Exception syncException) - { - if (syncException != null) + else if (syncException != null) { - return Task.FromException(syncException); + // Threw synchronously, let's respond. + jsRuntimeBaseInstance.EndInvokeDotNet(callId, false, syncException); } - else if (syncResult is Task syncResultTask) + else if (syncResult is Task task) { - return syncResultTask; + // Returned a task - we need to continue that task and then report an exception + // or return the value. + task.ContinueWith(t => + { + if (t.Exception != null) + { + var exception = t.Exception.GetBaseException(); + jsRuntimeBaseInstance.EndInvokeDotNet(callId, false, ExceptionDispatchInfo.Capture(exception)); + } + + var result = TaskGenericsUtil.GetTaskResult(task); + jsRuntimeBaseInstance.EndInvokeDotNet(callId, true, result); + }, TaskScheduler.Current); } else { - return Task.FromResult(syncResult); + jsRuntimeBaseInstance.EndInvokeDotNet(callId, true, syncResult); } } @@ -175,9 +171,10 @@ private static object InvokeSynchronously(string assemblyName, string methodIden { return methodInfo.Invoke(targetInstance, suppliedArgs); } - catch (Exception ex) + catch (TargetInvocationException tie) when (tie.InnerException != null) { - throw UnwrapException(ex); + ExceptionDispatchInfo.Capture(tie.InnerException).Throw(); + throw null; // unreachable } } @@ -285,15 +282,5 @@ private static Assembly GetRequiredLoadedAssembly(string assemblyName) return loadedAssemblies.FirstOrDefault(a => a.GetName().Name.Equals(assemblyName, StringComparison.Ordinal)) ?? throw new ArgumentException($"There is no loaded assembly with the name '{assemblyName}'."); } - - private static Exception UnwrapException(Exception ex) - { - while ((ex is AggregateException || ex is TargetInvocationException) && ex.InnerException != null) - { - ex = ex.InnerException; - } - - return ex; - } } } diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs b/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs index 09379396cf5e..d18bc7f4feeb 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Concurrent; +using System.Runtime.ExceptionServices; using System.Threading; using System.Threading.Tasks; @@ -80,6 +81,10 @@ internal void EndInvokeDotNet(string callId, bool success, object resultOrExcept { resultOrException = resultOrException.ToString(); } + else if (!success && resultOrException is ExceptionDispatchInfo edi) + { + resultOrException = edi.SourceException.ToString(); + } // We pass 0 as the async handle because we don't want the JS-side code to // send back any notification (we're just providing a result for an existing async call) diff --git a/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs b/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs index 93ef9a249850..e3f91a6fd0d2 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs @@ -285,6 +285,54 @@ public Task CanInvokeAsyncMethod() => WithJSRuntime(async jsRuntime => Assert.Equal(2468, resultDto2.IntVal); }); + + [Fact] + public Task CanInvokeSyncThrowingMethod() => WithJSRuntime(async jsRuntime => + { + // Arrange + + // Act + var callId = "123"; + var resultTask = jsRuntime.NextInvocationTask; + DotNetDispatcher.BeginInvoke(callId, thisAssemblyName, nameof(ThrowingClass.ThrowingMethod), default, default); + + await resultTask; // This won't throw, it sets properties on the jsRuntime. + + // Assert + var result = Json.Deserialize(jsRuntime.LastInvocationArgsJson); + Assert.Equal(callId, result[0]); + Assert.False((bool)result[1]); // Fails + + // Make sure the method that threw the exception shows up in the call stack + // https://github.com/aspnet/AspNetCore/issues/8612 + var exception = (string)result[2]; + Assert.Contains(nameof(ThrowingClass.ThrowingMethod), exception); + }); + + [Fact] + public Task CanInvokeAsyncThrowingMethod() => WithJSRuntime(async jsRuntime => + { + // Arrange + + // Act + var callId = "123"; + var resultTask = jsRuntime.NextInvocationTask; + DotNetDispatcher.BeginInvoke(callId, thisAssemblyName, nameof(ThrowingClass.AsyncThrowingMethod), default, default); + + await resultTask; // This won't throw, it sets properties on the jsRuntime. + + // Assert + var result = Json.Deserialize(jsRuntime.LastInvocationArgsJson); + Assert.Equal(callId, result[0]); + Assert.False((bool)result[1]); // Fails + + // Make sure the method that threw the exception shows up in the call stack + // https://github.com/aspnet/AspNetCore/issues/8612 + var exception = (string)result[2]; + Assert.Contains(nameof(ThrowingClass.AsyncThrowingMethod), exception); + }); + + Task WithJSRuntime(Action testCode) { return WithJSRuntime(jsRuntime => @@ -413,6 +461,22 @@ public class TestDTO public int IntVal { get; set; } } + public class ThrowingClass + { + [JSInvokable] + public static string ThrowingMethod() + { + throw new InvalidTimeZoneException(); + } + + [JSInvokable] + public static async Task AsyncThrowingMethod() + { + await Task.Yield(); + throw new InvalidTimeZoneException(); + } + } + public class TestJSRuntime : JSInProcessRuntimeBase { private TaskCompletionSource _nextInvocationTcs = new TaskCompletionSource(); From 76e0fc251c104a6acc9f4d8f86463dfe8c2cea4e Mon Sep 17 00:00:00 2001 From: Justin Kotalik Date: Tue, 9 Apr 2019 14:03:12 -0700 Subject: [PATCH 078/183] Add Repeat attribute (dotnet/extensions#1375) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/13a00b0557e51aefa8132781def19f334a829614 --- src/Testing/src/xunit/ConditionalFactAttribute.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Testing/src/xunit/ConditionalFactAttribute.cs b/src/Testing/src/xunit/ConditionalFactAttribute.cs index 7448b48d8cfc..ce37df2e5656 100644 --- a/src/Testing/src/xunit/ConditionalFactAttribute.cs +++ b/src/Testing/src/xunit/ConditionalFactAttribute.cs @@ -12,4 +12,4 @@ namespace Microsoft.AspNetCore.Testing.xunit public class ConditionalFactAttribute : FactAttribute { } -} \ No newline at end of file +} From 3ba58f028f538492797290c2a7876a0833dff762 Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Thu, 11 Apr 2019 10:30:52 -0700 Subject: [PATCH 079/183] Port fix for dotnet/extensions#1041 to 2.2 (dotnet/extensions#1312) * Update HealthCheckPublisherOptions.cs This seems wrong. Presumably fixes: https://github.com/aspnet/Extensions/issues/1041 (cherry picked from commit dotnet/extensions@b3c88d78fe112bc3b2e272299156857a383edf5d) * Update patchconfig.props\n\nCommit migrated from https://github.com/dotnet/extensions/commit/1301f31b91687ec8e9a34777c1b3095e53ee129f --- .../HealthChecks/src/HealthCheckPublisherOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HealthChecks/HealthChecks/src/HealthCheckPublisherOptions.cs b/src/HealthChecks/HealthChecks/src/HealthCheckPublisherOptions.cs index 1313718af874..6b7c8c3365b5 100644 --- a/src/HealthChecks/HealthChecks/src/HealthCheckPublisherOptions.cs +++ b/src/HealthChecks/HealthChecks/src/HealthCheckPublisherOptions.cs @@ -60,7 +60,7 @@ public TimeSpan Period throw new ArgumentException($"The {nameof(Period)} must not be infinite.", nameof(value)); } - _delay = value; + _period = value; } } From 7f5f0191019d017f5e28707d92c1f3789526e165 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Fri, 12 Apr 2019 23:21:12 -0700 Subject: [PATCH 080/183] Remove target invocation exceptions (dotnet/extensions#1413) - Use BindingFlags.DoNotWrapExceptions to invoke\n\nCommit migrated from https://github.com/dotnet/extensions/commit/0b1aa473a7a722cd3f0aab5166fbe9e5203f0582 --- src/Shared/ActivatorUtilities/ActivatorUtilities.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Shared/ActivatorUtilities/ActivatorUtilities.cs b/src/Shared/ActivatorUtilities/ActivatorUtilities.cs index e2553ced1a2b..c88914ee2870 100644 --- a/src/Shared/ActivatorUtilities/ActivatorUtilities.cs +++ b/src/Shared/ActivatorUtilities/ActivatorUtilities.cs @@ -403,16 +403,20 @@ public object CreateInstance(IServiceProvider provider) } } +#if NETCOREAPP3_0 + return _constructor.Invoke(BindingFlags.DoNotWrapExceptions, binder: null, parameters: _parameterValues, culture: null); +#else try { return _constructor.Invoke(_parameterValues); } - catch (TargetInvocationException ex) + catch (TargetInvocationException ex) when (ex.InnerException != null) { ExceptionDispatchInfo.Capture(ex.InnerException).Throw(); // The above line will always throw, but the compiler requires we throw explicitly. throw; } +#endif } } From f86f24b50a22105414b917e417f453992b986f3b Mon Sep 17 00:00:00 2001 From: viktorpeacock <44648849+viktorpeacock@users.noreply.github.com> Date: Wed, 17 Apr 2019 16:10:12 +0100 Subject: [PATCH 081/183] =?UTF-8?q?Tags=20defined=20during=20health=20chec?= =?UTF-8?q?k=20registration=20are=20now=20available=20in=20th=E2=80=A6=20(?= =?UTF-8?q?dotnet/extensions#1434)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Tags defined during health check registration are now available in the health report entries. * Additional constructor that accepts tags. Original constructor now calls a new one. * Update reference assemblies \n\nCommit migrated from https://github.com/dotnet/extensions/commit/8eaeab2a69bcb91acfedc7847a345f8b64994d89 --- ...ealthChecks.Abstractions.netstandard2.0.cs | 2 ++ .../Abstractions/src/HealthReportEntry.cs | 25 ++++++++++++++++++- .../src/DefaultHealthCheckService.cs | 3 ++- .../test/DefaultHealthCheckServiceTest.cs | 12 ++++++--- 4 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netstandard2.0.cs b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netstandard2.0.cs index 9ab497257e90..b229ca0d5387 100644 --- a/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netstandard2.0.cs +++ b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netstandard2.0.cs @@ -47,11 +47,13 @@ public partial struct HealthReportEntry private object _dummy; private int _dummyPrimitive; public HealthReportEntry(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus status, string description, System.TimeSpan duration, System.Exception exception, System.Collections.Generic.IReadOnlyDictionary data) { throw null; } + public HealthReportEntry(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus status, string description, System.TimeSpan duration, System.Exception exception, System.Collections.Generic.IReadOnlyDictionary data, System.Collections.Generic.IEnumerable tags = null) { throw null; } public System.Collections.Generic.IReadOnlyDictionary Data { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } public string Description { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } public System.TimeSpan Duration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } public System.Exception Exception { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } public Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus Status { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Collections.Generic.IEnumerable Tags { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } } public enum HealthStatus { diff --git a/src/HealthChecks/Abstractions/src/HealthReportEntry.cs b/src/HealthChecks/Abstractions/src/HealthReportEntry.cs index 6e7d6c6b8e55..043c1414a4b1 100644 --- a/src/HealthChecks/Abstractions/src/HealthReportEntry.cs +++ b/src/HealthChecks/Abstractions/src/HealthReportEntry.cs @@ -1,8 +1,9 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; +using System.Linq; namespace Microsoft.Extensions.Diagnostics.HealthChecks { @@ -23,14 +24,31 @@ public struct HealthReportEntry /// An representing the exception that was thrown when checking for status (if any). /// Additional key-value pairs describing the health of the component. public HealthReportEntry(HealthStatus status, string description, TimeSpan duration, Exception exception, IReadOnlyDictionary data) + : this(status, description, duration, exception, data, null) + { + } + + /// + /// Creates a new with the specified values for , , + /// , and . + /// + /// A value indicating the health status of the component that was checked. + /// A human-readable description of the status of the component that was checked. + /// A value indicating the health execution duration. + /// An representing the exception that was thrown when checking for status (if any). + /// Additional key-value pairs describing the health of the component. + /// Tags associated with the health check that generated the report entry. + public HealthReportEntry(HealthStatus status, string description, TimeSpan duration, Exception exception, IReadOnlyDictionary data, IEnumerable tags = null) { Status = status; Description = description; Duration = duration; Exception = exception; Data = data ?? _emptyReadOnlyDictionary; + Tags = tags ?? Enumerable.Empty(); } + /// /// Gets additional key-value pairs describing the health of the component. /// @@ -55,5 +73,10 @@ public HealthReportEntry(HealthStatus status, string description, TimeSpan durat /// Gets the health status of the component that was checked. /// public HealthStatus Status { get; } + + /// + /// Gets the tags associated with the health check. + /// + public IEnumerable Tags { get; } } } diff --git a/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs b/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs index c2c9084e0a24..d7ce9edb9e61 100644 --- a/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs +++ b/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs @@ -111,7 +111,8 @@ private async Task RunCheckAsync(IServiceScope scope, HealthC description: result.Description, duration: duration, exception: result.Exception, - data: result.Data); + data: result.Data, + tags: registration.Tags); Log.HealthCheckEnd(_logger, registration, entry, duration); Log.HealthCheckData(_logger, registration, entry); diff --git a/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs b/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs index 38442edb930d..50cf7ebeaeae 100644 --- a/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs +++ b/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs @@ -57,6 +57,9 @@ public async Task CheckAsync_RunsAllChecksAndAggregatesResultsAsync() const string UnhealthyMessage = "Halp!"; const string HealthyMessage = "Everything is A-OK"; var exception = new Exception("Things are pretty bad!"); + var healthyCheckTags = new List { "healthy-check-tag" }; + var degradedCheckTags = new List { "degraded-check-tag" }; + var unhealthyCheckTags = new List { "unhealthy-check-tag" }; // Arrange var data = new Dictionary() @@ -66,9 +69,9 @@ public async Task CheckAsync_RunsAllChecksAndAggregatesResultsAsync() var service = CreateHealthChecksService(b => { - b.AddAsyncCheck("HealthyCheck", _ => Task.FromResult(HealthCheckResult.Healthy(HealthyMessage, data))); - b.AddAsyncCheck("DegradedCheck", _ => Task.FromResult(HealthCheckResult.Degraded(DegradedMessage))); - b.AddAsyncCheck("UnhealthyCheck", _ => Task.FromResult(HealthCheckResult.Unhealthy(UnhealthyMessage, exception))); + b.AddAsyncCheck("HealthyCheck", _ => Task.FromResult(HealthCheckResult.Healthy(HealthyMessage, data)), healthyCheckTags); + b.AddAsyncCheck("DegradedCheck", _ => Task.FromResult(HealthCheckResult.Degraded(DegradedMessage)), degradedCheckTags); + b.AddAsyncCheck("UnhealthyCheck", _ => Task.FromResult(HealthCheckResult.Unhealthy(UnhealthyMessage, exception)), unhealthyCheckTags); }); // Act @@ -84,6 +87,7 @@ public async Task CheckAsync_RunsAllChecksAndAggregatesResultsAsync() Assert.Equal(HealthStatus.Degraded, actual.Value.Status); Assert.Null(actual.Value.Exception); Assert.Empty(actual.Value.Data); + Assert.Equal(actual.Value.Tags, degradedCheckTags); }, actual => { @@ -96,6 +100,7 @@ public async Task CheckAsync_RunsAllChecksAndAggregatesResultsAsync() Assert.Equal(DataKey, item.Key); Assert.Equal(DataValue, item.Value); }); + Assert.Equal(actual.Value.Tags, healthyCheckTags); }, actual => { @@ -104,6 +109,7 @@ public async Task CheckAsync_RunsAllChecksAndAggregatesResultsAsync() Assert.Equal(HealthStatus.Unhealthy, actual.Value.Status); Assert.Same(exception, actual.Value.Exception); Assert.Empty(actual.Value.Data); + Assert.Equal(actual.Value.Tags, unhealthyCheckTags); }); } From 188c45965a9677af9aa725e2f9eddfef7e211767 Mon Sep 17 00:00:00 2001 From: Andrew Stanton-Nurse Date: Fri, 19 Apr 2019 10:19:59 -0700 Subject: [PATCH 082/183] skip flaky attribute tests, they're just too annoying (dotnet/extensions#1476) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/97bd3149886d12fad88c1405f5abf5a993ede996 --- src/Testing/test/FlakyAttributeTest.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Testing/test/FlakyAttributeTest.cs b/src/Testing/test/FlakyAttributeTest.cs index 7837bd871139..1b9a122d9315 100644 --- a/src/Testing/test/FlakyAttributeTest.cs +++ b/src/Testing/test/FlakyAttributeTest.cs @@ -7,7 +7,7 @@ namespace Microsoft.AspNetCore.Testing.Tests { public class FlakyAttributeTest { - [Fact] + [Fact(Skip = "These tests are nice when you need them but annoying when on all the time.")] [Flaky("http://example.com", FlakyOn.All)] public void AlwaysFlakyInCI() { @@ -17,7 +17,7 @@ public void AlwaysFlakyInCI() } } - [Fact] + [Fact(Skip = "These tests are nice when you need them but annoying when on all the time.")] [Flaky("http://example.com", FlakyOn.Helix.All)] public void FlakyInHelixOnly() { @@ -27,7 +27,7 @@ public void FlakyInHelixOnly() } } - [Fact] + [Fact(Skip = "These tests are nice when you need them but annoying when on all the time.")] [Flaky("http://example.com", FlakyOn.Helix.macOS1012Amd64, FlakyOn.Helix.Fedora28Amd64)] public void FlakyInSpecificHelixQueue() { @@ -43,7 +43,7 @@ public void FlakyInSpecificHelixQueue() } } - [Fact] + [Fact(Skip = "These tests are nice when you need them but annoying when on all the time.")] [Flaky("http://example.com", FlakyOn.AzP.All)] public void FlakyInAzPOnly() { @@ -53,7 +53,7 @@ public void FlakyInAzPOnly() } } - [Fact] + [Fact(Skip = "These tests are nice when you need them but annoying when on all the time.")] [Flaky("http://example.com", FlakyOn.AzP.Windows)] public void FlakyInAzPWindowsOnly() { @@ -63,7 +63,7 @@ public void FlakyInAzPWindowsOnly() } } - [Fact] + [Fact(Skip = "These tests are nice when you need them but annoying when on all the time.")] [Flaky("http://example.com", FlakyOn.AzP.macOS)] public void FlakyInAzPmacOSOnly() { @@ -73,7 +73,7 @@ public void FlakyInAzPmacOSOnly() } } - [Fact] + [Fact(Skip = "These tests are nice when you need them but annoying when on all the time.")] [Flaky("http://example.com", FlakyOn.AzP.Linux)] public void FlakyInAzPLinuxOnly() { @@ -83,7 +83,7 @@ public void FlakyInAzPLinuxOnly() } } - [Fact] + [Fact(Skip = "These tests are nice when you need them but annoying when on all the time.")] [Flaky("http://example.com", FlakyOn.AzP.Linux, FlakyOn.AzP.macOS)] public void FlakyInAzPNonWindowsOnly() { From d1aa9055ce3a5c9ae0b7f80bf05f8efb1ae4119b Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Mon, 22 Apr 2019 16:37:47 -0700 Subject: [PATCH 083/183] Update dependencies from dotnet/arcade and re-generate reference source code (dotnet/extensions#1458) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/996c25be271fa2a42e535fcddec22471d746432f --- ...ions.Diagnostics.HealthChecks.Abstractions.netstandard2.0.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netstandard2.0.cs b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netstandard2.0.cs index b229ca0d5387..8c53adc27598 100644 --- a/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netstandard2.0.cs +++ b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netstandard2.0.cs @@ -57,9 +57,9 @@ public partial struct HealthReportEntry } public enum HealthStatus { + Unhealthy = 0, Degraded = 1, Healthy = 2, - Unhealthy = 0, } public partial interface IHealthCheck { From 3a1330b7acadbe0885d5e69db747adf48e2f1529 Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Fri, 3 May 2019 12:04:29 -0700 Subject: [PATCH 084/183] Enable resx source generation (dotnet/extensions#1635) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/ad92508be6fd31d5c7b9b4f643382a14ab7ffbce --- .../src/Properties/Resources.Designer.cs | 62 ------------------- 1 file changed, 62 deletions(-) delete mode 100644 src/Localization/Localization/src/Properties/Resources.Designer.cs diff --git a/src/Localization/Localization/src/Properties/Resources.Designer.cs b/src/Localization/Localization/src/Properties/Resources.Designer.cs deleted file mode 100644 index 1123d648ad25..000000000000 --- a/src/Localization/Localization/src/Properties/Resources.Designer.cs +++ /dev/null @@ -1,62 +0,0 @@ -// -namespace Microsoft.Extensions.Localization -{ - using System.Globalization; - using System.Reflection; - using System.Resources; - - internal static class Resources - { - private static readonly ResourceManager _resourceManager - = new ResourceManager("Microsoft.Extensions.Localization.Resources", typeof(Resources).GetTypeInfo().Assembly); - - /// - /// The manifest '{0}' was not found. - /// - internal static string Localization_MissingManifest - { - get { return GetString("Localization_MissingManifest"); } - } - - /// - /// The manifest '{0}' was not found. - /// - internal static string FormatLocalization_MissingManifest(object p0) - { - return string.Format(CultureInfo.CurrentCulture, GetString("Localization_MissingManifest"), p0); - } - - /// - /// No manifests exist for the current culture. - /// - internal static string Localization_MissingManifest_Parent - { - get { return GetString("Localization_MissingManifest_Parent"); } - } - - /// - /// No manifests exist for the current culture. - /// - internal static string FormatLocalization_MissingManifest_Parent() - { - return GetString("Localization_MissingManifest_Parent"); - } - - private static string GetString(string name, params string[] formatterNames) - { - var value = _resourceManager.GetString(name); - - System.Diagnostics.Debug.Assert(value != null); - - if (formatterNames != null) - { - for (var i = 0; i < formatterNames.Length; i++) - { - value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}"); - } - } - - return value; - } - } -} From 10e316f541ea0d6b3a941e41c62f09d5baabe329 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Mon, 6 May 2019 17:38:50 -0500 Subject: [PATCH 085/183] Remove the exception filter that is problematic for wasm AOT \n\nCommit migrated from https://github.com/dotnet/extensions/commit/b3cdc7bd5d46a70ae2e0294c750da78c659f04fe --- .../Microsoft.JSInterop/src/DotNetDispatcher.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs index ac936e670a49..20904e1bf09a 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs @@ -171,10 +171,15 @@ private static object InvokeSynchronously(string assemblyName, string methodIden { return methodInfo.Invoke(targetInstance, suppliedArgs); } - catch (TargetInvocationException tie) when (tie.InnerException != null) + catch (TargetInvocationException tie) { - ExceptionDispatchInfo.Capture(tie.InnerException).Throw(); - throw null; // unreachable + if (tie.InnerException != null) + { + ExceptionDispatchInfo.Capture(tie.InnerException).Throw(); + throw null; // unreached + } + + throw; } } From 2706bc5610fdfb7bee8511702babc530c073bb10 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Tue, 7 May 2019 18:00:45 -0500 Subject: [PATCH 086/183] Add comment about not using an exception filter \n\nCommit migrated from https://github.com/dotnet/extensions/commit/b6abd65ea27719ede2147a4a3ed6f4b226f11fd8 --- src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs index 20904e1bf09a..d6c83d512d88 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs @@ -171,7 +171,7 @@ private static object InvokeSynchronously(string assemblyName, string methodIden { return methodInfo.Invoke(targetInstance, suppliedArgs); } - catch (TargetInvocationException tie) + catch (TargetInvocationException tie) // Avoid using exception filters for AOT runtime support { if (tie.InnerException != null) { @@ -243,8 +243,8 @@ private static (MethodInfo, Type[]) GetCachedMethodInfo(string assemblyName, str var invokableMethods = GetRequiredLoadedAssembly(assemblyName) .GetExportedTypes() .SelectMany(type => type.GetMethods( - BindingFlags.Public | - BindingFlags.DeclaredOnly | + BindingFlags.Public | + BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static)) .Where(method => method.IsDefined(typeof(JSInvokableAttribute), inherit: false)); From 213076ff4b84bb9da4c4cdc56d448531a9d10d41 Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Tue, 21 May 2019 14:18:22 -0700 Subject: [PATCH 087/183] Obsolete TestPathUtilities (dotnet/extensions#1698) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/da5dee978f4eece3de85b776dfcf9dcc4cf5b109 --- src/Testing/src/TestPathUtilities.cs | 1 + src/Testing/test/TestPathUtilitiesTest.cs | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/Testing/src/TestPathUtilities.cs b/src/Testing/src/TestPathUtilities.cs index f982471f39b2..a5d83feeff6a 100644 --- a/src/Testing/src/TestPathUtilities.cs +++ b/src/Testing/src/TestPathUtilities.cs @@ -6,6 +6,7 @@ namespace Microsoft.AspNetCore.Testing { + [Obsolete("This API is obsolete and the pattern its usage encouraged should not be used anymore. See https://github.com/aspnet/Extensions/issues/1697 for details.")] public class TestPathUtilities { public static string GetRepoRootDirectory() diff --git a/src/Testing/test/TestPathUtilitiesTest.cs b/src/Testing/test/TestPathUtilitiesTest.cs index c77194a548ce..4a6a74ae9c87 100644 --- a/src/Testing/test/TestPathUtilitiesTest.cs +++ b/src/Testing/test/TestPathUtilitiesTest.cs @@ -9,6 +9,9 @@ namespace Microsoft.AspNetCore.Testing { public class TestPathUtilitiesTest { + // Entire test pending removal - see https://github.com/aspnet/Extensions/issues/1697 +#pragma warning disable 0618 + [Fact] public void GetSolutionRootDirectory_ResolvesSolutionRoot() { @@ -27,5 +30,6 @@ public void GetSolutionRootDirectory_Throws_IfNotFound() var exception = Assert.Throws(() => TestPathUtilities.GetSolutionRootDirectory("NotTesting")); Assert.Equal($"Solution file NotTesting.sln could not be found in {AppContext.BaseDirectory} or its parent directories.", exception.Message); } +#pragma warning restore 0618 } } From cfc1320ee245f442460ec8084260807fe2fdd142 Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Thu, 23 May 2019 16:33:55 -0700 Subject: [PATCH 088/183] Add support for source-build (dotnet/extensions#1740) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/ad4067b012f62a70bb7378c3e0a029168f3eb106 --- src/Testing/Directory.Build.props | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/Testing/Directory.Build.props diff --git a/src/Testing/Directory.Build.props b/src/Testing/Directory.Build.props new file mode 100644 index 000000000000..68f87d4f2484 --- /dev/null +++ b/src/Testing/Directory.Build.props @@ -0,0 +1,8 @@ + + + + + + true + + From 8b068f23201339b2cbf806c10501d794763f5060 Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Tue, 28 May 2019 18:07:58 -0700 Subject: [PATCH 089/183] Build and publish NPM packages in CI (dotnet/extensions#1748) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/92b2c76bc8bb082e49bc7549e4d95ab244bd6975 --- src/JSInterop/Microsoft.JSInterop.JS/src/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/JSInterop/Microsoft.JSInterop.JS/src/package.json b/src/JSInterop/Microsoft.JSInterop.JS/src/package.json index 8be950e16fef..803e02a35b85 100644 --- a/src/JSInterop/Microsoft.JSInterop.JS/src/package.json +++ b/src/JSInterop/Microsoft.JSInterop.JS/src/package.json @@ -12,12 +12,12 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/aspnet/AspNetCore.git" + "url": "git+https://github.com/aspnet/Extensions.git" }, "author": "Microsoft", "license": "Apache-2.0", "bugs": { - "url": "https://github.com/aspnet/AspNetCore/issues" + "url": "https://github.com/aspnet/Extensions/issues" }, "homepage": "https://github.com/aspnet/Extensions/tree/master/src/JSInterop#readme", "files": [ From 78daf0166c82aabff2762247c64d86a7096aa80e Mon Sep 17 00:00:00 2001 From: Pranav K Date: Wed, 29 May 2019 09:47:26 -0700 Subject: [PATCH 090/183] Use System.Text.Json in Microsoft.JSInterop (dotnet/extensions#1704) * Use System.Text.Json in Microsoft.JSInterop \n\nCommit migrated from https://github.com/dotnet/extensions/commit/f6c9258abe10d6e79ab76b5b8217b0d2b892aa0e --- .../src/src/Microsoft.JSInterop.ts | 11 +- .../ref/Microsoft.JSInterop.csproj | 2 +- .../ref/Microsoft.JSInterop.netstandard2.0.cs | 35 +- .../src/DotNetDispatcher.cs | 212 +- .../src/DotNetObjectRef.cs | 59 +- .../src/DotNetObjectRefManager.cs | 51 + .../src/DotNetObjectRefOfT.cs | 76 + .../src/ICustomArgSerializer.cs | 22 - .../src/IDotNetObjectRef.cs | 12 + .../Microsoft.JSInterop/src/IJSRuntime.cs | 17 +- .../src/InteropArgSerializerStrategy.cs | 121 - .../src/JSAsyncCallResult.cs | 20 +- .../src/JSInProcessRuntimeBase.cs | 17 +- .../Microsoft.JSInterop/src/JSRuntimeBase.cs | 61 +- .../Microsoft.JSInterop/src/Json/CamelCase.cs | 59 - .../Microsoft.JSInterop/src/Json/Json.cs | 39 - .../src/Json/SimpleJson/README.txt | 24 - .../src/Json/SimpleJson/SimpleJson.cs | 2201 ----------------- .../src/JsonSerializerOptionsProvider.cs | 15 + .../src/Microsoft.JSInterop.csproj | 6 +- .../test/DotNetDispatcherTest.cs | 162 +- .../test/DotNetObjectRefTest.cs | 61 +- .../test/JSInProcessRuntimeBaseTest.cs | 35 +- .../test/JSRuntimeBaseTest.cs | 59 +- .../Microsoft.JSInterop/test/JSRuntimeTest.cs | 3 - .../Microsoft.JSInterop/test/JsonUtilTest.cs | 349 --- 26 files changed, 549 insertions(+), 3180 deletions(-) create mode 100644 src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRefManager.cs create mode 100644 src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRefOfT.cs delete mode 100644 src/JSInterop/Microsoft.JSInterop/src/ICustomArgSerializer.cs create mode 100644 src/JSInterop/Microsoft.JSInterop/src/IDotNetObjectRef.cs delete mode 100644 src/JSInterop/Microsoft.JSInterop/src/InteropArgSerializerStrategy.cs delete mode 100644 src/JSInterop/Microsoft.JSInterop/src/Json/CamelCase.cs delete mode 100644 src/JSInterop/Microsoft.JSInterop/src/Json/Json.cs delete mode 100644 src/JSInterop/Microsoft.JSInterop/src/Json/SimpleJson/README.txt delete mode 100644 src/JSInterop/Microsoft.JSInterop/src/Json/SimpleJson/SimpleJson.cs create mode 100644 src/JSInterop/Microsoft.JSInterop/src/JsonSerializerOptionsProvider.cs delete mode 100644 src/JSInterop/Microsoft.JSInterop/test/JsonUtilTest.cs diff --git a/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts b/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts index 4b5d409d0f85..5386b92bb50c 100644 --- a/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts +++ b/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts @@ -264,17 +264,14 @@ module DotNet { } public serializeAsArg() { - return `__dotNetObject:${this._id}`; + return {__dotNetObject: this._id}; } } - const dotNetObjectValueFormat = /^__dotNetObject\:(\d+)$/; + const dotNetObjectRefKey = '__dotNetObject'; attachReviver(function reviveDotNetObject(key: any, value: any) { - if (typeof value === 'string') { - const match = value.match(dotNetObjectValueFormat); - if (match) { - return new DotNetObject(parseInt(match[1])); - } + if (value && typeof value === 'object' && value.hasOwnProperty(dotNetObjectRefKey)) { + return new DotNetObject(value.__dotNetObject); } // Unrecognized - let another reviver handle it diff --git a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj index 87fd913427e6..5f83c5309186 100644 --- a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj +++ b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj @@ -5,6 +5,6 @@ - + diff --git a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs index 95b9b7956c51..1db3385bd7f8 100644 --- a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs +++ b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs @@ -12,12 +12,19 @@ public static void EndInvoke(long asyncHandle, bool succeeded, Microsoft.JSInter [Microsoft.JSInterop.JSInvokableAttribute("DotNetDispatcher.ReleaseDotNetObject")] public static void ReleaseDotNetObject(long dotNetObjectId) { } } - public partial class DotNetObjectRef : System.IDisposable + public static partial class DotNetObjectRef { - public DotNetObjectRef(object value) { } - public object Value { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public static Microsoft.JSInterop.DotNetObjectRef Create(TValue value) where TValue : class { throw null; } + } + public sealed partial class DotNetObjectRef : System.IDisposable where TValue : class + { + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public DotNetObjectRef() { } + [System.Text.Json.Serialization.JsonIgnoreAttribute] + public TValue Value { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public long __dotNetObject { get { throw null; } set { } } public void Dispose() { } - public void EnsureAttachedToJsRuntime(Microsoft.JSInterop.IJSRuntime runtime) { } } public partial interface IJSInProcessRuntime : Microsoft.JSInterop.IJSRuntime { @@ -25,8 +32,7 @@ public partial interface IJSInProcessRuntime : Microsoft.JSInterop.IJSRuntime } public partial interface IJSRuntime { - System.Threading.Tasks.Task InvokeAsync(string identifier, params object[] args); - void UntrackObjectRef(Microsoft.JSInterop.DotNetObjectRef dotNetObjectRef); + System.Threading.Tasks.Task InvokeAsync(string identifier, params object[] args); } public partial class JSException : System.Exception { @@ -36,7 +42,7 @@ public abstract partial class JSInProcessRuntimeBase : Microsoft.JSInterop.JSRun { protected JSInProcessRuntimeBase() { } protected abstract string InvokeJS(string identifier, string argsJson); - public T Invoke(string identifier, params object[] args) { throw null; } + public TValue Invoke(string identifier, params object[] args) { throw null; } } [System.AttributeUsageAttribute(System.AttributeTargets.Method, AllowMultiple=true)] public partial class JSInvokableAttribute : System.Attribute @@ -45,30 +51,21 @@ public JSInvokableAttribute() { } public JSInvokableAttribute(string identifier) { } public string Identifier { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } } - public static partial class Json - { - public static T Deserialize(string json) { throw null; } - public static string Serialize(object value) { throw null; } - } public static partial class JSRuntime { public static void SetCurrentJSRuntime(Microsoft.JSInterop.IJSRuntime instance) { } } public abstract partial class JSRuntimeBase : Microsoft.JSInterop.IJSRuntime { - public JSRuntimeBase() { } + protected JSRuntimeBase() { } protected abstract void BeginInvokeJS(long asyncHandle, string identifier, string argsJson); public System.Threading.Tasks.Task InvokeAsync(string identifier, params object[] args) { throw null; } - public void UntrackObjectRef(Microsoft.JSInterop.DotNetObjectRef dotNetObjectRef) { } } } namespace Microsoft.JSInterop.Internal { - public partial interface ICustomArgSerializer - { - object ToJsonPrimitive(); - } - public partial class JSAsyncCallResult + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public sealed partial class JSAsyncCallResult { internal JSAsyncCallResult() { } } diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs index d6c83d512d88..5ecd3506e093 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs @@ -1,14 +1,16 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using Microsoft.JSInterop.Internal; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Runtime.ExceptionServices; +using System.Text.Json; +using System.Text.Json.Serialization; using System.Threading.Tasks; +using Microsoft.JSInterop.Internal; namespace Microsoft.JSInterop { @@ -17,8 +19,10 @@ namespace Microsoft.JSInterop /// public static class DotNetDispatcher { - private static ConcurrentDictionary> _cachedMethodsByAssembly - = new ConcurrentDictionary>(); + internal const string DotNetObjectRefKey = nameof(DotNetObjectRef.__dotNetObject); + + private static readonly ConcurrentDictionary> _cachedMethodsByAssembly + = new ConcurrentDictionary>(); /// /// Receives a call from JS to .NET, locating and invoking the specified method. @@ -35,17 +39,19 @@ public static string Invoke(string assemblyName, string methodIdentifier, long d // the targeted method has [JSInvokable]. It is not itself subject to that restriction, // because there would be nobody to police that. This method *is* the police. - // DotNetDispatcher only works with JSRuntimeBase instances. - var jsRuntime = (JSRuntimeBase)JSRuntime.Current; - var targetInstance = (object)null; if (dotNetObjectId != default) { - targetInstance = jsRuntime.ArgSerializerStrategy.FindDotNetObject(dotNetObjectId); + targetInstance = DotNetObjectRefManager.Current.FindDotNetObject(dotNetObjectId); } var syncResult = InvokeSynchronously(assemblyName, methodIdentifier, targetInstance, argsJson); - return syncResult == null ? null : Json.Serialize(syncResult, jsRuntime.ArgSerializerStrategy); + if (syncResult == null) + { + return null; + } + + return JsonSerializer.ToString(syncResult, JsonSerializerOptionsProvider.Options); } /// @@ -69,9 +75,11 @@ public static void BeginInvoke(string callId, string assemblyName, string method // code has to implement its own way of returning async results. var jsRuntimeBaseInstance = (JSRuntimeBase)JSRuntime.Current; - var targetInstance = dotNetObjectId == default - ? null - : jsRuntimeBaseInstance.ArgSerializerStrategy.FindDotNetObject(dotNetObjectId); + var targetInstance = (object)null; + if (dotNetObjectId != default) + { + targetInstance = DotNetObjectRefManager.Current.FindDotNetObject(dotNetObjectId); + } // Using ExceptionDispatchInfo here throughout because we want to always preserve // original stack traces. @@ -121,6 +129,7 @@ public static void BeginInvoke(string callId, string assemblyName, string method private static object InvokeSynchronously(string assemblyName, string methodIdentifier, object targetInstance, string argsJson) { + AssemblyKey assemblyKey; if (targetInstance != null) { if (assemblyName != null) @@ -128,44 +137,16 @@ private static object InvokeSynchronously(string assemblyName, string methodIden throw new ArgumentException($"For instance method calls, '{nameof(assemblyName)}' should be null. Value received: '{assemblyName}'."); } - assemblyName = targetInstance.GetType().Assembly.GetName().Name; + assemblyKey = new AssemblyKey(targetInstance.GetType().Assembly); } - - var (methodInfo, parameterTypes) = GetCachedMethodInfo(assemblyName, methodIdentifier); - - // There's no direct way to say we want to deserialize as an array with heterogenous - // entry types (e.g., [string, int, bool]), so we need to deserialize in two phases. - // First we deserialize as object[], for which SimpleJson will supply JsonObject - // instances for nonprimitive values. - var suppliedArgs = (object[])null; - var suppliedArgsLength = 0; - if (argsJson != null) - { - suppliedArgs = Json.Deserialize(argsJson).ToArray(); - suppliedArgsLength = suppliedArgs.Length; - } - if (suppliedArgsLength != parameterTypes.Length) + else { - throw new ArgumentException($"In call to '{methodIdentifier}', expected {parameterTypes.Length} parameters but received {suppliedArgsLength}."); + assemblyKey = new AssemblyKey(assemblyName); } - // Second, convert each supplied value to the type expected by the method - var runtime = (JSRuntimeBase)JSRuntime.Current; - var serializerStrategy = runtime.ArgSerializerStrategy; - for (var i = 0; i < suppliedArgsLength; i++) - { - if (parameterTypes[i] == typeof(JSAsyncCallResult)) - { - // For JS async call results, we have to defer the deserialization until - // later when we know what type it's meant to be deserialized as - suppliedArgs[i] = new JSAsyncCallResult(suppliedArgs[i]); - } - else - { - suppliedArgs[i] = serializerStrategy.DeserializeObject( - suppliedArgs[i], parameterTypes[i]); - } - } + var (methodInfo, parameterTypes) = GetCachedMethodInfo(assemblyKey, methodIdentifier); + + var suppliedArgs = ParseArguments(methodIdentifier, argsJson, parameterTypes); try { @@ -183,6 +164,85 @@ private static object InvokeSynchronously(string assemblyName, string methodIden } } + private static object[] ParseArguments(string methodIdentifier, string argsJson, Type[] parameterTypes) + { + if (parameterTypes.Length == 0) + { + return Array.Empty(); + } + + // There's no direct way to say we want to deserialize as an array with heterogenous + // entry types (e.g., [string, int, bool]), so we need to deserialize in two phases. + var jsonDocument = JsonDocument.Parse(argsJson); + var shouldDisposeJsonDocument = true; + try + { + if (jsonDocument.RootElement.Type != JsonValueType.Array) + { + throw new ArgumentException($"Expected a JSON array but got {jsonDocument.RootElement.Type}."); + } + + var suppliedArgsLength = jsonDocument.RootElement.GetArrayLength(); + + if (suppliedArgsLength != parameterTypes.Length) + { + throw new ArgumentException($"In call to '{methodIdentifier}', expected {parameterTypes.Length} parameters but received {suppliedArgsLength}."); + } + + // Second, convert each supplied value to the type expected by the method + var suppliedArgs = new object[parameterTypes.Length]; + var index = 0; + foreach (var item in jsonDocument.RootElement.EnumerateArray()) + { + var parameterType = parameterTypes[index]; + + if (parameterType == typeof(JSAsyncCallResult)) + { + // We will pass the JsonDocument instance to JAsyncCallResult and make JSRuntimeBase + // responsible for disposing it. + shouldDisposeJsonDocument = false; + // For JS async call results, we have to defer the deserialization until + // later when we know what type it's meant to be deserialized as + suppliedArgs[index] = new JSAsyncCallResult(jsonDocument, item); + } + else if (IsIncorrectDotNetObjectRefUse(item, parameterType)) + { + throw new InvalidOperationException($"In call to '{methodIdentifier}', parameter of type '{parameterType.Name}' at index {(index + 1)} must be declared as type 'DotNetObjectRef<{parameterType.Name}>' to receive the incoming value."); + } + else + { + suppliedArgs[index] = JsonSerializer.Parse(item.GetRawText(), parameterType, JsonSerializerOptionsProvider.Options); + } + + index++; + } + + if (shouldDisposeJsonDocument) + { + jsonDocument.Dispose(); + } + + return suppliedArgs; + } + catch + { + // Always dispose the JsonDocument in case of an error. + jsonDocument.Dispose(); + throw; + } + + + static bool IsIncorrectDotNetObjectRefUse(JsonElement item, Type parameterType) + { + // Check for incorrect use of DotNetObjectRef at the top level. We know it's + // an incorrect use if there's a object that looks like { '__dotNetObject': }, + // but we aren't assigning to DotNetObjectRef{T}. + return item.Type == JsonValueType.Object && + item.TryGetProperty(DotNetObjectRefKey, out _) && + !typeof(IDotNetObjectRef).IsAssignableFrom(parameterType); + } + } + /// /// Receives notification that a call from .NET to JS has finished, marking the /// associated as completed. @@ -192,7 +252,7 @@ private static object InvokeSynchronously(string assemblyName, string methodIden /// If is true, specifies the invocation result. If is false, gives the corresponding to the invocation failure. [JSInvokable(nameof(DotNetDispatcher) + "." + nameof(EndInvoke))] public static void EndInvoke(long asyncHandle, bool succeeded, JSAsyncCallResult result) - => ((JSRuntimeBase)JSRuntime.Current).EndInvokeJS(asyncHandle, succeeded, result.ResultOrException); + => ((JSRuntimeBase)JSRuntime.Current).EndInvokeJS(asyncHandle, succeeded, result); /// /// Releases the reference to the specified .NET object. This allows the .NET runtime @@ -207,16 +267,14 @@ public static void EndInvoke(long asyncHandle, bool succeeded, JSAsyncCallResult [JSInvokable(nameof(DotNetDispatcher) + "." + nameof(ReleaseDotNetObject))] public static void ReleaseDotNetObject(long dotNetObjectId) { - // DotNetDispatcher only works with JSRuntimeBase instances. - var jsRuntime = (JSRuntimeBase)JSRuntime.Current; - jsRuntime.ArgSerializerStrategy.ReleaseDotNetObject(dotNetObjectId); + DotNetObjectRefManager.Current.ReleaseDotNetObject(dotNetObjectId); } - private static (MethodInfo, Type[]) GetCachedMethodInfo(string assemblyName, string methodIdentifier) + private static (MethodInfo, Type[]) GetCachedMethodInfo(AssemblyKey assemblyKey, string methodIdentifier) { - if (string.IsNullOrWhiteSpace(assemblyName)) + if (string.IsNullOrWhiteSpace(assemblyKey.AssemblyName)) { - throw new ArgumentException("Cannot be null, empty, or whitespace.", nameof(assemblyName)); + throw new ArgumentException("Cannot be null, empty, or whitespace.", nameof(assemblyKey.AssemblyName)); } if (string.IsNullOrWhiteSpace(methodIdentifier)) @@ -224,23 +282,23 @@ private static (MethodInfo, Type[]) GetCachedMethodInfo(string assemblyName, str throw new ArgumentException("Cannot be null, empty, or whitespace.", nameof(methodIdentifier)); } - var assemblyMethods = _cachedMethodsByAssembly.GetOrAdd(assemblyName, ScanAssemblyForCallableMethods); + var assemblyMethods = _cachedMethodsByAssembly.GetOrAdd(assemblyKey, ScanAssemblyForCallableMethods); if (assemblyMethods.TryGetValue(methodIdentifier, out var result)) { return result; } else { - throw new ArgumentException($"The assembly '{assemblyName}' does not contain a public method with [{nameof(JSInvokableAttribute)}(\"{methodIdentifier}\")]."); + throw new ArgumentException($"The assembly '{assemblyKey.AssemblyName}' does not contain a public method with [{nameof(JSInvokableAttribute)}(\"{methodIdentifier}\")]."); } } - private static IReadOnlyDictionary ScanAssemblyForCallableMethods(string assemblyName) + private static Dictionary ScanAssemblyForCallableMethods(AssemblyKey assemblyKey) { // TODO: Consider looking first for assembly-level attributes (i.e., if there are any, // only use those) to avoid scanning, especially for framework assemblies. - var result = new Dictionary(); - var invokableMethods = GetRequiredLoadedAssembly(assemblyName) + var result = new Dictionary(StringComparer.Ordinal); + var invokableMethods = GetRequiredLoadedAssembly(assemblyKey) .GetExportedTypes() .SelectMany(type => type.GetMethods( BindingFlags.Public | @@ -261,7 +319,7 @@ private static (MethodInfo, Type[]) GetCachedMethodInfo(string assemblyName, str { if (result.ContainsKey(identifier)) { - throw new InvalidOperationException($"The assembly '{assemblyName}' contains more than one " + + throw new InvalidOperationException($"The assembly '{assemblyKey.AssemblyName}' contains more than one " + $"[JSInvokable] method with identifier '{identifier}'. All [JSInvokable] methods within the same " + $"assembly must have different identifiers. You can pass a custom identifier as a parameter to " + $"the [JSInvokable] attribute."); @@ -276,7 +334,7 @@ private static (MethodInfo, Type[]) GetCachedMethodInfo(string assemblyName, str return result; } - private static Assembly GetRequiredLoadedAssembly(string assemblyName) + private static Assembly GetRequiredLoadedAssembly(AssemblyKey assemblyKey) { // We don't want to load assemblies on demand here, because we don't necessarily trust // "assemblyName" to be something the developer intended to load. So only pick from the @@ -284,8 +342,40 @@ private static Assembly GetRequiredLoadedAssembly(string assemblyName) // In some edge cases this might force developers to explicitly call something on the // target assembly (from .NET) before they can invoke its allowed methods from JS. var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies(); - return loadedAssemblies.FirstOrDefault(a => a.GetName().Name.Equals(assemblyName, StringComparison.Ordinal)) - ?? throw new ArgumentException($"There is no loaded assembly with the name '{assemblyName}'."); + return loadedAssemblies.FirstOrDefault(a => new AssemblyKey(a).Equals(assemblyKey)) + ?? throw new ArgumentException($"There is no loaded assembly with the name '{assemblyKey.AssemblyName}'."); } + + private readonly struct AssemblyKey : IEquatable + { + public AssemblyKey(Assembly assembly) + { + Assembly = assembly; + AssemblyName = assembly.GetName().Name; + } + + public AssemblyKey(string assemblyName) + { + Assembly = null; + AssemblyName = assemblyName; + } + + public Assembly Assembly { get; } + + public string AssemblyName { get; } + + public bool Equals(AssemblyKey other) + { + if (Assembly != null && other.Assembly != null) + { + return Assembly == other.Assembly; + } + + return AssemblyName.Equals(other.AssemblyName, StringComparison.Ordinal); + } + + public override int GetHashCode() => StringComparer.Ordinal.GetHashCode(AssemblyName); + } + } } diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRef.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRef.cs index aa62bee34124..1aabc5ad5949 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRef.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRef.cs @@ -1,66 +1,21 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; -using System.Threading; - namespace Microsoft.JSInterop { /// - /// Wraps a JS interop argument, indicating that the value should not be serialized as JSON - /// but instead should be passed as a reference. - /// - /// To avoid leaking memory, the reference must later be disposed by JS code or by .NET code. + /// Provides convenience methods to produce a . /// - public class DotNetObjectRef : IDisposable + public static class DotNetObjectRef { /// - /// Gets the object instance represented by this wrapper. - /// - public object Value { get; } - - // We track an associated IJSRuntime purely so that this class can be IDisposable - // in the normal way. Developers are more likely to use objectRef.Dispose() than - // some less familiar API such as JSRuntime.Current.UntrackObjectRef(objectRef). - private IJSRuntime _attachedToRuntime; - - /// - /// Constructs an instance of . - /// - /// The value being wrapped. - public DotNetObjectRef(object value) - { - Value = value; - } - - /// - /// Ensures the is associated with the specified . - /// Developers do not normally need to invoke this manually, since it is called automatically by - /// framework code. - /// - /// The . - public void EnsureAttachedToJsRuntime(IJSRuntime runtime) - { - // The reason we populate _attachedToRuntime here rather than in the constructor - // is to ensure developers can't accidentally try to reuse DotNetObjectRef across - // different IJSRuntime instances. This method gets called as part of serializing - // the DotNetObjectRef during an interop call. - - var existingRuntime = Interlocked.CompareExchange(ref _attachedToRuntime, runtime, null); - if (existingRuntime != null && existingRuntime != runtime) - { - throw new InvalidOperationException($"The {nameof(DotNetObjectRef)} is already associated with a different {nameof(IJSRuntime)}. Do not attempt to re-use {nameof(DotNetObjectRef)} instances with multiple {nameof(IJSRuntime)} instances."); - } - } - - /// - /// Stops tracking this object reference, allowing it to be garbage collected - /// (if there are no other references to it). Once the instance is disposed, it - /// can no longer be used in interop calls from JavaScript code. + /// Creates a new instance of . /// - public void Dispose() + /// The reference type to track. + /// An instance of . + public static DotNetObjectRef Create(TValue value) where TValue : class { - _attachedToRuntime?.UntrackObjectRef(this); + return new DotNetObjectRef(value); } } } diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRefManager.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRefManager.cs new file mode 100644 index 000000000000..f263716f53f6 --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRefManager.cs @@ -0,0 +1,51 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Concurrent; +using System.Threading; + +namespace Microsoft.JSInterop +{ + internal class DotNetObjectRefManager + { + private long _nextId = 0; // 0 signals no object, but we increment prior to assignment. The first tracked object should have id 1 + private readonly ConcurrentDictionary _trackedRefsById = new ConcurrentDictionary(); + + public static DotNetObjectRefManager Current + { + get + { + if (!(JSRuntime.Current is JSRuntimeBase jsRuntimeBase)) + { + throw new InvalidOperationException("JSRuntime must be set up correctly and must be an instance of JSRuntimeBase to use DotNetObjectRef."); + } + + return jsRuntimeBase.ObjectRefManager; + } + } + + public long TrackObject(IDotNetObjectRef dotNetObjectRef) + { + var dotNetObjectId = Interlocked.Increment(ref _nextId); + _trackedRefsById[dotNetObjectId] = dotNetObjectRef; + + return dotNetObjectId; + } + + public object FindDotNetObject(long dotNetObjectId) + { + return _trackedRefsById.TryGetValue(dotNetObjectId, out var dotNetObjectRef) + ? dotNetObjectRef.Value + : throw new ArgumentException($"There is no tracked object with id '{dotNetObjectId}'. Perhaps the DotNetObjectRef instance was already disposed.", nameof(dotNetObjectId)); + + } + + /// + /// Stops tracking the specified .NET object reference. + /// This may be invoked either by disposing a DotNetObjectRef in .NET code, or via JS interop by calling "dispose" on the corresponding instance in JavaScript code + /// + /// The ID of the . + public void ReleaseDotNetObject(long dotNetObjectId) => _trackedRefsById.TryRemove(dotNetObjectId, out _); + } +} diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRefOfT.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRefOfT.cs new file mode 100644 index 000000000000..8b7035e957cf --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRefOfT.cs @@ -0,0 +1,76 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.ComponentModel; +using System.Text.Json.Serialization; + +namespace Microsoft.JSInterop +{ + /// + /// Wraps a JS interop argument, indicating that the value should not be serialized as JSON + /// but instead should be passed as a reference. + /// + /// To avoid leaking memory, the reference must later be disposed by JS code or by .NET code. + /// + /// The type of the value to wrap. + public sealed class DotNetObjectRef : IDotNetObjectRef, IDisposable where TValue : class + { + private long? _trackingId; + + /// + /// This API is for meant for JSON deserialization and should not be used by user code. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public DotNetObjectRef() + { + } + + /// + /// Initializes a new instance of . + /// + /// The value to pass by reference. + internal DotNetObjectRef(TValue value) + { + Value = value; + _trackingId = DotNetObjectRefManager.Current.TrackObject(this); + } + + /// + /// Gets the object instance represented by this wrapper. + /// + [JsonIgnore] + public TValue Value { get; private set; } + + /// + /// This API is for meant for JSON serialization and should not be used by user code. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public long __dotNetObject + { + get => _trackingId.Value; + set + { + if (_trackingId != null) + { + throw new InvalidOperationException($"{nameof(DotNetObjectRef)} cannot be reinitialized."); + } + + _trackingId = value; + Value = (TValue)DotNetObjectRefManager.Current.FindDotNetObject(value); + } + } + + object IDotNetObjectRef.Value => Value; + + /// + /// Stops tracking this object reference, allowing it to be garbage collected + /// (if there are no other references to it). Once the instance is disposed, it + /// can no longer be used in interop calls from JavaScript code. + /// + public void Dispose() + { + DotNetObjectRefManager.Current.ReleaseDotNetObject(_trackingId.Value); + } + } +} diff --git a/src/JSInterop/Microsoft.JSInterop/src/ICustomArgSerializer.cs b/src/JSInterop/Microsoft.JSInterop/src/ICustomArgSerializer.cs deleted file mode 100644 index f4012af8e9e2..000000000000 --- a/src/JSInterop/Microsoft.JSInterop/src/ICustomArgSerializer.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -namespace Microsoft.JSInterop.Internal -{ - // This is "soft" internal because we're trying to avoid expanding JsonUtil into a sophisticated - // API. Developers who want that would be better served by using a different JSON package - // instead. Also the perf implications of the ICustomArgSerializer approach aren't ideal - // (it forces structs to be boxed, and returning a dictionary means lots more allocations - // and boxing of any value-typed properties). - - /// - /// Internal. Intended for framework use only. - /// - public interface ICustomArgSerializer - { - /// - /// Internal. Intended for framework use only. - /// - object ToJsonPrimitive(); - } -} diff --git a/src/JSInterop/Microsoft.JSInterop/src/IDotNetObjectRef.cs b/src/JSInterop/Microsoft.JSInterop/src/IDotNetObjectRef.cs new file mode 100644 index 000000000000..5f21808a9f24 --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop/src/IDotNetObjectRef.cs @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.JSInterop +{ + internal interface IDotNetObjectRef : IDisposable + { + public object Value { get; } + } +} diff --git a/src/JSInterop/Microsoft.JSInterop/src/IJSRuntime.cs b/src/JSInterop/Microsoft.JSInterop/src/IJSRuntime.cs index b56d1f0089fa..97713bb3c136 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/IJSRuntime.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/IJSRuntime.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Threading.Tasks; namespace Microsoft.JSInterop @@ -13,20 +14,10 @@ public interface IJSRuntime /// /// Invokes the specified JavaScript function asynchronously. /// - /// The JSON-serializable return type. + /// The JSON-serializable return type. /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. /// JSON-serializable arguments. - /// An instance of obtained by JSON-deserializing the return value. - Task InvokeAsync(string identifier, params object[] args); - - /// - /// Stops tracking the .NET object represented by the . - /// This allows it to be garbage collected (if nothing else holds a reference to it) - /// and means the JS-side code can no longer invoke methods on the instance or pass - /// it as an argument to subsequent calls. - /// - /// The reference to stop tracking. - /// This method is called automatically by . - void UntrackObjectRef(DotNetObjectRef dotNetObjectRef); + /// An instance of obtained by JSON-deserializing the return value. + Task InvokeAsync(string identifier, params object[] args); } } diff --git a/src/JSInterop/Microsoft.JSInterop/src/InteropArgSerializerStrategy.cs b/src/JSInterop/Microsoft.JSInterop/src/InteropArgSerializerStrategy.cs deleted file mode 100644 index 663c1cf85aba..000000000000 --- a/src/JSInterop/Microsoft.JSInterop/src/InteropArgSerializerStrategy.cs +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using Microsoft.JSInterop.Internal; -using SimpleJson; -using System; -using System.Collections.Generic; - -namespace Microsoft.JSInterop -{ - internal class InteropArgSerializerStrategy : PocoJsonSerializerStrategy - { - private readonly JSRuntimeBase _jsRuntime; - private const string _dotNetObjectPrefix = "__dotNetObject:"; - private object _storageLock = new object(); - private long _nextId = 1; // Start at 1, because 0 signals "no object" - private Dictionary _trackedRefsById = new Dictionary(); - private Dictionary _trackedIdsByRef = new Dictionary(); - - public InteropArgSerializerStrategy(JSRuntimeBase jsRuntime) - { - _jsRuntime = jsRuntime ?? throw new ArgumentNullException(nameof(jsRuntime)); - } - - protected override bool TrySerializeKnownTypes(object input, out object output) - { - switch (input) - { - case DotNetObjectRef marshalByRefValue: - EnsureDotNetObjectTracked(marshalByRefValue, out var id); - - // Special value format recognized by the code in Microsoft.JSInterop.js - // If we have to make it more clash-resistant, we can do - output = _dotNetObjectPrefix + id; - - return true; - - case ICustomArgSerializer customArgSerializer: - output = customArgSerializer.ToJsonPrimitive(); - return true; - - default: - return base.TrySerializeKnownTypes(input, out output); - } - } - - public override object DeserializeObject(object value, Type type) - { - if (value is string valueString) - { - if (valueString.StartsWith(_dotNetObjectPrefix)) - { - var dotNetObjectId = long.Parse(valueString.Substring(_dotNetObjectPrefix.Length)); - return FindDotNetObject(dotNetObjectId); - } - } - - return base.DeserializeObject(value, type); - } - - public object FindDotNetObject(long dotNetObjectId) - { - lock (_storageLock) - { - return _trackedRefsById.TryGetValue(dotNetObjectId, out var dotNetObjectRef) - ? dotNetObjectRef.Value - : throw new ArgumentException($"There is no tracked object with id '{dotNetObjectId}'. Perhaps the reference was already released.", nameof(dotNetObjectId)); - } - } - - /// - /// Stops tracking the specified .NET object reference. - /// This overload is typically invoked from JS code via JS interop. - /// - /// The ID of the . - public void ReleaseDotNetObject(long dotNetObjectId) - { - lock (_storageLock) - { - if (_trackedRefsById.TryGetValue(dotNetObjectId, out var dotNetObjectRef)) - { - _trackedRefsById.Remove(dotNetObjectId); - _trackedIdsByRef.Remove(dotNetObjectRef); - } - } - } - - /// - /// Stops tracking the specified .NET object reference. - /// This overload is typically invoked from .NET code by . - /// - /// The . - public void ReleaseDotNetObject(DotNetObjectRef dotNetObjectRef) - { - lock (_storageLock) - { - if (_trackedIdsByRef.TryGetValue(dotNetObjectRef, out var dotNetObjectId)) - { - _trackedRefsById.Remove(dotNetObjectId); - _trackedIdsByRef.Remove(dotNetObjectRef); - } - } - } - - private void EnsureDotNetObjectTracked(DotNetObjectRef dotNetObjectRef, out long dotNetObjectId) - { - dotNetObjectRef.EnsureAttachedToJsRuntime(_jsRuntime); - - lock (_storageLock) - { - // Assign an ID only if it doesn't already have one - if (!_trackedIdsByRef.TryGetValue(dotNetObjectRef, out dotNetObjectId)) - { - dotNetObjectId = _nextId++; - _trackedRefsById.Add(dotNetObjectId, dotNetObjectRef); - _trackedIdsByRef.Add(dotNetObjectRef, dotNetObjectId); - } - } - } - } -} diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSAsyncCallResult.cs b/src/JSInterop/Microsoft.JSInterop/src/JSAsyncCallResult.cs index d46517eddc69..1ea8c47995e5 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/JSAsyncCallResult.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/JSAsyncCallResult.cs @@ -1,6 +1,9 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System.ComponentModel; +using System.Text.Json; + namespace Microsoft.JSInterop.Internal { // This type takes care of a special case in handling the result of an async call from @@ -20,17 +23,16 @@ namespace Microsoft.JSInterop.Internal /// /// Intended for framework use only. /// - public class JSAsyncCallResult + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class JSAsyncCallResult { - internal object ResultOrException { get; } - - /// - /// Constructs an instance of . - /// - /// The result of the call. - internal JSAsyncCallResult(object resultOrException) + internal JSAsyncCallResult(JsonDocument document, JsonElement jsonElement) { - ResultOrException = resultOrException; + JsonDocument = document; + JsonElement = jsonElement; } + + internal JsonElement JsonElement { get; } + internal JsonDocument JsonDocument { get; } } } diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntimeBase.cs b/src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntimeBase.cs index 49a47d0595a3..7e6dcdd46207 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntimeBase.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntimeBase.cs @@ -1,6 +1,8 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System.Text.Json.Serialization; + namespace Microsoft.JSInterop { /// @@ -11,14 +13,19 @@ public abstract class JSInProcessRuntimeBase : JSRuntimeBase, IJSInProcessRuntim /// /// Invokes the specified JavaScript function synchronously. /// - /// The JSON-serializable return type. + /// The JSON-serializable return type. /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. /// JSON-serializable arguments. - /// An instance of obtained by JSON-deserializing the return value. - public T Invoke(string identifier, params object[] args) + /// An instance of obtained by JSON-deserializing the return value. + public TValue Invoke(string identifier, params object[] args) { - var resultJson = InvokeJS(identifier, Json.Serialize(args, ArgSerializerStrategy)); - return Json.Deserialize(resultJson, ArgSerializerStrategy); + var resultJson = InvokeJS(identifier, JsonSerializer.ToString(args, JsonSerializerOptionsProvider.Options)); + if (resultJson is null) + { + return default; + } + + return JsonSerializer.Parse(resultJson, JsonSerializerOptionsProvider.Options); } /// diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs b/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs index d18bc7f4feeb..70ab856a89d6 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs @@ -4,8 +4,10 @@ using System; using System.Collections.Concurrent; using System.Runtime.ExceptionServices; +using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; +using Microsoft.JSInterop.Internal; namespace Microsoft.JSInterop { @@ -18,19 +20,7 @@ public abstract class JSRuntimeBase : IJSRuntime private readonly ConcurrentDictionary _pendingTasks = new ConcurrentDictionary(); - internal InteropArgSerializerStrategy ArgSerializerStrategy { get; } - - /// - /// Constructs an instance of . - /// - public JSRuntimeBase() - { - ArgSerializerStrategy = new InteropArgSerializerStrategy(this); - } - - /// - public void UntrackObjectRef(DotNetObjectRef dotNetObjectRef) - => ArgSerializerStrategy.ReleaseDotNetObject(dotNetObjectRef); + internal DotNetObjectRefManager ObjectRefManager { get; } = new DotNetObjectRefManager(); /// /// Invokes the specified JavaScript function asynchronously. @@ -51,9 +41,9 @@ public Task InvokeAsync(string identifier, params object[] args) try { - var argsJson = args?.Length > 0 - ? Json.Serialize(args, ArgSerializerStrategy) - : null; + var argsJson = args?.Length > 0 ? + JsonSerializer.ToString(args, JsonSerializerOptionsProvider.Options) : + null; BeginInvokeJS(taskId, identifier, argsJson); return tcs.Task; } @@ -88,33 +78,32 @@ internal void EndInvokeDotNet(string callId, bool success, object resultOrExcept // We pass 0 as the async handle because we don't want the JS-side code to // send back any notification (we're just providing a result for an existing async call) - BeginInvokeJS(0, "DotNet.jsCallDispatcher.endInvokeDotNetFromJS", Json.Serialize(new[] { - callId, - success, - resultOrException - }, ArgSerializerStrategy)); + var args = JsonSerializer.ToString(new[] { callId, success, resultOrException }, JsonSerializerOptionsProvider.Options); + BeginInvokeJS(0, "DotNet.jsCallDispatcher.endInvokeDotNetFromJS", args); } - internal void EndInvokeJS(long asyncHandle, bool succeeded, object resultOrException) + internal void EndInvokeJS(long asyncHandle, bool succeeded, JSAsyncCallResult asyncCallResult) { - if (!_pendingTasks.TryRemove(asyncHandle, out var tcs)) + using (asyncCallResult?.JsonDocument) { - throw new ArgumentException($"There is no pending task with handle '{asyncHandle}'."); - } - - if (succeeded) - { - var resultType = TaskGenericsUtil.GetTaskCompletionSourceResultType(tcs); - if (resultOrException is SimpleJson.JsonObject || resultOrException is SimpleJson.JsonArray) + if (!_pendingTasks.TryRemove(asyncHandle, out var tcs)) { - resultOrException = ArgSerializerStrategy.DeserializeObject(resultOrException, resultType); + throw new ArgumentException($"There is no pending task with handle '{asyncHandle}'."); } - TaskGenericsUtil.SetTaskCompletionSourceResult(tcs, resultOrException); - } - else - { - TaskGenericsUtil.SetTaskCompletionSourceException(tcs, new JSException(resultOrException.ToString())); + if (succeeded) + { + var resultType = TaskGenericsUtil.GetTaskCompletionSourceResultType(tcs); + var result = asyncCallResult != null ? + JsonSerializer.Parse(asyncCallResult.JsonElement.GetRawText(), resultType, JsonSerializerOptionsProvider.Options) : + null; + TaskGenericsUtil.SetTaskCompletionSourceResult(tcs, result); + } + else + { + var exceptionText = asyncCallResult?.JsonElement.ToString() ?? string.Empty; + TaskGenericsUtil.SetTaskCompletionSourceException(tcs, new JSException(exceptionText)); + } } } } diff --git a/src/JSInterop/Microsoft.JSInterop/src/Json/CamelCase.cs b/src/JSInterop/Microsoft.JSInterop/src/Json/CamelCase.cs deleted file mode 100644 index 8caae1387b08..000000000000 --- a/src/JSInterop/Microsoft.JSInterop/src/Json/CamelCase.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; - -namespace Microsoft.JSInterop -{ - internal static class CamelCase - { - public static string MemberNameToCamelCase(string value) - { - if (string.IsNullOrEmpty(value)) - { - throw new ArgumentException( - $"The value '{value ?? "null"}' is not a valid member name.", - nameof(value)); - } - - // If we don't need to modify the value, bail out without creating a char array - if (!char.IsUpper(value[0])) - { - return value; - } - - // We have to modify at least one character - var chars = value.ToCharArray(); - - var length = chars.Length; - if (length < 2 || !char.IsUpper(chars[1])) - { - // Only the first character needs to be modified - // Note that this branch is functionally necessary, because the 'else' branch below - // never looks at char[1]. It's always looking at the n+2 character. - chars[0] = char.ToLowerInvariant(chars[0]); - } - else - { - // If chars[0] and chars[1] are both upper, then we'll lowercase the first char plus - // any consecutive uppercase ones, stopping if we find any char that is followed by a - // non-uppercase one - var i = 0; - while (i < length) - { - chars[i] = char.ToLowerInvariant(chars[i]); - - i++; - - // If the next-plus-one char isn't also uppercase, then we're now on the last uppercase, so stop - if (i < length - 1 && !char.IsUpper(chars[i + 1])) - { - break; - } - } - } - - return new string(chars); - } - } -} diff --git a/src/JSInterop/Microsoft.JSInterop/src/Json/Json.cs b/src/JSInterop/Microsoft.JSInterop/src/Json/Json.cs deleted file mode 100644 index 7275dfe42720..000000000000 --- a/src/JSInterop/Microsoft.JSInterop/src/Json/Json.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -namespace Microsoft.JSInterop -{ - /// - /// Provides mechanisms for converting between .NET objects and JSON strings for use - /// when making calls to JavaScript functions via . - /// - /// Warning: This is not intended as a general-purpose JSON library. It is only intended - /// for use when making calls via . Eventually its implementation - /// will be replaced by something more general-purpose. - /// - public static class Json - { - /// - /// Serializes the value as a JSON string. - /// - /// The value to serialize. - /// The JSON string. - public static string Serialize(object value) - => SimpleJson.SimpleJson.SerializeObject(value); - - internal static string Serialize(object value, SimpleJson.IJsonSerializerStrategy serializerStrategy) - => SimpleJson.SimpleJson.SerializeObject(value, serializerStrategy); - - /// - /// Deserializes the JSON string, creating an object of the specified generic type. - /// - /// The type of object to create. - /// The JSON string. - /// An object of the specified type. - public static T Deserialize(string json) - => SimpleJson.SimpleJson.DeserializeObject(json); - - internal static T Deserialize(string json, SimpleJson.IJsonSerializerStrategy serializerStrategy) - => SimpleJson.SimpleJson.DeserializeObject(json, serializerStrategy); - } -} diff --git a/src/JSInterop/Microsoft.JSInterop/src/Json/SimpleJson/README.txt b/src/JSInterop/Microsoft.JSInterop/src/Json/SimpleJson/README.txt deleted file mode 100644 index 5e58eb710615..000000000000 --- a/src/JSInterop/Microsoft.JSInterop/src/Json/SimpleJson/README.txt +++ /dev/null @@ -1,24 +0,0 @@ -SimpleJson is from https://github.com/facebook-csharp-sdk/simple-json - -LICENSE (from https://github.com/facebook-csharp-sdk/simple-json/blob/08b6871e8f63e866810d25e7a03c48502c9a234b/LICENSE.txt): -===== -Copyright (c) 2011, The Outercurve Foundation - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/src/JSInterop/Microsoft.JSInterop/src/Json/SimpleJson/SimpleJson.cs b/src/JSInterop/Microsoft.JSInterop/src/Json/SimpleJson/SimpleJson.cs deleted file mode 100644 index d12c6fae30c7..000000000000 --- a/src/JSInterop/Microsoft.JSInterop/src/Json/SimpleJson/SimpleJson.cs +++ /dev/null @@ -1,2201 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) 2011, The Outercurve Foundation. -// -// Licensed under the MIT License (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.opensource.org/licenses/mit-license.php -// -// 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. -// -// Nathan Totten (ntotten.com), Jim Zimmerman (jimzimmerman.com) and Prabir Shrestha (prabir.me) -// https://github.com/facebook-csharp-sdk/simple-json -//----------------------------------------------------------------------- - -// VERSION: - -// NOTE: uncomment the following line to make SimpleJson class internal. -#define SIMPLE_JSON_INTERNAL - -// NOTE: uncomment the following line to make JsonArray and JsonObject class internal. -#define SIMPLE_JSON_OBJARRAYINTERNAL - -// NOTE: uncomment the following line to enable dynamic support. -//#define SIMPLE_JSON_DYNAMIC - -// NOTE: uncomment the following line to enable DataContract support. -//#define SIMPLE_JSON_DATACONTRACT - -// NOTE: uncomment the following line to enable IReadOnlyCollection and IReadOnlyList support. -//#define SIMPLE_JSON_READONLY_COLLECTIONS - -// NOTE: uncomment the following line to disable linq expressions/compiled lambda (better performance) instead of method.invoke(). -// define if you are using .net framework <= 3.0 or < WP7.5 -#define SIMPLE_JSON_NO_LINQ_EXPRESSION - -// NOTE: uncomment the following line if you are compiling under Window Metro style application/library. -// usually already defined in properties -//#define NETFX_CORE; - -// If you are targetting WinStore, WP8 and NET4.5+ PCL make sure to #define SIMPLE_JSON_TYPEINFO; - -// original json parsing code from http://techblog.procurios.nl/k/618/news/view/14605/14863/How-do-I-write-my-own-parser-for-JSON.html - -#if NETFX_CORE -#define SIMPLE_JSON_TYPEINFO -#endif - -using System; -using System.CodeDom.Compiler; -using System.Collections; -using System.Collections.Generic; -#if !SIMPLE_JSON_NO_LINQ_EXPRESSION -using System.Linq.Expressions; -#endif -using System.ComponentModel; -using System.Diagnostics.CodeAnalysis; -#if SIMPLE_JSON_DYNAMIC -using System.Dynamic; -#endif -using System.Globalization; -using System.Reflection; -using System.Runtime.Serialization; -using System.Text; -using Microsoft.JSInterop; -using SimpleJson.Reflection; - -// ReSharper disable LoopCanBeConvertedToQuery -// ReSharper disable RedundantExplicitArrayCreation -// ReSharper disable SuggestUseVarKeywordEvident -namespace SimpleJson -{ - /// - /// Represents the json array. - /// - [GeneratedCode("simple-json", "1.0.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] -#if SIMPLE_JSON_OBJARRAYINTERNAL - internal -#else - public -#endif - class JsonArray : List - { - /// - /// Initializes a new instance of the class. - /// - public JsonArray() { } - - /// - /// Initializes a new instance of the class. - /// - /// The capacity of the json array. - public JsonArray(int capacity) : base(capacity) { } - - /// - /// The json representation of the array. - /// - /// The json representation of the array. - public override string ToString() - { - return SimpleJson.SerializeObject(this) ?? string.Empty; - } - } - - /// - /// Represents the json object. - /// - [GeneratedCode("simple-json", "1.0.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] -#if SIMPLE_JSON_OBJARRAYINTERNAL - internal -#else - public -#endif - class JsonObject : -#if SIMPLE_JSON_DYNAMIC - DynamicObject, -#endif - IDictionary - { - /// - /// The internal member dictionary. - /// - private readonly Dictionary _members; - - /// - /// Initializes a new instance of . - /// - public JsonObject() - { - _members = new Dictionary(); - } - - /// - /// Initializes a new instance of . - /// - /// The implementation to use when comparing keys, or null to use the default for the type of the key. - public JsonObject(IEqualityComparer comparer) - { - _members = new Dictionary(comparer); - } - - /// - /// Gets the at the specified index. - /// - /// - public object this[int index] - { - get { return GetAtIndex(_members, index); } - } - - internal static object GetAtIndex(IDictionary obj, int index) - { - if (obj == null) - throw new ArgumentNullException("obj"); - if (index >= obj.Count) - throw new ArgumentOutOfRangeException("index"); - int i = 0; - foreach (KeyValuePair o in obj) - if (i++ == index) return o.Value; - return null; - } - - /// - /// Adds the specified key. - /// - /// The key. - /// The value. - public void Add(string key, object value) - { - _members.Add(key, value); - } - - /// - /// Determines whether the specified key contains key. - /// - /// The key. - /// - /// true if the specified key contains key; otherwise, false. - /// - public bool ContainsKey(string key) - { - return _members.ContainsKey(key); - } - - /// - /// Gets the keys. - /// - /// The keys. - public ICollection Keys - { - get { return _members.Keys; } - } - - /// - /// Removes the specified key. - /// - /// The key. - /// - public bool Remove(string key) - { - return _members.Remove(key); - } - - /// - /// Tries the get value. - /// - /// The key. - /// The value. - /// - public bool TryGetValue(string key, out object value) - { - return _members.TryGetValue(key, out value); - } - - /// - /// Gets the values. - /// - /// The values. - public ICollection Values - { - get { return _members.Values; } - } - - /// - /// Gets or sets the with the specified key. - /// - /// - public object this[string key] - { - get { return _members[key]; } - set { _members[key] = value; } - } - - /// - /// Adds the specified item. - /// - /// The item. - public void Add(KeyValuePair item) - { - _members.Add(item.Key, item.Value); - } - - /// - /// Clears this instance. - /// - public void Clear() - { - _members.Clear(); - } - - /// - /// Determines whether [contains] [the specified item]. - /// - /// The item. - /// - /// true if [contains] [the specified item]; otherwise, false. - /// - public bool Contains(KeyValuePair item) - { - return _members.ContainsKey(item.Key) && _members[item.Key] == item.Value; - } - - /// - /// Copies to. - /// - /// The array. - /// Index of the array. - public void CopyTo(KeyValuePair[] array, int arrayIndex) - { - if (array == null) throw new ArgumentNullException("array"); - int num = Count; - foreach (KeyValuePair kvp in this) - { - array[arrayIndex++] = kvp; - if (--num <= 0) - return; - } - } - - /// - /// Gets the count. - /// - /// The count. - public int Count - { - get { return _members.Count; } - } - - /// - /// Gets a value indicating whether this instance is read only. - /// - /// - /// true if this instance is read only; otherwise, false. - /// - public bool IsReadOnly - { - get { return false; } - } - - /// - /// Removes the specified item. - /// - /// The item. - /// - public bool Remove(KeyValuePair item) - { - return _members.Remove(item.Key); - } - - /// - /// Gets the enumerator. - /// - /// - public IEnumerator> GetEnumerator() - { - return _members.GetEnumerator(); - } - - /// - /// Returns an enumerator that iterates through a collection. - /// - /// - /// An object that can be used to iterate through the collection. - /// - IEnumerator IEnumerable.GetEnumerator() - { - return _members.GetEnumerator(); - } - - /// - /// Returns a json that represents the current . - /// - /// - /// A json that represents the current . - /// - public override string ToString() - { - return SimpleJson.SerializeObject(this); - } - -#if SIMPLE_JSON_DYNAMIC - /// - /// Provides implementation for type conversion operations. Classes derived from the class can override this method to specify dynamic behavior for operations that convert an object from one type to another. - /// - /// Provides information about the conversion operation. The binder.Type property provides the type to which the object must be converted. For example, for the statement (String)sampleObject in C# (CType(sampleObject, Type) in Visual Basic), where sampleObject is an instance of the class derived from the class, binder.Type returns the type. The binder.Explicit property provides information about the kind of conversion that occurs. It returns true for explicit conversion and false for implicit conversion. - /// The result of the type conversion operation. - /// - /// Alwasy returns true. - /// - public override bool TryConvert(ConvertBinder binder, out object result) - { - // - if (binder == null) - throw new ArgumentNullException("binder"); - // - Type targetType = binder.Type; - - if ((targetType == typeof(IEnumerable)) || - (targetType == typeof(IEnumerable>)) || - (targetType == typeof(IDictionary)) || - (targetType == typeof(IDictionary))) - { - result = this; - return true; - } - - return base.TryConvert(binder, out result); - } - - /// - /// Provides the implementation for operations that delete an object member. This method is not intended for use in C# or Visual Basic. - /// - /// Provides information about the deletion. - /// - /// Alwasy returns true. - /// - public override bool TryDeleteMember(DeleteMemberBinder binder) - { - // - if (binder == null) - throw new ArgumentNullException("binder"); - // - return _members.Remove(binder.Name); - } - - /// - /// Provides the implementation for operations that get a value by index. Classes derived from the class can override this method to specify dynamic behavior for indexing operations. - /// - /// Provides information about the operation. - /// The indexes that are used in the operation. For example, for the sampleObject[3] operation in C# (sampleObject(3) in Visual Basic), where sampleObject is derived from the DynamicObject class, is equal to 3. - /// The result of the index operation. - /// - /// Alwasy returns true. - /// - public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) - { - if (indexes == null) throw new ArgumentNullException("indexes"); - if (indexes.Length == 1) - { - result = ((IDictionary)this)[(string)indexes[0]]; - return true; - } - result = null; - return true; - } - - /// - /// Provides the implementation for operations that get member values. Classes derived from the class can override this method to specify dynamic behavior for operations such as getting a value for a property. - /// - /// Provides information about the object that called the dynamic operation. The binder.Name property provides the name of the member on which the dynamic operation is performed. For example, for the Console.WriteLine(sampleObject.SampleProperty) statement, where sampleObject is an instance of the class derived from the class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies whether the member name is case-sensitive. - /// The result of the get operation. For example, if the method is called for a property, you can assign the property value to . - /// - /// Alwasy returns true. - /// - public override bool TryGetMember(GetMemberBinder binder, out object result) - { - object value; - if (_members.TryGetValue(binder.Name, out value)) - { - result = value; - return true; - } - result = null; - return true; - } - - /// - /// Provides the implementation for operations that set a value by index. Classes derived from the class can override this method to specify dynamic behavior for operations that access objects by a specified index. - /// - /// Provides information about the operation. - /// The indexes that are used in the operation. For example, for the sampleObject[3] = 10 operation in C# (sampleObject(3) = 10 in Visual Basic), where sampleObject is derived from the class, is equal to 3. - /// The value to set to the object that has the specified index. For example, for the sampleObject[3] = 10 operation in C# (sampleObject(3) = 10 in Visual Basic), where sampleObject is derived from the class, is equal to 10. - /// - /// true if the operation is successful; otherwise, false. If this method returns false, the run-time binder of the language determines the behavior. (In most cases, a language-specific run-time exception is thrown. - /// - public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value) - { - if (indexes == null) throw new ArgumentNullException("indexes"); - if (indexes.Length == 1) - { - ((IDictionary)this)[(string)indexes[0]] = value; - return true; - } - return base.TrySetIndex(binder, indexes, value); - } - - /// - /// Provides the implementation for operations that set member values. Classes derived from the class can override this method to specify dynamic behavior for operations such as setting a value for a property. - /// - /// Provides information about the object that called the dynamic operation. The binder.Name property provides the name of the member to which the value is being assigned. For example, for the statement sampleObject.SampleProperty = "Test", where sampleObject is an instance of the class derived from the class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies whether the member name is case-sensitive. - /// The value to set to the member. For example, for sampleObject.SampleProperty = "Test", where sampleObject is an instance of the class derived from the class, the is "Test". - /// - /// true if the operation is successful; otherwise, false. If this method returns false, the run-time binder of the language determines the behavior. (In most cases, a language-specific run-time exception is thrown.) - /// - public override bool TrySetMember(SetMemberBinder binder, object value) - { - // - if (binder == null) - throw new ArgumentNullException("binder"); - // - _members[binder.Name] = value; - return true; - } - - /// - /// Returns the enumeration of all dynamic member names. - /// - /// - /// A sequence that contains dynamic member names. - /// - public override IEnumerable GetDynamicMemberNames() - { - foreach (var key in Keys) - yield return key; - } -#endif - } -} - -namespace SimpleJson -{ - /// - /// This class encodes and decodes JSON strings. - /// Spec. details, see http://www.json.org/ - /// - /// JSON uses Arrays and Objects. These correspond here to the datatypes JsonArray(IList<object>) and JsonObject(IDictionary<string,object>). - /// All numbers are parsed to doubles. - /// - [GeneratedCode("simple-json", "1.0.0")] -#if SIMPLE_JSON_INTERNAL - internal -#else - public -#endif - static class SimpleJson - { - private const int TOKEN_NONE = 0; - private const int TOKEN_CURLY_OPEN = 1; - private const int TOKEN_CURLY_CLOSE = 2; - private const int TOKEN_SQUARED_OPEN = 3; - private const int TOKEN_SQUARED_CLOSE = 4; - private const int TOKEN_COLON = 5; - private const int TOKEN_COMMA = 6; - private const int TOKEN_STRING = 7; - private const int TOKEN_NUMBER = 8; - private const int TOKEN_TRUE = 9; - private const int TOKEN_FALSE = 10; - private const int TOKEN_NULL = 11; - private const int BUILDER_CAPACITY = 2000; - - private static readonly char[] EscapeTable; - private static readonly char[] EscapeCharacters = new char[] { '"', '\\', '\b', '\f', '\n', '\r', '\t' }; - private static readonly string EscapeCharactersString = new string(EscapeCharacters); - - static SimpleJson() - { - EscapeTable = new char[93]; - EscapeTable['"'] = '"'; - EscapeTable['\\'] = '\\'; - EscapeTable['\b'] = 'b'; - EscapeTable['\f'] = 'f'; - EscapeTable['\n'] = 'n'; - EscapeTable['\r'] = 'r'; - EscapeTable['\t'] = 't'; - } - - /// - /// Parses the string json into a value - /// - /// A JSON string. - /// An IList<object>, a IDictionary<string,object>, a double, a string, null, true, or false - public static object DeserializeObject(string json) - { - object obj; - if (TryDeserializeObject(json, out obj)) - return obj; - throw new SerializationException("Invalid JSON string"); - } - - /// - /// Try parsing the json string into a value. - /// - /// - /// A JSON string. - /// - /// - /// The object. - /// - /// - /// Returns true if successful otherwise false. - /// - [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification="Need to support .NET 2")] - public static bool TryDeserializeObject(string json, out object obj) - { - bool success = true; - if (json != null) - { - char[] charArray = json.ToCharArray(); - int index = 0; - obj = ParseValue(charArray, ref index, ref success); - } - else - obj = null; - - return success; - } - - public static object DeserializeObject(string json, Type type, IJsonSerializerStrategy jsonSerializerStrategy) - { - object jsonObject = DeserializeObject(json); - return type == null || jsonObject != null && ReflectionUtils.IsAssignableFrom(jsonObject.GetType(), type) - ? jsonObject - : (jsonSerializerStrategy ?? CurrentJsonSerializerStrategy).DeserializeObject(jsonObject, type); - } - - public static object DeserializeObject(string json, Type type) - { - return DeserializeObject(json, type, null); - } - - public static T DeserializeObject(string json, IJsonSerializerStrategy jsonSerializerStrategy) - { - return (T)DeserializeObject(json, typeof(T), jsonSerializerStrategy); - } - - public static T DeserializeObject(string json) - { - return (T)DeserializeObject(json, typeof(T), null); - } - - /// - /// Converts a IDictionary<string,object> / IList<object> object into a JSON string - /// - /// A IDictionary<string,object> / IList<object> - /// Serializer strategy to use - /// A JSON encoded string, or null if object 'json' is not serializable - public static string SerializeObject(object json, IJsonSerializerStrategy jsonSerializerStrategy) - { - StringBuilder builder = new StringBuilder(BUILDER_CAPACITY); - bool success = SerializeValue(jsonSerializerStrategy, json, builder); - return (success ? builder.ToString() : null); - } - - public static string SerializeObject(object json) - { - return SerializeObject(json, CurrentJsonSerializerStrategy); - } - - public static string EscapeToJavascriptString(string jsonString) - { - if (string.IsNullOrEmpty(jsonString)) - return jsonString; - - StringBuilder sb = new StringBuilder(); - char c; - - for (int i = 0; i < jsonString.Length; ) - { - c = jsonString[i++]; - - if (c == '\\') - { - int remainingLength = jsonString.Length - i; - if (remainingLength >= 2) - { - char lookahead = jsonString[i]; - if (lookahead == '\\') - { - sb.Append('\\'); - ++i; - } - else if (lookahead == '"') - { - sb.Append("\""); - ++i; - } - else if (lookahead == 't') - { - sb.Append('\t'); - ++i; - } - else if (lookahead == 'b') - { - sb.Append('\b'); - ++i; - } - else if (lookahead == 'n') - { - sb.Append('\n'); - ++i; - } - else if (lookahead == 'r') - { - sb.Append('\r'); - ++i; - } - } - } - else - { - sb.Append(c); - } - } - return sb.ToString(); - } - - static IDictionary ParseObject(char[] json, ref int index, ref bool success) - { - IDictionary table = new JsonObject(); - int token; - - // { - NextToken(json, ref index); - - bool done = false; - while (!done) - { - token = LookAhead(json, index); - if (token == TOKEN_NONE) - { - success = false; - return null; - } - else if (token == TOKEN_COMMA) - NextToken(json, ref index); - else if (token == TOKEN_CURLY_CLOSE) - { - NextToken(json, ref index); - return table; - } - else - { - // name - string name = ParseString(json, ref index, ref success); - if (!success) - { - success = false; - return null; - } - // : - token = NextToken(json, ref index); - if (token != TOKEN_COLON) - { - success = false; - return null; - } - // value - object value = ParseValue(json, ref index, ref success); - if (!success) - { - success = false; - return null; - } - table[name] = value; - } - } - return table; - } - - static JsonArray ParseArray(char[] json, ref int index, ref bool success) - { - JsonArray array = new JsonArray(); - - // [ - NextToken(json, ref index); - - bool done = false; - while (!done) - { - int token = LookAhead(json, index); - if (token == TOKEN_NONE) - { - success = false; - return null; - } - else if (token == TOKEN_COMMA) - NextToken(json, ref index); - else if (token == TOKEN_SQUARED_CLOSE) - { - NextToken(json, ref index); - break; - } - else - { - object value = ParseValue(json, ref index, ref success); - if (!success) - return null; - array.Add(value); - } - } - return array; - } - - static object ParseValue(char[] json, ref int index, ref bool success) - { - switch (LookAhead(json, index)) - { - case TOKEN_STRING: - return ParseString(json, ref index, ref success); - case TOKEN_NUMBER: - return ParseNumber(json, ref index, ref success); - case TOKEN_CURLY_OPEN: - return ParseObject(json, ref index, ref success); - case TOKEN_SQUARED_OPEN: - return ParseArray(json, ref index, ref success); - case TOKEN_TRUE: - NextToken(json, ref index); - return true; - case TOKEN_FALSE: - NextToken(json, ref index); - return false; - case TOKEN_NULL: - NextToken(json, ref index); - return null; - case TOKEN_NONE: - break; - } - success = false; - return null; - } - - static string ParseString(char[] json, ref int index, ref bool success) - { - StringBuilder s = new StringBuilder(BUILDER_CAPACITY); - char c; - - EatWhitespace(json, ref index); - - // " - c = json[index++]; - bool complete = false; - while (!complete) - { - if (index == json.Length) - break; - - c = json[index++]; - if (c == '"') - { - complete = true; - break; - } - else if (c == '\\') - { - if (index == json.Length) - break; - c = json[index++]; - if (c == '"') - s.Append('"'); - else if (c == '\\') - s.Append('\\'); - else if (c == '/') - s.Append('/'); - else if (c == 'b') - s.Append('\b'); - else if (c == 'f') - s.Append('\f'); - else if (c == 'n') - s.Append('\n'); - else if (c == 'r') - s.Append('\r'); - else if (c == 't') - s.Append('\t'); - else if (c == 'u') - { - int remainingLength = json.Length - index; - if (remainingLength >= 4) - { - // parse the 32 bit hex into an integer codepoint - uint codePoint; - if (!(success = UInt32.TryParse(new string(json, index, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out codePoint))) - return ""; - - // convert the integer codepoint to a unicode char and add to string - if (0xD800 <= codePoint && codePoint <= 0xDBFF) // if high surrogate - { - index += 4; // skip 4 chars - remainingLength = json.Length - index; - if (remainingLength >= 6) - { - uint lowCodePoint; - if (new string(json, index, 2) == "\\u" && UInt32.TryParse(new string(json, index + 2, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out lowCodePoint)) - { - if (0xDC00 <= lowCodePoint && lowCodePoint <= 0xDFFF) // if low surrogate - { - s.Append((char)codePoint); - s.Append((char)lowCodePoint); - index += 6; // skip 6 chars - continue; - } - } - } - success = false; // invalid surrogate pair - return ""; - } - s.Append(ConvertFromUtf32((int)codePoint)); - // skip 4 chars - index += 4; - } - else - break; - } - } - else - s.Append(c); - } - if (!complete) - { - success = false; - return null; - } - return s.ToString(); - } - - private static string ConvertFromUtf32(int utf32) - { - // http://www.java2s.com/Open-Source/CSharp/2.6.4-mono-.net-core/System/System/Char.cs.htm - if (utf32 < 0 || utf32 > 0x10FFFF) - throw new ArgumentOutOfRangeException("utf32", "The argument must be from 0 to 0x10FFFF."); - if (0xD800 <= utf32 && utf32 <= 0xDFFF) - throw new ArgumentOutOfRangeException("utf32", "The argument must not be in surrogate pair range."); - if (utf32 < 0x10000) - return new string((char)utf32, 1); - utf32 -= 0x10000; - return new string(new char[] { (char)((utf32 >> 10) + 0xD800), (char)(utf32 % 0x0400 + 0xDC00) }); - } - - static object ParseNumber(char[] json, ref int index, ref bool success) - { - EatWhitespace(json, ref index); - int lastIndex = GetLastIndexOfNumber(json, index); - int charLength = (lastIndex - index) + 1; - object returnNumber; - string str = new string(json, index, charLength); - if (str.IndexOf(".", StringComparison.OrdinalIgnoreCase) != -1 || str.IndexOf("e", StringComparison.OrdinalIgnoreCase) != -1) - { - double number; - success = double.TryParse(new string(json, index, charLength), NumberStyles.Any, CultureInfo.InvariantCulture, out number); - returnNumber = number; - } - else - { - long number; - success = long.TryParse(new string(json, index, charLength), NumberStyles.Any, CultureInfo.InvariantCulture, out number); - returnNumber = number; - } - index = lastIndex + 1; - return returnNumber; - } - - static int GetLastIndexOfNumber(char[] json, int index) - { - int lastIndex; - for (lastIndex = index; lastIndex < json.Length; lastIndex++) - if ("0123456789+-.eE".IndexOf(json[lastIndex]) == -1) break; - return lastIndex - 1; - } - - static void EatWhitespace(char[] json, ref int index) - { - for (; index < json.Length; index++) { - switch (json[index]) { - case ' ': - case '\t': - case '\n': - case '\r': - case '\b': - case '\f': - break; - default: - return; - } - } - } - - static int LookAhead(char[] json, int index) - { - int saveIndex = index; - return NextToken(json, ref saveIndex); - } - - [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] - static int NextToken(char[] json, ref int index) - { - EatWhitespace(json, ref index); - if (index == json.Length) - return TOKEN_NONE; - char c = json[index]; - index++; - switch (c) - { - case '{': - return TOKEN_CURLY_OPEN; - case '}': - return TOKEN_CURLY_CLOSE; - case '[': - return TOKEN_SQUARED_OPEN; - case ']': - return TOKEN_SQUARED_CLOSE; - case ',': - return TOKEN_COMMA; - case '"': - return TOKEN_STRING; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case '-': - return TOKEN_NUMBER; - case ':': - return TOKEN_COLON; - } - index--; - int remainingLength = json.Length - index; - // false - if (remainingLength >= 5) - { - if (json[index] == 'f' && json[index + 1] == 'a' && json[index + 2] == 'l' && json[index + 3] == 's' && json[index + 4] == 'e') - { - index += 5; - return TOKEN_FALSE; - } - } - // true - if (remainingLength >= 4) - { - if (json[index] == 't' && json[index + 1] == 'r' && json[index + 2] == 'u' && json[index + 3] == 'e') - { - index += 4; - return TOKEN_TRUE; - } - } - // null - if (remainingLength >= 4) - { - if (json[index] == 'n' && json[index + 1] == 'u' && json[index + 2] == 'l' && json[index + 3] == 'l') - { - index += 4; - return TOKEN_NULL; - } - } - return TOKEN_NONE; - } - - static bool SerializeValue(IJsonSerializerStrategy jsonSerializerStrategy, object value, StringBuilder builder) - { - bool success = true; - string stringValue = value as string; - if (stringValue != null) - success = SerializeString(stringValue, builder); - else - { - IDictionary dict = value as IDictionary; - if (dict != null) - { - success = SerializeObject(jsonSerializerStrategy, dict.Keys, dict.Values, builder); - } - else - { - IDictionary stringDictionary = value as IDictionary; - if (stringDictionary != null) - { - success = SerializeObject(jsonSerializerStrategy, stringDictionary.Keys, stringDictionary.Values, builder); - } - else - { - IEnumerable enumerableValue = value as IEnumerable; - if (enumerableValue != null) - success = SerializeArray(jsonSerializerStrategy, enumerableValue, builder); - else if (IsNumeric(value)) - success = SerializeNumber(value, builder); - else if (value is bool) - builder.Append((bool)value ? "true" : "false"); - else if (value == null) - builder.Append("null"); - else - { - object serializedObject; - success = jsonSerializerStrategy.TrySerializeNonPrimitiveObject(value, out serializedObject); - if (success) - SerializeValue(jsonSerializerStrategy, serializedObject, builder); - } - } - } - } - return success; - } - - static bool SerializeObject(IJsonSerializerStrategy jsonSerializerStrategy, IEnumerable keys, IEnumerable values, StringBuilder builder) - { - builder.Append("{"); - IEnumerator ke = keys.GetEnumerator(); - IEnumerator ve = values.GetEnumerator(); - bool first = true; - while (ke.MoveNext() && ve.MoveNext()) - { - object key = ke.Current; - object value = ve.Current; - if (!first) - builder.Append(","); - string stringKey = key as string; - if (stringKey != null) - SerializeString(stringKey, builder); - else - if (!SerializeValue(jsonSerializerStrategy, value, builder)) return false; - builder.Append(":"); - if (!SerializeValue(jsonSerializerStrategy, value, builder)) - return false; - first = false; - } - builder.Append("}"); - return true; - } - - static bool SerializeArray(IJsonSerializerStrategy jsonSerializerStrategy, IEnumerable anArray, StringBuilder builder) - { - builder.Append("["); - bool first = true; - foreach (object value in anArray) - { - if (!first) - builder.Append(","); - if (!SerializeValue(jsonSerializerStrategy, value, builder)) - return false; - first = false; - } - builder.Append("]"); - return true; - } - - static bool SerializeString(string aString, StringBuilder builder) - { - // Happy path if there's nothing to be escaped. IndexOfAny is highly optimized (and unmanaged) - if (aString.IndexOfAny(EscapeCharacters) == -1) - { - builder.Append('"'); - builder.Append(aString); - builder.Append('"'); - - return true; - } - - builder.Append('"'); - int safeCharacterCount = 0; - char[] charArray = aString.ToCharArray(); - - for (int i = 0; i < charArray.Length; i++) - { - char c = charArray[i]; - - // Non ascii characters are fine, buffer them up and send them to the builder - // in larger chunks if possible. The escape table is a 1:1 translation table - // with \0 [default(char)] denoting a safe character. - if (c >= EscapeTable.Length || EscapeTable[c] == default(char)) - { - safeCharacterCount++; - } - else - { - if (safeCharacterCount > 0) - { - builder.Append(charArray, i - safeCharacterCount, safeCharacterCount); - safeCharacterCount = 0; - } - - builder.Append('\\'); - builder.Append(EscapeTable[c]); - } - } - - if (safeCharacterCount > 0) - { - builder.Append(charArray, charArray.Length - safeCharacterCount, safeCharacterCount); - } - - builder.Append('"'); - return true; - } - - static bool SerializeNumber(object number, StringBuilder builder) - { - if (number is long) - builder.Append(((long)number).ToString(CultureInfo.InvariantCulture)); - else if (number is ulong) - builder.Append(((ulong)number).ToString(CultureInfo.InvariantCulture)); - else if (number is int) - builder.Append(((int)number).ToString(CultureInfo.InvariantCulture)); - else if (number is uint) - builder.Append(((uint)number).ToString(CultureInfo.InvariantCulture)); - else if (number is decimal) - builder.Append(((decimal)number).ToString(CultureInfo.InvariantCulture)); - else if (number is float) - builder.Append(((float)number).ToString(CultureInfo.InvariantCulture)); - else - builder.Append(Convert.ToDouble(number, CultureInfo.InvariantCulture).ToString("r", CultureInfo.InvariantCulture)); - return true; - } - - /// - /// Determines if a given object is numeric in any way - /// (can be integer, double, null, etc). - /// - static bool IsNumeric(object value) - { - if (value is sbyte) return true; - if (value is byte) return true; - if (value is short) return true; - if (value is ushort) return true; - if (value is int) return true; - if (value is uint) return true; - if (value is long) return true; - if (value is ulong) return true; - if (value is float) return true; - if (value is double) return true; - if (value is decimal) return true; - return false; - } - - private static IJsonSerializerStrategy _currentJsonSerializerStrategy; - public static IJsonSerializerStrategy CurrentJsonSerializerStrategy - { - get - { - return _currentJsonSerializerStrategy ?? - (_currentJsonSerializerStrategy = -#if SIMPLE_JSON_DATACONTRACT - DataContractJsonSerializerStrategy -#else - PocoJsonSerializerStrategy -#endif -); - } - set - { - _currentJsonSerializerStrategy = value; - } - } - - private static PocoJsonSerializerStrategy _pocoJsonSerializerStrategy; - [EditorBrowsable(EditorBrowsableState.Advanced)] - public static PocoJsonSerializerStrategy PocoJsonSerializerStrategy - { - get - { - return _pocoJsonSerializerStrategy ?? (_pocoJsonSerializerStrategy = new PocoJsonSerializerStrategy()); - } - } - -#if SIMPLE_JSON_DATACONTRACT - - private static DataContractJsonSerializerStrategy _dataContractJsonSerializerStrategy; - [System.ComponentModel.EditorBrowsable(EditorBrowsableState.Advanced)] - public static DataContractJsonSerializerStrategy DataContractJsonSerializerStrategy - { - get - { - return _dataContractJsonSerializerStrategy ?? (_dataContractJsonSerializerStrategy = new DataContractJsonSerializerStrategy()); - } - } - -#endif - } - - [GeneratedCode("simple-json", "1.0.0")] -#if SIMPLE_JSON_INTERNAL - internal -#else - public -#endif - interface IJsonSerializerStrategy - { - [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification="Need to support .NET 2")] - bool TrySerializeNonPrimitiveObject(object input, out object output); - object DeserializeObject(object value, Type type); - } - - [GeneratedCode("simple-json", "1.0.0")] -#if SIMPLE_JSON_INTERNAL - internal -#else - public -#endif - class PocoJsonSerializerStrategy : IJsonSerializerStrategy - { - internal IDictionary ConstructorCache; - internal IDictionary> GetCache; - internal IDictionary>> SetCache; - - internal static readonly Type[] EmptyTypes = new Type[0]; - internal static readonly Type[] ArrayConstructorParameterTypes = new Type[] { typeof(int) }; - - private static readonly string[] Iso8601Format = new string[] - { - @"yyyy-MM-dd\THH:mm:ss.FFFFFFF\Z", - @"yyyy-MM-dd\THH:mm:ss\Z", - @"yyyy-MM-dd\THH:mm:ssK" - }; - - public PocoJsonSerializerStrategy() - { - ConstructorCache = new ReflectionUtils.ThreadSafeDictionary(ConstructorDelegateFactory); - GetCache = new ReflectionUtils.ThreadSafeDictionary>(GetterValueFactory); - SetCache = new ReflectionUtils.ThreadSafeDictionary>>(SetterValueFactory); - } - - protected virtual string MapClrMemberNameToJsonFieldName(string clrPropertyName) - { - return CamelCase.MemberNameToCamelCase(clrPropertyName); - } - - internal virtual ReflectionUtils.ConstructorDelegate ConstructorDelegateFactory(Type key) - { - // We need List(int) constructor so that DeserializeObject method will work for generating IList-declared values - var needsCapacityArgument = key.IsArray || key.IsConstructedGenericType && key.GetGenericTypeDefinition() == typeof(List<>); - return ReflectionUtils.GetConstructor(key, needsCapacityArgument ? ArrayConstructorParameterTypes : EmptyTypes); - } - - internal virtual IDictionary GetterValueFactory(Type type) - { - IDictionary result = new Dictionary(); - foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) - { - if (propertyInfo.CanRead) - { - MethodInfo getMethod = ReflectionUtils.GetGetterMethodInfo(propertyInfo); - if (getMethod.IsStatic || !getMethod.IsPublic) - continue; - result[MapClrMemberNameToJsonFieldName(propertyInfo.Name)] = ReflectionUtils.GetGetMethod(propertyInfo); - } - } - foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) - { - if (fieldInfo.IsStatic || !fieldInfo.IsPublic) - continue; - result[MapClrMemberNameToJsonFieldName(fieldInfo.Name)] = ReflectionUtils.GetGetMethod(fieldInfo); - } - return result; - } - - internal virtual IDictionary> SetterValueFactory(Type type) - { - // BLAZOR-SPECIFIC MODIFICATION FROM STOCK SIMPLEJSON: - // - // For incoming keys we match case-insensitively. But if two .NET properties differ only by case, - // it's ambiguous which should be used: the one that matches the incoming JSON exactly, or the - // one that uses 'correct' PascalCase corresponding to the incoming camelCase? What if neither - // meets these descriptions? - // - // To resolve this: - // - If multiple public properties differ only by case, we throw - // - If multiple public fields differ only by case, we throw - // - If there's a public property and a public field that differ only by case, we prefer the property - // This unambiguously selects one member, and that's what we'll use. - - IDictionary> result = new Dictionary>(StringComparer.OrdinalIgnoreCase); - foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) - { - if (propertyInfo.CanWrite) - { - MethodInfo setMethod = ReflectionUtils.GetSetterMethodInfo(propertyInfo); - if (setMethod.IsStatic) - continue; - if (result.ContainsKey(propertyInfo.Name)) - { - throw new InvalidOperationException($"The type '{type.FullName}' contains multiple public properties with names case-insensitively matching '{propertyInfo.Name.ToLowerInvariant()}'. Such types cannot be used for JSON deserialization."); - } - result[propertyInfo.Name] = new KeyValuePair(propertyInfo.PropertyType, ReflectionUtils.GetSetMethod(propertyInfo)); - } - } - - IDictionary> fieldResult = new Dictionary>(StringComparer.OrdinalIgnoreCase); - foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) - { - if (fieldInfo.IsInitOnly || fieldInfo.IsStatic || !fieldInfo.IsPublic) - continue; - if (fieldResult.ContainsKey(fieldInfo.Name)) - { - throw new InvalidOperationException($"The type '{type.FullName}' contains multiple public fields with names case-insensitively matching '{fieldInfo.Name.ToLowerInvariant()}'. Such types cannot be used for JSON deserialization."); - } - fieldResult[fieldInfo.Name] = new KeyValuePair(fieldInfo.FieldType, ReflectionUtils.GetSetMethod(fieldInfo)); - if (!result.ContainsKey(fieldInfo.Name)) - { - result[fieldInfo.Name] = fieldResult[fieldInfo.Name]; - } - } - - return result; - } - - public virtual bool TrySerializeNonPrimitiveObject(object input, out object output) - { - return TrySerializeKnownTypes(input, out output) || TrySerializeUnknownTypes(input, out output); - } - - [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] - public virtual object DeserializeObject(object value, Type type) - { - if (type == null) throw new ArgumentNullException("type"); - string str = value as string; - - if (type == typeof (Guid) && string.IsNullOrEmpty(str)) - return default(Guid); - - if (type.IsEnum) - { - type = type.GetEnumUnderlyingType(); - } - - if (value == null) - return null; - - object obj = null; - - if (str != null) - { - if (str.Length != 0) // We know it can't be null now. - { - if (type == typeof(TimeSpan) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(TimeSpan))) - return TimeSpan.ParseExact(str, "c", CultureInfo.InvariantCulture); - if (type == typeof(DateTime) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(DateTime))) - return DateTime.TryParseExact(str, Iso8601Format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out var result) - ? result : DateTime.Parse(str, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind); - if (type == typeof(DateTimeOffset) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(DateTimeOffset))) - return DateTimeOffset.TryParseExact(str, Iso8601Format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out var result) - ? result : DateTimeOffset.Parse(str, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind); - if (type == typeof(Guid) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid))) - return new Guid(str); - if (type == typeof(Uri)) - { - bool isValid = Uri.IsWellFormedUriString(str, UriKind.RelativeOrAbsolute); - - Uri result; - if (isValid && Uri.TryCreate(str, UriKind.RelativeOrAbsolute, out result)) - return result; - - return null; - } - - if (type == typeof(string)) - return str; - - return Convert.ChangeType(str, type, CultureInfo.InvariantCulture); - } - else - { - if (type == typeof(Guid)) - obj = default(Guid); - else if (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid)) - obj = null; - else - obj = str; - } - // Empty string case - if (!ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid)) - return str; - } - else if (value is bool) - return value; - - bool valueIsLong = value is long; - bool valueIsDouble = value is double; - if ((valueIsLong && type == typeof(long)) || (valueIsDouble && type == typeof(double))) - return value; - if ((valueIsDouble && type != typeof(double)) || (valueIsLong && type != typeof(long))) - { - obj = type == typeof(int) || type == typeof(long) || type == typeof(double) || type == typeof(float) || type == typeof(bool) || type == typeof(decimal) || type == typeof(byte) || type == typeof(short) - ? Convert.ChangeType(value, type, CultureInfo.InvariantCulture) - : value; - } - else - { - IDictionary objects = value as IDictionary; - if (objects != null) - { - IDictionary jsonObject = objects; - - if (ReflectionUtils.IsTypeDictionary(type)) - { - // if dictionary then - Type[] types = ReflectionUtils.GetGenericTypeArguments(type); - Type keyType = types[0]; - Type valueType = types[1]; - - Type genericType = typeof(Dictionary<,>).MakeGenericType(keyType, valueType); - - IDictionary dict = (IDictionary)ConstructorCache[genericType](); - - foreach (KeyValuePair kvp in jsonObject) - dict.Add(kvp.Key, DeserializeObject(kvp.Value, valueType)); - - obj = dict; - } - else - { - if (type == typeof(object)) - obj = value; - else - { - var constructorDelegate = ConstructorCache[type] - ?? throw new InvalidOperationException($"Cannot deserialize JSON into type '{type.FullName}' because it does not have a public parameterless constructor."); - obj = constructorDelegate(); - - var setterCache = SetCache[type]; - foreach (var jsonKeyValuePair in jsonObject) - { - if (setterCache.TryGetValue(jsonKeyValuePair.Key, out var setter)) - { - var jsonValue = DeserializeObject(jsonKeyValuePair.Value, setter.Key); - setter.Value(obj, jsonValue); - } - } - } - } - } - else - { - IList valueAsList = value as IList; - if (valueAsList != null) - { - IList jsonObject = valueAsList; - IList list = null; - - if (type.IsArray) - { - list = (IList)ConstructorCache[type](jsonObject.Count); - int i = 0; - foreach (object o in jsonObject) - list[i++] = DeserializeObject(o, type.GetElementType()); - } - else if (ReflectionUtils.IsTypeGenericCollectionInterface(type) || ReflectionUtils.IsAssignableFrom(typeof(IList), type)) - { - Type innerType = ReflectionUtils.GetGenericListElementType(type); - list = (IList)(ConstructorCache[type] ?? ConstructorCache[typeof(List<>).MakeGenericType(innerType)])(jsonObject.Count); - foreach (object o in jsonObject) - list.Add(DeserializeObject(o, innerType)); - } - obj = list; - } - } - return obj; - } - if (ReflectionUtils.IsNullableType(type)) - { - // For nullable enums serialized as numbers - if (Nullable.GetUnderlyingType(type).IsEnum) - { - return Enum.ToObject(Nullable.GetUnderlyingType(type), value); - } - - return ReflectionUtils.ToNullableType(obj, type); - } - - return obj; - } - - protected virtual object SerializeEnum(Enum p) - { - return Convert.ToDouble(p, CultureInfo.InvariantCulture); - } - - [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification="Need to support .NET 2")] - protected virtual bool TrySerializeKnownTypes(object input, out object output) - { - bool returnValue = true; - if (input is DateTime) - output = ((DateTime)input).ToUniversalTime().ToString(Iso8601Format[0], CultureInfo.InvariantCulture); - else if (input is DateTimeOffset) - output = ((DateTimeOffset)input).ToString("o"); - else if (input is Guid) - output = ((Guid)input).ToString("D"); - else if (input is Uri) - output = input.ToString(); - else if (input is TimeSpan) - output = ((TimeSpan)input).ToString("c"); - else - { - Enum inputEnum = input as Enum; - if (inputEnum != null) - output = SerializeEnum(inputEnum); - else - { - returnValue = false; - output = null; - } - } - return returnValue; - } - [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification="Need to support .NET 2")] - protected virtual bool TrySerializeUnknownTypes(object input, out object output) - { - if (input == null) throw new ArgumentNullException("input"); - output = null; - Type type = input.GetType(); - if (type.FullName == null) - return false; - IDictionary obj = new JsonObject(); - IDictionary getters = GetCache[type]; - foreach (KeyValuePair getter in getters) - { - if (getter.Value != null) - obj.Add(MapClrMemberNameToJsonFieldName(getter.Key), getter.Value(input)); - } - output = obj; - return true; - } - } - -#if SIMPLE_JSON_DATACONTRACT - [GeneratedCode("simple-json", "1.0.0")] -#if SIMPLE_JSON_INTERNAL - internal -#else - public -#endif - class DataContractJsonSerializerStrategy : PocoJsonSerializerStrategy - { - public DataContractJsonSerializerStrategy() - { - GetCache = new ReflectionUtils.ThreadSafeDictionary>(GetterValueFactory); - SetCache = new ReflectionUtils.ThreadSafeDictionary>>(SetterValueFactory); - } - - internal override IDictionary GetterValueFactory(Type type) - { - bool hasDataContract = ReflectionUtils.GetAttribute(type, typeof(DataContractAttribute)) != null; - if (!hasDataContract) - return base.GetterValueFactory(type); - string jsonKey; - IDictionary result = new Dictionary(); - foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) - { - if (propertyInfo.CanRead) - { - MethodInfo getMethod = ReflectionUtils.GetGetterMethodInfo(propertyInfo); - if (!getMethod.IsStatic && CanAdd(propertyInfo, out jsonKey)) - result[jsonKey] = ReflectionUtils.GetGetMethod(propertyInfo); - } - } - foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) - { - if (!fieldInfo.IsStatic && CanAdd(fieldInfo, out jsonKey)) - result[jsonKey] = ReflectionUtils.GetGetMethod(fieldInfo); - } - return result; - } - - internal override IDictionary> SetterValueFactory(Type type) - { - bool hasDataContract = ReflectionUtils.GetAttribute(type, typeof(DataContractAttribute)) != null; - if (!hasDataContract) - return base.SetterValueFactory(type); - string jsonKey; - IDictionary> result = new Dictionary>(); - foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) - { - if (propertyInfo.CanWrite) - { - MethodInfo setMethod = ReflectionUtils.GetSetterMethodInfo(propertyInfo); - if (!setMethod.IsStatic && CanAdd(propertyInfo, out jsonKey)) - result[jsonKey] = new KeyValuePair(propertyInfo.PropertyType, ReflectionUtils.GetSetMethod(propertyInfo)); - } - } - foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) - { - if (!fieldInfo.IsInitOnly && !fieldInfo.IsStatic && CanAdd(fieldInfo, out jsonKey)) - result[jsonKey] = new KeyValuePair(fieldInfo.FieldType, ReflectionUtils.GetSetMethod(fieldInfo)); - } - // todo implement sorting for DATACONTRACT. - return result; - } - - private static bool CanAdd(MemberInfo info, out string jsonKey) - { - jsonKey = null; - if (ReflectionUtils.GetAttribute(info, typeof(IgnoreDataMemberAttribute)) != null) - return false; - DataMemberAttribute dataMemberAttribute = (DataMemberAttribute)ReflectionUtils.GetAttribute(info, typeof(DataMemberAttribute)); - if (dataMemberAttribute == null) - return false; - jsonKey = string.IsNullOrEmpty(dataMemberAttribute.Name) ? info.Name : dataMemberAttribute.Name; - return true; - } - } - -#endif - - namespace Reflection - { - // This class is meant to be copied into other libraries. So we want to exclude it from Code Analysis rules - // that might be in place in the target project. - [GeneratedCode("reflection-utils", "1.0.0")] -#if SIMPLE_JSON_REFLECTION_UTILS_PUBLIC - public -#else - internal -#endif - class ReflectionUtils - { - private static readonly object[] EmptyObjects = new object[] { }; - - public delegate object GetDelegate(object source); - public delegate void SetDelegate(object source, object value); - public delegate object ConstructorDelegate(params object[] args); - - public delegate TValue ThreadSafeDictionaryValueFactory(TKey key); - -#if SIMPLE_JSON_TYPEINFO - public static TypeInfo GetTypeInfo(Type type) - { - return type.GetTypeInfo(); - } -#else - public static Type GetTypeInfo(Type type) - { - return type; - } -#endif - - public static Attribute GetAttribute(MemberInfo info, Type type) - { -#if SIMPLE_JSON_TYPEINFO - if (info == null || type == null || !info.IsDefined(type)) - return null; - return info.GetCustomAttribute(type); -#else - if (info == null || type == null || !Attribute.IsDefined(info, type)) - return null; - return Attribute.GetCustomAttribute(info, type); -#endif - } - - public static Type GetGenericListElementType(Type type) - { - IEnumerable interfaces; -#if SIMPLE_JSON_TYPEINFO - interfaces = type.GetTypeInfo().ImplementedInterfaces; -#else - interfaces = type.GetInterfaces(); -#endif - foreach (Type implementedInterface in interfaces) - { - if (IsTypeGeneric(implementedInterface) && - implementedInterface.GetGenericTypeDefinition() == typeof (IList<>)) - { - return GetGenericTypeArguments(implementedInterface)[0]; - } - } - return GetGenericTypeArguments(type)[0]; - } - - public static Attribute GetAttribute(Type objectType, Type attributeType) - { - -#if SIMPLE_JSON_TYPEINFO - if (objectType == null || attributeType == null || !objectType.GetTypeInfo().IsDefined(attributeType)) - return null; - return objectType.GetTypeInfo().GetCustomAttribute(attributeType); -#else - if (objectType == null || attributeType == null || !Attribute.IsDefined(objectType, attributeType)) - return null; - return Attribute.GetCustomAttribute(objectType, attributeType); -#endif - } - - public static Type[] GetGenericTypeArguments(Type type) - { -#if SIMPLE_JSON_TYPEINFO - return type.GetTypeInfo().GenericTypeArguments; -#else - return type.GetGenericArguments(); -#endif - } - - public static bool IsTypeGeneric(Type type) - { - return GetTypeInfo(type).IsGenericType; - } - - public static bool IsTypeGenericCollectionInterface(Type type) - { - if (!IsTypeGeneric(type)) - return false; - - Type genericDefinition = type.GetGenericTypeDefinition(); - - return (genericDefinition == typeof(IList<>) - || genericDefinition == typeof(ICollection<>) - || genericDefinition == typeof(IEnumerable<>) -#if SIMPLE_JSON_READONLY_COLLECTIONS - || genericDefinition == typeof(IReadOnlyCollection<>) - || genericDefinition == typeof(IReadOnlyList<>) -#endif - ); - } - - public static bool IsAssignableFrom(Type type1, Type type2) - { - return GetTypeInfo(type1).IsAssignableFrom(GetTypeInfo(type2)); - } - - public static bool IsTypeDictionary(Type type) - { -#if SIMPLE_JSON_TYPEINFO - if (typeof(IDictionary<,>).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) - return true; -#else - if (typeof(System.Collections.IDictionary).IsAssignableFrom(type)) - return true; -#endif - if (!GetTypeInfo(type).IsGenericType) - return false; - - Type genericDefinition = type.GetGenericTypeDefinition(); - return genericDefinition == typeof(IDictionary<,>); - } - - public static bool IsNullableType(Type type) - { - return GetTypeInfo(type).IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); - } - - public static object ToNullableType(object obj, Type nullableType) - { - return obj == null ? null : Convert.ChangeType(obj, Nullable.GetUnderlyingType(nullableType), CultureInfo.InvariantCulture); - } - - public static bool IsValueType(Type type) - { - return GetTypeInfo(type).IsValueType; - } - - public static IEnumerable GetConstructors(Type type) - { -#if SIMPLE_JSON_TYPEINFO - return type.GetTypeInfo().DeclaredConstructors; -#else - const BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; - return type.GetConstructors(flags); -#endif - } - - public static ConstructorInfo GetConstructorInfo(Type type, params Type[] argsType) - { - IEnumerable constructorInfos = GetConstructors(type); - int i; - bool matches; - foreach (ConstructorInfo constructorInfo in constructorInfos) - { - ParameterInfo[] parameters = constructorInfo.GetParameters(); - if (argsType.Length != parameters.Length) - continue; - - i = 0; - matches = true; - foreach (ParameterInfo parameterInfo in constructorInfo.GetParameters()) - { - if (parameterInfo.ParameterType != argsType[i]) - { - matches = false; - break; - } - } - - if (matches) - return constructorInfo; - } - - return null; - } - - public static IEnumerable GetProperties(Type type) - { -#if SIMPLE_JSON_TYPEINFO - return type.GetRuntimeProperties(); -#else - return type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); -#endif - } - - public static IEnumerable GetFields(Type type) - { -#if SIMPLE_JSON_TYPEINFO - return type.GetRuntimeFields(); -#else - return type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); -#endif - } - - public static MethodInfo GetGetterMethodInfo(PropertyInfo propertyInfo) - { -#if SIMPLE_JSON_TYPEINFO - return propertyInfo.GetMethod; -#else - return propertyInfo.GetGetMethod(true); -#endif - } - - public static MethodInfo GetSetterMethodInfo(PropertyInfo propertyInfo) - { -#if SIMPLE_JSON_TYPEINFO - return propertyInfo.SetMethod; -#else - return propertyInfo.GetSetMethod(true); -#endif - } - - public static ConstructorDelegate GetConstructor(ConstructorInfo constructorInfo) - { -#if SIMPLE_JSON_NO_LINQ_EXPRESSION - return GetConstructorByReflection(constructorInfo); -#else - return GetConstructorByExpression(constructorInfo); -#endif - } - - public static ConstructorDelegate GetConstructor(Type type, params Type[] argsType) - { -#if SIMPLE_JSON_NO_LINQ_EXPRESSION - return GetConstructorByReflection(type, argsType); -#else - return GetConstructorByExpression(type, argsType); -#endif - } - - public static ConstructorDelegate GetConstructorByReflection(ConstructorInfo constructorInfo) - { - return delegate(object[] args) { return constructorInfo.Invoke(args); }; - } - - public static ConstructorDelegate GetConstructorByReflection(Type type, params Type[] argsType) - { - ConstructorInfo constructorInfo = GetConstructorInfo(type, argsType); - - if (constructorInfo == null && argsType.Length == 0 && type.IsValueType) - { - // If it's a struct, then parameterless constructors are implicit - // We can always call Activator.CreateInstance in lieu of a zero-arg constructor - return args => Activator.CreateInstance(type); - } - - return constructorInfo == null ? null : GetConstructorByReflection(constructorInfo); - } - -#if !SIMPLE_JSON_NO_LINQ_EXPRESSION - - public static ConstructorDelegate GetConstructorByExpression(ConstructorInfo constructorInfo) - { - ParameterInfo[] paramsInfo = constructorInfo.GetParameters(); - ParameterExpression param = Expression.Parameter(typeof(object[]), "args"); - Expression[] argsExp = new Expression[paramsInfo.Length]; - for (int i = 0; i < paramsInfo.Length; i++) - { - Expression index = Expression.Constant(i); - Type paramType = paramsInfo[i].ParameterType; - Expression paramAccessorExp = Expression.ArrayIndex(param, index); - Expression paramCastExp = Expression.Convert(paramAccessorExp, paramType); - argsExp[i] = paramCastExp; - } - NewExpression newExp = Expression.New(constructorInfo, argsExp); - Expression> lambda = Expression.Lambda>(newExp, param); - Func compiledLambda = lambda.Compile(); - return delegate(object[] args) { return compiledLambda(args); }; - } - - public static ConstructorDelegate GetConstructorByExpression(Type type, params Type[] argsType) - { - ConstructorInfo constructorInfo = GetConstructorInfo(type, argsType); - return constructorInfo == null ? null : GetConstructorByExpression(constructorInfo); - } - -#endif - - public static GetDelegate GetGetMethod(PropertyInfo propertyInfo) - { -#if SIMPLE_JSON_NO_LINQ_EXPRESSION - return GetGetMethodByReflection(propertyInfo); -#else - return GetGetMethodByExpression(propertyInfo); -#endif - } - - public static GetDelegate GetGetMethod(FieldInfo fieldInfo) - { -#if SIMPLE_JSON_NO_LINQ_EXPRESSION - return GetGetMethodByReflection(fieldInfo); -#else - return GetGetMethodByExpression(fieldInfo); -#endif - } - - public static GetDelegate GetGetMethodByReflection(PropertyInfo propertyInfo) - { - MethodInfo methodInfo = GetGetterMethodInfo(propertyInfo); - return delegate(object source) { return methodInfo.Invoke(source, EmptyObjects); }; - } - - public static GetDelegate GetGetMethodByReflection(FieldInfo fieldInfo) - { - return delegate(object source) { return fieldInfo.GetValue(source); }; - } - -#if !SIMPLE_JSON_NO_LINQ_EXPRESSION - - public static GetDelegate GetGetMethodByExpression(PropertyInfo propertyInfo) - { - MethodInfo getMethodInfo = GetGetterMethodInfo(propertyInfo); - ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); - UnaryExpression instanceCast = (!IsValueType(propertyInfo.DeclaringType)) ? Expression.TypeAs(instance, propertyInfo.DeclaringType) : Expression.Convert(instance, propertyInfo.DeclaringType); - Func compiled = Expression.Lambda>(Expression.TypeAs(Expression.Call(instanceCast, getMethodInfo), typeof(object)), instance).Compile(); - return delegate(object source) { return compiled(source); }; - } - - public static GetDelegate GetGetMethodByExpression(FieldInfo fieldInfo) - { - ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); - MemberExpression member = Expression.Field(Expression.Convert(instance, fieldInfo.DeclaringType), fieldInfo); - GetDelegate compiled = Expression.Lambda(Expression.Convert(member, typeof(object)), instance).Compile(); - return delegate(object source) { return compiled(source); }; - } - -#endif - - public static SetDelegate GetSetMethod(PropertyInfo propertyInfo) - { -#if SIMPLE_JSON_NO_LINQ_EXPRESSION - return GetSetMethodByReflection(propertyInfo); -#else - return GetSetMethodByExpression(propertyInfo); -#endif - } - - public static SetDelegate GetSetMethod(FieldInfo fieldInfo) - { -#if SIMPLE_JSON_NO_LINQ_EXPRESSION - return GetSetMethodByReflection(fieldInfo); -#else - return GetSetMethodByExpression(fieldInfo); -#endif - } - - public static SetDelegate GetSetMethodByReflection(PropertyInfo propertyInfo) - { - MethodInfo methodInfo = GetSetterMethodInfo(propertyInfo); - return delegate(object source, object value) { methodInfo.Invoke(source, new object[] { value }); }; - } - - public static SetDelegate GetSetMethodByReflection(FieldInfo fieldInfo) - { - return delegate(object source, object value) { fieldInfo.SetValue(source, value); }; - } - -#if !SIMPLE_JSON_NO_LINQ_EXPRESSION - - public static SetDelegate GetSetMethodByExpression(PropertyInfo propertyInfo) - { - MethodInfo setMethodInfo = GetSetterMethodInfo(propertyInfo); - ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); - ParameterExpression value = Expression.Parameter(typeof(object), "value"); - UnaryExpression instanceCast = (!IsValueType(propertyInfo.DeclaringType)) ? Expression.TypeAs(instance, propertyInfo.DeclaringType) : Expression.Convert(instance, propertyInfo.DeclaringType); - UnaryExpression valueCast = (!IsValueType(propertyInfo.PropertyType)) ? Expression.TypeAs(value, propertyInfo.PropertyType) : Expression.Convert(value, propertyInfo.PropertyType); - Action compiled = Expression.Lambda>(Expression.Call(instanceCast, setMethodInfo, valueCast), new ParameterExpression[] { instance, value }).Compile(); - return delegate(object source, object val) { compiled(source, val); }; - } - - public static SetDelegate GetSetMethodByExpression(FieldInfo fieldInfo) - { - ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); - ParameterExpression value = Expression.Parameter(typeof(object), "value"); - Action compiled = Expression.Lambda>( - Assign(Expression.Field(Expression.Convert(instance, fieldInfo.DeclaringType), fieldInfo), Expression.Convert(value, fieldInfo.FieldType)), instance, value).Compile(); - return delegate(object source, object val) { compiled(source, val); }; - } - - public static BinaryExpression Assign(Expression left, Expression right) - { -#if SIMPLE_JSON_TYPEINFO - return Expression.Assign(left, right); -#else - MethodInfo assign = typeof(Assigner<>).MakeGenericType(left.Type).GetMethod("Assign"); - BinaryExpression assignExpr = Expression.Add(left, right, assign); - return assignExpr; -#endif - } - - private static class Assigner - { - public static T Assign(ref T left, T right) - { - return (left = right); - } - } - -#endif - - public sealed class ThreadSafeDictionary : IDictionary - { - private readonly object _lock = new object(); - private readonly ThreadSafeDictionaryValueFactory _valueFactory; - private Dictionary _dictionary; - - public ThreadSafeDictionary(ThreadSafeDictionaryValueFactory valueFactory) - { - _valueFactory = valueFactory; - } - - private TValue Get(TKey key) - { - if (_dictionary == null) - return AddValue(key); - TValue value; - if (!_dictionary.TryGetValue(key, out value)) - return AddValue(key); - return value; - } - - private TValue AddValue(TKey key) - { - TValue value = _valueFactory(key); - lock (_lock) - { - if (_dictionary == null) - { - _dictionary = new Dictionary(); - _dictionary[key] = value; - } - else - { - TValue val; - if (_dictionary.TryGetValue(key, out val)) - return val; - Dictionary dict = new Dictionary(_dictionary); - dict[key] = value; - _dictionary = dict; - } - } - return value; - } - - public void Add(TKey key, TValue value) - { - throw new NotImplementedException(); - } - - public bool ContainsKey(TKey key) - { - return _dictionary.ContainsKey(key); - } - - public ICollection Keys - { - get { return _dictionary.Keys; } - } - - public bool Remove(TKey key) - { - throw new NotImplementedException(); - } - - public bool TryGetValue(TKey key, out TValue value) - { - value = this[key]; - return true; - } - - public ICollection Values - { - get { return _dictionary.Values; } - } - - public TValue this[TKey key] - { - get { return Get(key); } - set { throw new NotImplementedException(); } - } - - public void Add(KeyValuePair item) - { - throw new NotImplementedException(); - } - - public void Clear() - { - throw new NotImplementedException(); - } - - public bool Contains(KeyValuePair item) - { - throw new NotImplementedException(); - } - - public void CopyTo(KeyValuePair[] array, int arrayIndex) - { - throw new NotImplementedException(); - } - - public int Count - { - get { return _dictionary.Count; } - } - - public bool IsReadOnly - { - get { throw new NotImplementedException(); } - } - - public bool Remove(KeyValuePair item) - { - throw new NotImplementedException(); - } - - public IEnumerator> GetEnumerator() - { - return _dictionary.GetEnumerator(); - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return _dictionary.GetEnumerator(); - } - } - - } - } -} -// ReSharper restore LoopCanBeConvertedToQuery -// ReSharper restore RedundantExplicitArrayCreation -// ReSharper restore SuggestUseVarKeywordEvident diff --git a/src/JSInterop/Microsoft.JSInterop/src/JsonSerializerOptionsProvider.cs b/src/JSInterop/Microsoft.JSInterop/src/JsonSerializerOptionsProvider.cs new file mode 100644 index 000000000000..0292039eaf7c --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop/src/JsonSerializerOptionsProvider.cs @@ -0,0 +1,15 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Text.Json.Serialization; + +namespace Microsoft.JSInterop +{ + internal static class JsonSerializerOptionsProvider + { + public static readonly JsonSerializerOptions Options = new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + }; + } +} diff --git a/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj b/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj index f92b8d457d5f..bc912b97cc55 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj +++ b/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj @@ -1,4 +1,4 @@ - + netstandard2.0 @@ -8,6 +8,10 @@ true + + + + diff --git a/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs b/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs index e3f91a6fd0d2..c65b4e4680d3 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs @@ -3,6 +3,8 @@ using System; using System.Linq; +using System.Text.Json; +using System.Text.Json.Serialization; using System.Threading.Tasks; using Xunit; @@ -10,10 +12,7 @@ namespace Microsoft.JSInterop.Tests { public class DotNetDispatcherTest { - private readonly static string thisAssemblyName - = typeof(DotNetDispatcherTest).Assembly.GetName().Name; - private readonly TestJSRuntime jsRuntime - = new TestJSRuntime(); + private readonly static string thisAssemblyName = typeof(DotNetDispatcherTest).Assembly.GetName().Name; [Fact] public void CannotInvokeWithEmptyAssemblyName() @@ -24,7 +23,7 @@ public void CannotInvokeWithEmptyAssemblyName() }); Assert.StartsWith("Cannot be null, empty, or whitespace.", ex.Message); - Assert.Equal("assemblyName", ex.ParamName); + Assert.Equal("AssemblyName", ex.ParamName); } [Fact] @@ -73,7 +72,7 @@ public void CannotInvokeUnsuitableMethods(string methodIdentifier) Assert.Equal($"The assembly '{thisAssemblyName}' does not contain a public method with [JSInvokableAttribute(\"{methodIdentifier}\")].", ex.Message); } - [Fact(Skip = "https://github.com/aspnet/AspNetCore-Internal/issues/1733")] + [Fact] public Task CanInvokeStaticVoidMethod() => WithJSRuntime(jsRuntime => { // Arrange/Act @@ -90,7 +89,7 @@ public Task CanInvokeStaticNonVoidMethod() => WithJSRuntime(jsRuntime => { // Arrange/Act var resultJson = DotNetDispatcher.Invoke(thisAssemblyName, "InvocableStaticNonVoid", default, null); - var result = Json.Deserialize(resultJson); + var result = JsonSerializer.Parse(resultJson, JsonSerializerOptionsProvider.Options); // Assert Assert.Equal("Test", result.StringVal); @@ -102,50 +101,81 @@ public Task CanInvokeStaticNonVoidMethodWithoutCustomIdentifier() => WithJSRunti { // Arrange/Act var resultJson = DotNetDispatcher.Invoke(thisAssemblyName, nameof(SomePublicType.InvokableMethodWithoutCustomIdentifier), default, null); - var result = Json.Deserialize(resultJson); + var result = JsonSerializer.Parse(resultJson, JsonSerializerOptionsProvider.Options); // Assert Assert.Equal("InvokableMethodWithoutCustomIdentifier", result.StringVal); Assert.Equal(456, result.IntVal); }); - [Fact(Skip = "https://github.com/aspnet/AspNetCore-Internal/issues/1733")] + [Fact] public Task CanInvokeStaticWithParams() => WithJSRuntime(jsRuntime => { // Arrange: Track a .NET object to use as an arg var arg3 = new TestDTO { IntVal = 999, StringVal = "My string" }; - jsRuntime.Invoke("unimportant", new DotNetObjectRef(arg3)); + var objectRef = DotNetObjectRef.Create(arg3); + jsRuntime.Invoke("unimportant", objectRef); // Arrange: Remaining args - var argsJson = Json.Serialize(new object[] { + var argsJson = JsonSerializer.ToString(new object[] + { new TestDTO { StringVal = "Another string", IntVal = 456 }, new[] { 100, 200 }, - "__dotNetObject:1" - }); + objectRef + }, JsonSerializerOptionsProvider.Options); // Act var resultJson = DotNetDispatcher.Invoke(thisAssemblyName, "InvocableStaticWithParams", default, argsJson); - var result = Json.Deserialize(resultJson); + var result = JsonDocument.Parse(resultJson); + var root = result.RootElement; // Assert: First result value marshalled via JSON - var resultDto1 = (TestDTO)jsRuntime.ArgSerializerStrategy.DeserializeObject(result[0], typeof(TestDTO)); + var resultDto1 = JsonSerializer.Parse(root[0].GetRawText(), JsonSerializerOptionsProvider.Options); + Assert.Equal("ANOTHER STRING", resultDto1.StringVal); Assert.Equal(756, resultDto1.IntVal); // Assert: Second result value marshalled by ref - var resultDto2Ref = (string)result[1]; - Assert.Equal("__dotNetObject:2", resultDto2Ref); - var resultDto2 = (TestDTO)jsRuntime.ArgSerializerStrategy.FindDotNetObject(2); + var resultDto2Ref = root[1]; + Assert.False(resultDto2Ref.TryGetProperty(nameof(TestDTO.StringVal), out _)); + Assert.False(resultDto2Ref.TryGetProperty(nameof(TestDTO.IntVal), out _)); + + Assert.True(resultDto2Ref.TryGetProperty(DotNetDispatcher.DotNetObjectRefKey, out var property)); + var resultDto2 = Assert.IsType(DotNetObjectRefManager.Current.FindDotNetObject(property.GetInt64())); Assert.Equal("MY STRING", resultDto2.StringVal); Assert.Equal(1299, resultDto2.IntVal); }); - [Fact(Skip = "https://github.com/aspnet/AspNetCore-Internal/issues/1733")] + [Fact] + public Task InvokingWithIncorrectUseOfDotNetObjectRefThrows() => WithJSRuntime(jsRuntime => + { + // Arrange + var method = nameof(SomePublicType.IncorrectDotNetObjectRefUsage); + var arg3 = new TestDTO { IntVal = 999, StringVal = "My string" }; + var objectRef = DotNetObjectRef.Create(arg3); + jsRuntime.Invoke("unimportant", objectRef); + + // Arrange: Remaining args + var argsJson = JsonSerializer.ToString(new object[] + { + new TestDTO { StringVal = "Another string", IntVal = 456 }, + new[] { 100, 200 }, + objectRef + }, JsonSerializerOptionsProvider.Options); + + // Act & Assert + var ex = Assert.Throws(() => + DotNetDispatcher.Invoke(thisAssemblyName, method, default, argsJson)); + Assert.Equal($"In call to '{method}', parameter of type '{nameof(TestDTO)}' at index 3 must be declared as type 'DotNetObjectRef' to receive the incoming value.", ex.Message); + }); + + [Fact] public Task CanInvokeInstanceVoidMethod() => WithJSRuntime(jsRuntime => { // Arrange: Track some instance var targetInstance = new SomePublicType(); - jsRuntime.Invoke("unimportant", new DotNetObjectRef(targetInstance)); + var objectRef = DotNetObjectRef.Create(targetInstance); + jsRuntime.Invoke("unimportant", objectRef); // Act var resultJson = DotNetDispatcher.Invoke(null, "InvokableInstanceVoid", 1, null); @@ -155,12 +185,13 @@ public Task CanInvokeInstanceVoidMethod() => WithJSRuntime(jsRuntime => Assert.True(targetInstance.DidInvokeMyInvocableInstanceVoid); }); - [Fact(Skip = "https://github.com/aspnet/AspNetCore-Internal/issues/1733")] + [Fact] public Task CanInvokeBaseInstanceVoidMethod() => WithJSRuntime(jsRuntime => { // Arrange: Track some instance var targetInstance = new DerivedClass(); - jsRuntime.Invoke("unimportant", new DotNetObjectRef(targetInstance)); + var objectRef = DotNetObjectRef.Create(targetInstance); + jsRuntime.Invoke("unimportant", objectRef); // Act var resultJson = DotNetDispatcher.Invoke(null, "BaseClassInvokableInstanceVoid", 1, null); @@ -178,7 +209,7 @@ public Task CannotUseDotNetObjectRefAfterDisposal() => WithJSRuntime(jsRuntime = // Arrange: Track some instance, then dispose it var targetInstance = new SomePublicType(); - var objectRef = new DotNetObjectRef(targetInstance); + var objectRef = DotNetObjectRef.Create(targetInstance); jsRuntime.Invoke("unimportant", objectRef); objectRef.Dispose(); @@ -196,7 +227,7 @@ public Task CannotUseDotNetObjectRefAfterReleaseDotNetObject() => WithJSRuntime( // Arrange: Track some instance, then dispose it var targetInstance = new SomePublicType(); - var objectRef = new DotNetObjectRef(targetInstance); + var objectRef = DotNetObjectRef.Create(targetInstance); jsRuntime.Invoke("unimportant", objectRef); DotNetDispatcher.ReleaseDotNetObject(1); @@ -206,23 +237,23 @@ public Task CannotUseDotNetObjectRefAfterReleaseDotNetObject() => WithJSRuntime( Assert.StartsWith("There is no tracked object with id '1'.", ex.Message); }); - [Fact(Skip = "https://github.com/aspnet/AspNetCore-Internal/issues/1733")] + [Fact] public Task CanInvokeInstanceMethodWithParams() => WithJSRuntime(jsRuntime => { // Arrange: Track some instance plus another object we'll pass as a param var targetInstance = new SomePublicType(); var arg2 = new TestDTO { IntVal = 1234, StringVal = "My string" }; jsRuntime.Invoke("unimportant", - new DotNetObjectRef(targetInstance), - new DotNetObjectRef(arg2)); - var argsJson = "[\"myvalue\",\"__dotNetObject:2\"]"; + DotNetObjectRef.Create(targetInstance), + DotNetObjectRef.Create(arg2)); + var argsJson = "[\"myvalue\",{\"__dotNetObject\":2}]"; // Act var resultJson = DotNetDispatcher.Invoke(null, "InvokableInstanceMethod", 1, argsJson); // Assert - Assert.Equal("[\"You passed myvalue\",\"__dotNetObject:3\"]", resultJson); - var resultDto = (TestDTO)jsRuntime.ArgSerializerStrategy.FindDotNetObject(3); + Assert.Equal("[\"You passed myvalue\",{\"__dotNetObject\":3}]", resultJson); + var resultDto = (TestDTO)jsRuntime.ObjectRefManager.FindDotNetObject(3); Assert.Equal(1235, resultDto.IntVal); Assert.Equal("MY STRING", resultDto.StringVal); }); @@ -231,7 +262,7 @@ public Task CanInvokeInstanceMethodWithParams() => WithJSRuntime(jsRuntime => public void CannotInvokeWithIncorrectNumberOfParams() { // Arrange - var argsJson = Json.Serialize(new object[] { 1, 2, 3, 4 }); + var argsJson = JsonSerializer.ToString(new object[] { 1, 2, 3, 4 }, JsonSerializerOptionsProvider.Options); // Act/Assert var ex = Assert.Throws(() => @@ -242,50 +273,50 @@ public void CannotInvokeWithIncorrectNumberOfParams() Assert.Equal("In call to 'InvocableStaticWithParams', expected 3 parameters but received 4.", ex.Message); } - [Fact(Skip = "https://github.com/aspnet/AspNetCore-Internal/issues/1733")] + [Fact] public Task CanInvokeAsyncMethod() => WithJSRuntime(async jsRuntime => { // Arrange: Track some instance plus another object we'll pass as a param var targetInstance = new SomePublicType(); var arg2 = new TestDTO { IntVal = 1234, StringVal = "My string" }; - jsRuntime.Invoke("unimportant", new DotNetObjectRef(targetInstance), new DotNetObjectRef(arg2)); + var arg1Ref = DotNetObjectRef.Create(targetInstance); + var arg2Ref = DotNetObjectRef.Create(arg2); + jsRuntime.Invoke("unimportant", arg1Ref, arg2Ref); // Arrange: all args - var argsJson = Json.Serialize(new object[] + var argsJson = JsonSerializer.ToString(new object[] { new TestDTO { IntVal = 1000, StringVal = "String via JSON" }, - "__dotNetObject:2" - }); + arg2Ref, + }, JsonSerializerOptionsProvider.Options); // Act var callId = "123"; var resultTask = jsRuntime.NextInvocationTask; DotNetDispatcher.BeginInvoke(callId, null, "InvokableAsyncMethod", 1, argsJson); await resultTask; - var result = Json.Deserialize(jsRuntime.LastInvocationArgsJson); - var resultValue = (SimpleJson.JsonArray)result[2]; + var result = JsonDocument.Parse(jsRuntime.LastInvocationArgsJson).RootElement; + var resultValue = result[2]; // Assert: Correct info to complete the async call Assert.Equal(0, jsRuntime.LastInvocationAsyncHandle); // 0 because it doesn't want a further callback from JS to .NET Assert.Equal("DotNet.jsCallDispatcher.endInvokeDotNetFromJS", jsRuntime.LastInvocationIdentifier); - Assert.Equal(3, result.Count); - Assert.Equal(callId, result[0]); - Assert.True((bool)result[1]); // Success flag + Assert.Equal(3, result.GetArrayLength()); + Assert.Equal(callId, result[0].GetString()); + Assert.True(result[1].GetBoolean()); // Success flag // Assert: First result value marshalled via JSON - var resultDto1 = (TestDTO)jsRuntime.ArgSerializerStrategy.DeserializeObject(resultValue[0], typeof(TestDTO)); + var resultDto1 = JsonSerializer.Parse(resultValue[0].GetRawText(), JsonSerializerOptionsProvider.Options); Assert.Equal("STRING VIA JSON", resultDto1.StringVal); Assert.Equal(2000, resultDto1.IntVal); // Assert: Second result value marshalled by ref - var resultDto2Ref = (string)resultValue[1]; - Assert.Equal("__dotNetObject:3", resultDto2Ref); - var resultDto2 = (TestDTO)jsRuntime.ArgSerializerStrategy.FindDotNetObject(3); + var resultDto2Ref = JsonSerializer.Parse>(resultValue[1].GetRawText(), JsonSerializerOptionsProvider.Options); + var resultDto2 = resultDto2Ref.Value; Assert.Equal("MY STRING", resultDto2.StringVal); Assert.Equal(2468, resultDto2.IntVal); }); - [Fact] public Task CanInvokeSyncThrowingMethod() => WithJSRuntime(async jsRuntime => { @@ -299,13 +330,13 @@ public Task CanInvokeSyncThrowingMethod() => WithJSRuntime(async jsRuntime => await resultTask; // This won't throw, it sets properties on the jsRuntime. // Assert - var result = Json.Deserialize(jsRuntime.LastInvocationArgsJson); - Assert.Equal(callId, result[0]); - Assert.False((bool)result[1]); // Fails + var result = JsonDocument.Parse(jsRuntime.LastInvocationArgsJson).RootElement; + Assert.Equal(callId, result[0].GetString()); + Assert.False(result[1].GetBoolean()); // Fails // Make sure the method that threw the exception shows up in the call stack // https://github.com/aspnet/AspNetCore/issues/8612 - var exception = (string)result[2]; + var exception = result[2].GetString(); Assert.Contains(nameof(ThrowingClass.ThrowingMethod), exception); }); @@ -322,17 +353,16 @@ public Task CanInvokeAsyncThrowingMethod() => WithJSRuntime(async jsRuntime => await resultTask; // This won't throw, it sets properties on the jsRuntime. // Assert - var result = Json.Deserialize(jsRuntime.LastInvocationArgsJson); - Assert.Equal(callId, result[0]); - Assert.False((bool)result[1]); // Fails + var result = JsonDocument.Parse(jsRuntime.LastInvocationArgsJson).RootElement; + Assert.Equal(callId, result[0].GetString()); + Assert.False(result[1].GetBoolean()); // Fails // Make sure the method that threw the exception shows up in the call stack // https://github.com/aspnet/AspNetCore/issues/8612 - var exception = (string)result[2]; + var exception = result[2].GetString(); Assert.Contains(nameof(ThrowingClass.AsyncThrowingMethod), exception); }); - Task WithJSRuntime(Action testCode) { return WithJSRuntime(jsRuntime => @@ -379,7 +409,7 @@ public static object MyInvocableNonVoid() => new TestDTO { StringVal = "Test", IntVal = 123 }; [JSInvokable("InvocableStaticWithParams")] - public static object[] MyInvocableWithParams(TestDTO dtoViaJson, int[] incrementAmounts, TestDTO dtoByRef) + public static object[] MyInvocableWithParams(TestDTO dtoViaJson, int[] incrementAmounts, DotNetObjectRef dtoByRef) => new object[] { new TestDTO // Return via JSON marshalling @@ -387,13 +417,17 @@ public static object[] MyInvocableWithParams(TestDTO dtoViaJson, int[] increment StringVal = dtoViaJson.StringVal.ToUpperInvariant(), IntVal = dtoViaJson.IntVal + incrementAmounts.Sum() }, - new DotNetObjectRef(new TestDTO // Return by ref + DotNetObjectRef.Create(new TestDTO // Return by ref { - StringVal = dtoByRef.StringVal.ToUpperInvariant(), - IntVal = dtoByRef.IntVal + incrementAmounts.Sum() + StringVal = dtoByRef.Value.StringVal.ToUpperInvariant(), + IntVal = dtoByRef.Value.IntVal + incrementAmounts.Sum() }) }; + [JSInvokable(nameof(IncorrectDotNetObjectRefUsage))] + public static object[] IncorrectDotNetObjectRefUsage(TestDTO dtoViaJson, int[] incrementAmounts, TestDTO dtoByRef) + => throw new InvalidOperationException("Shouldn't be called"); + [JSInvokable] public static TestDTO InvokableMethodWithoutCustomIdentifier() => new TestDTO { StringVal = "InvokableMethodWithoutCustomIdentifier", IntVal = 456 }; @@ -405,14 +439,15 @@ public void InvokableInstanceVoid() } [JSInvokable] - public object[] InvokableInstanceMethod(string someString, TestDTO someDTO) + public object[] InvokableInstanceMethod(string someString, DotNetObjectRef someDTORef) { + var someDTO = someDTORef.Value; // Returning an array to make the point that object references // can be embedded anywhere in the result return new object[] { $"You passed {someString}", - new DotNetObjectRef(new TestDTO + DotNetObjectRef.Create(new TestDTO { IntVal = someDTO.IntVal + 1, StringVal = someDTO.StringVal.ToUpperInvariant() @@ -421,9 +456,10 @@ public object[] InvokableInstanceMethod(string someString, TestDTO someDTO) } [JSInvokable] - public async Task InvokableAsyncMethod(TestDTO dtoViaJson, TestDTO dtoByRef) + public async Task InvokableAsyncMethod(TestDTO dtoViaJson, DotNetObjectRef dtoByRefWrapper) { await Task.Delay(50); + var dtoByRef = dtoByRefWrapper.Value; return new object[] { new TestDTO // Return via JSON @@ -431,7 +467,7 @@ public async Task InvokableAsyncMethod(TestDTO dtoViaJson, TestDTO dto StringVal = dtoViaJson.StringVal.ToUpperInvariant(), IntVal = dtoViaJson.IntVal * 2, }, - new DotNetObjectRef(new TestDTO // Return by ref + DotNetObjectRef.Create(new TestDTO // Return by ref { StringVal = dtoByRef.StringVal.ToUpperInvariant(), IntVal = dtoByRef.IntVal * 2, diff --git a/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectRefTest.cs b/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectRefTest.cs index 1bdec6d46521..2b46831a16fb 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectRefTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectRefTest.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Collections.Generic; using System.Threading.Tasks; using Xunit; @@ -11,58 +10,44 @@ namespace Microsoft.JSInterop.Tests public class DotNetObjectRefTest { [Fact] - public void CanAccessValue() + public Task CanAccessValue() => WithJSRuntime(_ => { var obj = new object(); - Assert.Same(obj, new DotNetObjectRef(obj).Value); - } + Assert.Same(obj, DotNetObjectRef.Create(obj).Value); + }); [Fact] - public void CanAssociateWithSameRuntimeMultipleTimes() - { - var objRef = new DotNetObjectRef(new object()); - var jsRuntime = new TestJsRuntime(); - objRef.EnsureAttachedToJsRuntime(jsRuntime); - objRef.EnsureAttachedToJsRuntime(jsRuntime); - } - - [Fact] - public void CannotAssociateWithDifferentRuntimes() - { - var objRef = new DotNetObjectRef(new object()); - var jsRuntime1 = new TestJsRuntime(); - var jsRuntime2 = new TestJsRuntime(); - objRef.EnsureAttachedToJsRuntime(jsRuntime1); - - var ex = Assert.Throws( - () => objRef.EnsureAttachedToJsRuntime(jsRuntime2)); - Assert.Contains("Do not attempt to re-use", ex.Message); - } - - [Fact] - public void NotifiesAssociatedJsRuntimeOfDisposal() + public Task NotifiesAssociatedJsRuntimeOfDisposal() => WithJSRuntime(jsRuntime => { // Arrange - var objRef = new DotNetObjectRef(new object()); - var jsRuntime = new TestJsRuntime(); - objRef.EnsureAttachedToJsRuntime(jsRuntime); + var objRef = DotNetObjectRef.Create(new object()); + var trackingId = objRef.__dotNetObject; // Act objRef.Dispose(); // Assert - Assert.Equal(new[] { objRef }, jsRuntime.UntrackedRefs); - } + var ex = Assert.Throws(() => jsRuntime.ObjectRefManager.FindDotNetObject(trackingId)); + Assert.StartsWith("There is no tracked object with id '1'.", ex.Message); + }); - class TestJsRuntime : IJSRuntime + class TestJSRuntime : JSRuntimeBase { - public List UntrackedRefs = new List(); + protected override void BeginInvokeJS(long asyncHandle, string identifier, string argsJson) + { + throw new NotImplementedException(); + } + } - public Task InvokeAsync(string identifier, params object[] args) - => throw new NotImplementedException(); + async Task WithJSRuntime(Action testCode) + { + // Since the tests rely on the asynclocal JSRuntime.Current, ensure we + // are on a distinct async context with a non-null JSRuntime.Current + await Task.Yield(); - public void UntrackObjectRef(DotNetObjectRef dotNetObjectRef) - => UntrackedRefs.Add(dotNetObjectRef); + var runtime = new TestJSRuntime(); + JSRuntime.SetCurrentJSRuntime(runtime); + testCode(runtime); } } } diff --git a/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeBaseTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeBaseTest.cs index a13d53677a32..dda3f74184d4 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeBaseTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeBaseTest.cs @@ -10,15 +10,15 @@ namespace Microsoft.JSInterop.Tests { public class JSInProcessRuntimeBaseTest { - [Fact(Skip = "https://github.com/aspnet/AspNetCore-Internal/issues/1807#issuecomment-470756811")] + [Fact] public void DispatchesSyncCallsAndDeserializesResults() { // Arrange var runtime = new TestJSInProcessRuntime { - NextResultJson = Json.Serialize( - new TestDTO { IntValue = 123, StringValue = "Hello" }) + NextResultJson = "{\"intValue\":123,\"stringValue\":\"Hello\"}" }; + JSRuntime.SetCurrentJSRuntime(runtime); // Act var syncResult = runtime.Invoke("test identifier 1", "arg1", 123, true); @@ -36,18 +36,19 @@ public void SerializesDotNetObjectWrappersInKnownFormat() { // Arrange var runtime = new TestJSInProcessRuntime { NextResultJson = null }; + JSRuntime.SetCurrentJSRuntime(runtime); var obj1 = new object(); var obj2 = new object(); var obj3 = new object(); // Act // Showing we can pass the DotNetObject either as top-level args or nested - var syncResult = runtime.Invoke("test identifier", - new DotNetObjectRef(obj1), + var syncResult = runtime.Invoke>("test identifier", + DotNetObjectRef.Create(obj1), new Dictionary { - { "obj2", new DotNetObjectRef(obj2) }, - { "obj3", new DotNetObjectRef(obj3) } + { "obj2", DotNetObjectRef.Create(obj2) }, + { "obj3", DotNetObjectRef.Create(obj3) }, }); // Assert: Handles null result string @@ -56,12 +57,12 @@ public void SerializesDotNetObjectWrappersInKnownFormat() // Assert: Serialized as expected var call = runtime.InvokeCalls.Single(); Assert.Equal("test identifier", call.Identifier); - Assert.Equal("[\"__dotNetObject:1\",{\"obj2\":\"__dotNetObject:2\",\"obj3\":\"__dotNetObject:3\"}]", call.ArgsJson); + Assert.Equal("[{\"__dotNetObject\":1},{\"obj2\":{\"__dotNetObject\":2},\"obj3\":{\"__dotNetObject\":3}}]", call.ArgsJson); // Assert: Objects were tracked - Assert.Same(obj1, runtime.ArgSerializerStrategy.FindDotNetObject(1)); - Assert.Same(obj2, runtime.ArgSerializerStrategy.FindDotNetObject(2)); - Assert.Same(obj3, runtime.ArgSerializerStrategy.FindDotNetObject(3)); + Assert.Same(obj1, runtime.ObjectRefManager.FindDotNetObject(1)); + Assert.Same(obj2, runtime.ObjectRefManager.FindDotNetObject(2)); + Assert.Same(obj3, runtime.ObjectRefManager.FindDotNetObject(3)); } [Fact] @@ -70,20 +71,22 @@ public void SyncCallResultCanIncludeDotNetObjects() // Arrange var runtime = new TestJSInProcessRuntime { - NextResultJson = "[\"__dotNetObject:2\",\"__dotNetObject:1\"]" + NextResultJson = "[{\"__dotNetObject\":2},{\"__dotNetObject\":1}]" }; + JSRuntime.SetCurrentJSRuntime(runtime); var obj1 = new object(); var obj2 = new object(); // Act - var syncResult = runtime.Invoke("test identifier", - new DotNetObjectRef(obj1), + var syncResult = runtime.Invoke[]>( + "test identifier", + DotNetObjectRef.Create(obj1), "some other arg", - new DotNetObjectRef(obj2)); + DotNetObjectRef.Create(obj2)); var call = runtime.InvokeCalls.Single(); // Assert - Assert.Equal(new[] { obj2, obj1 }, syncResult); + Assert.Equal(new[] { obj2, obj1 }, syncResult.Select(r => r.Value)); } class TestDTO diff --git a/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs index ab048e812fe7..82126836d8bb 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs @@ -1,10 +1,11 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using Microsoft.JSInterop.Internal; using System; using System.Collections.Generic; using System.Linq; +using System.Text.Json; +using Microsoft.JSInterop.Internal; using Xunit; namespace Microsoft.JSInterop.Tests @@ -47,12 +48,13 @@ public void CanCompleteAsyncCallsAsSuccess() var task = runtime.InvokeAsync("test identifier", Array.Empty()); Assert.False(unrelatedTask.IsCompleted); Assert.False(task.IsCompleted); + using var jsonDocument = JsonDocument.Parse("\"my result\""); // Act/Assert: Task can be completed runtime.OnEndInvoke( runtime.BeginInvokeCalls[1].AsyncHandle, /* succeeded: */ true, - "my result"); + new JSAsyncCallResult(jsonDocument, jsonDocument.RootElement)); Assert.False(unrelatedTask.IsCompleted); Assert.True(task.IsCompleted); Assert.Equal("my result", task.Result); @@ -69,12 +71,13 @@ public void CanCompleteAsyncCallsAsFailure() var task = runtime.InvokeAsync("test identifier", Array.Empty()); Assert.False(unrelatedTask.IsCompleted); Assert.False(task.IsCompleted); + using var jsonDocument = JsonDocument.Parse("\"This is a test exception\""); // Act/Assert: Task can be failed runtime.OnEndInvoke( runtime.BeginInvokeCalls[1].AsyncHandle, /* succeeded: */ false, - "This is a test exception"); + new JSAsyncCallResult(jsonDocument, jsonDocument.RootElement)); Assert.False(unrelatedTask.IsCompleted); Assert.True(task.IsCompleted); @@ -106,20 +109,21 @@ public void SerializesDotNetObjectWrappersInKnownFormat() { // Arrange var runtime = new TestJSRuntime(); + JSRuntime.SetCurrentJSRuntime(runtime); var obj1 = new object(); var obj2 = new object(); var obj3 = new object(); // Act // Showing we can pass the DotNetObject either as top-level args or nested - var obj1Ref = new DotNetObjectRef(obj1); - var obj1DifferentRef = new DotNetObjectRef(obj1); + var obj1Ref = DotNetObjectRef.Create(obj1); + var obj1DifferentRef = DotNetObjectRef.Create(obj1); runtime.InvokeAsync("test identifier", obj1Ref, new Dictionary { - { "obj2", new DotNetObjectRef(obj2) }, - { "obj3", new DotNetObjectRef(obj3) }, + { "obj2", DotNetObjectRef.Create(obj2) }, + { "obj3", DotNetObjectRef.Create(obj3) }, { "obj1SameRef", obj1Ref }, { "obj1DifferentRef", obj1DifferentRef }, }); @@ -127,28 +131,13 @@ public void SerializesDotNetObjectWrappersInKnownFormat() // Assert: Serialized as expected var call = runtime.BeginInvokeCalls.Single(); Assert.Equal("test identifier", call.Identifier); - Assert.Equal("[\"__dotNetObject:1\",{\"obj2\":\"__dotNetObject:2\",\"obj3\":\"__dotNetObject:3\",\"obj1SameRef\":\"__dotNetObject:1\",\"obj1DifferentRef\":\"__dotNetObject:4\"}]", call.ArgsJson); + Assert.Equal("[{\"__dotNetObject\":1},{\"obj2\":{\"__dotNetObject\":3},\"obj3\":{\"__dotNetObject\":4},\"obj1SameRef\":{\"__dotNetObject\":1},\"obj1DifferentRef\":{\"__dotNetObject\":2}}]", call.ArgsJson); // Assert: Objects were tracked - Assert.Same(obj1, runtime.ArgSerializerStrategy.FindDotNetObject(1)); - Assert.Same(obj2, runtime.ArgSerializerStrategy.FindDotNetObject(2)); - Assert.Same(obj3, runtime.ArgSerializerStrategy.FindDotNetObject(3)); - Assert.Same(obj1, runtime.ArgSerializerStrategy.FindDotNetObject(4)); - } - - [Fact] - public void SupportsCustomSerializationForArguments() - { - // Arrange - var runtime = new TestJSRuntime(); - - // Arrange/Act - runtime.InvokeAsync("test identifier", - new WithCustomArgSerializer()); - - // Asssert - var call = runtime.BeginInvokeCalls.Single(); - Assert.Equal("[{\"key1\":\"value1\",\"key2\":123}]", call.ArgsJson); + Assert.Same(obj1, runtime.ObjectRefManager.FindDotNetObject(1)); + Assert.Same(obj1, runtime.ObjectRefManager.FindDotNetObject(2)); + Assert.Same(obj2, runtime.ObjectRefManager.FindDotNetObject(3)); + Assert.Same(obj3, runtime.ObjectRefManager.FindDotNetObject(4)); } class TestJSRuntime : JSRuntimeBase @@ -172,20 +161,8 @@ protected override void BeginInvokeJS(long asyncHandle, string identifier, strin }); } - public void OnEndInvoke(long asyncHandle, bool succeeded, object resultOrException) - => EndInvokeJS(asyncHandle, succeeded, resultOrException); - } - - class WithCustomArgSerializer : ICustomArgSerializer - { - public object ToJsonPrimitive() - { - return new Dictionary - { - { "key1", "value1" }, - { "key2", 123 }, - }; - } + public void OnEndInvoke(long asyncHandle, bool succeeded, JSAsyncCallResult callResult) + => EndInvokeJS(asyncHandle, succeeded, callResult); } } } diff --git a/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeTest.cs index b8a1c363dc99..ca8c96df6010 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeTest.cs @@ -29,9 +29,6 @@ private class FakeJSRuntime : IJSRuntime { public Task InvokeAsync(string identifier, params object[] args) => throw new NotImplementedException(); - - public void UntrackObjectRef(DotNetObjectRef dotNetObjectRef) - => throw new NotImplementedException(); } } } diff --git a/src/JSInterop/Microsoft.JSInterop/test/JsonUtilTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JsonUtilTest.cs deleted file mode 100644 index 2b239faab9c2..000000000000 --- a/src/JSInterop/Microsoft.JSInterop/test/JsonUtilTest.cs +++ /dev/null @@ -1,349 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using Microsoft.JSInterop.Internal; -using System; -using System.Collections.Generic; -using Xunit; - -namespace Microsoft.JSInterop.Tests -{ - public class JsonUtilTest - { - // It's not useful to have a complete set of behavior specifications for - // what the JSON serializer/deserializer does in all cases here. We merely - // expose a simple wrapper over a third-party library that maintains its - // own specs and tests. - // - // We should only add tests here to cover behaviors that Blazor itself - // depends on. - - [Theory] - [InlineData(null, "null")] - [InlineData("My string", "\"My string\"")] - [InlineData(123, "123")] - [InlineData(123.456f, "123.456")] - [InlineData(123.456d, "123.456")] - [InlineData(true, "true")] - public void CanSerializePrimitivesToJson(object value, string expectedJson) - { - Assert.Equal(expectedJson, Json.Serialize(value)); - } - - [Theory] - [InlineData("null", null)] - [InlineData("\"My string\"", "My string")] - [InlineData("123", 123L)] // Would also accept 123 as a System.Int32, but Int64 is fine as a default - [InlineData("123.456", 123.456d)] - [InlineData("true", true)] - public void CanDeserializePrimitivesFromJson(string json, object expectedValue) - { - Assert.Equal(expectedValue, Json.Deserialize(json)); - } - - [Fact] - public void CanSerializeClassToJson() - { - // Arrange - var person = new Person - { - Id = 1844, - Name = "Athos", - Pets = new[] { "Aramis", "Porthos", "D'Artagnan" }, - Hobby = Hobbies.Swordfighting, - SecondaryHobby = Hobbies.Reading, - Nicknames = new List { "Comte de la Fère", "Armand" }, - BirthInstant = new DateTimeOffset(1825, 8, 6, 18, 45, 21, TimeSpan.FromHours(-6)), - Age = new TimeSpan(7665, 1, 30, 0), - Allergies = new Dictionary { { "Ducks", true }, { "Geese", false } }, - }; - - // Act/Assert - Assert.Equal( - "{\"id\":1844,\"name\":\"Athos\",\"pets\":[\"Aramis\",\"Porthos\",\"D'Artagnan\"],\"hobby\":2,\"secondaryHobby\":1,\"nullHobby\":null,\"nicknames\":[\"Comte de la Fère\",\"Armand\"],\"birthInstant\":\"1825-08-06T18:45:21.0000000-06:00\",\"age\":\"7665.01:30:00\",\"allergies\":{\"Ducks\":true,\"Geese\":false}}", - Json.Serialize(person)); - } - - [Fact] - public void CanDeserializeClassFromJson() - { - // Arrange - var json = "{\"id\":1844,\"name\":\"Athos\",\"pets\":[\"Aramis\",\"Porthos\",\"D'Artagnan\"],\"hobby\":2,\"secondaryHobby\":1,\"nullHobby\":null,\"nicknames\":[\"Comte de la Fère\",\"Armand\"],\"birthInstant\":\"1825-08-06T18:45:21.0000000-06:00\",\"age\":\"7665.01:30:00\",\"allergies\":{\"Ducks\":true,\"Geese\":false}}"; - - // Act - var person = Json.Deserialize(json); - - // Assert - Assert.Equal(1844, person.Id); - Assert.Equal("Athos", person.Name); - Assert.Equal(new[] { "Aramis", "Porthos", "D'Artagnan" }, person.Pets); - Assert.Equal(Hobbies.Swordfighting, person.Hobby); - Assert.Equal(Hobbies.Reading, person.SecondaryHobby); - Assert.Null(person.NullHobby); - Assert.Equal(new[] { "Comte de la Fère", "Armand" }, person.Nicknames); - Assert.Equal(new DateTimeOffset(1825, 8, 6, 18, 45, 21, TimeSpan.FromHours(-6)), person.BirthInstant); - Assert.Equal(new TimeSpan(7665, 1, 30, 0), person.Age); - Assert.Equal(new Dictionary { { "Ducks", true }, { "Geese", false } }, person.Allergies); - } - - [Fact] - public void CanDeserializeWithCaseInsensitiveKeys() - { - // Arrange - var json = "{\"ID\":1844,\"NamE\":\"Athos\"}"; - - // Act - var person = Json.Deserialize(json); - - // Assert - Assert.Equal(1844, person.Id); - Assert.Equal("Athos", person.Name); - } - - [Fact] - public void DeserializationPrefersPropertiesOverFields() - { - // Arrange - var json = "{\"member1\":\"Hello\"}"; - - // Act - var person = Json.Deserialize(json); - - // Assert - Assert.Equal("Hello", person.Member1); - Assert.Null(person.member1); - } - - [Fact] - public void CanSerializeStructToJson() - { - // Arrange - var commandResult = new SimpleStruct - { - StringProperty = "Test", - BoolProperty = true, - NullableIntProperty = 1 - }; - - // Act - var result = Json.Serialize(commandResult); - - // Assert - Assert.Equal("{\"stringProperty\":\"Test\",\"boolProperty\":true,\"nullableIntProperty\":1}", result); - } - - [Fact] - public void CanDeserializeStructFromJson() - { - // Arrange - var json = "{\"stringProperty\":\"Test\",\"boolProperty\":true,\"nullableIntProperty\":1}"; - - //Act - var simpleError = Json.Deserialize(json); - - // Assert - Assert.Equal("Test", simpleError.StringProperty); - Assert.True(simpleError.BoolProperty); - Assert.Equal(1, simpleError.NullableIntProperty); - } - - [Fact] - public void CanCreateInstanceOfClassWithPrivateConstructor() - { - // Arrange - var expectedName = "NameValue"; - var json = $"{{\"Name\":\"{expectedName}\"}}"; - - // Act - var instance = Json.Deserialize(json); - - // Assert - Assert.Equal(expectedName, instance.Name); - } - - [Fact] - public void CanSetValueOfPublicPropertiesWithNonPublicSetters() - { - // Arrange - var expectedPrivateValue = "PrivateValue"; - var expectedProtectedValue = "ProtectedValue"; - var expectedInternalValue = "InternalValue"; - - var json = "{" + - $"\"PrivateSetter\":\"{expectedPrivateValue}\"," + - $"\"ProtectedSetter\":\"{expectedProtectedValue}\"," + - $"\"InternalSetter\":\"{expectedInternalValue}\"," + - "}"; - - // Act - var instance = Json.Deserialize(json); - - // Assert - Assert.Equal(expectedPrivateValue, instance.PrivateSetter); - Assert.Equal(expectedProtectedValue, instance.ProtectedSetter); - Assert.Equal(expectedInternalValue, instance.InternalSetter); - } - - [Fact] - public void RejectsTypesWithAmbiguouslyNamedProperties() - { - var ex = Assert.Throws(() => - { - Json.Deserialize("{}"); - }); - - Assert.Equal($"The type '{typeof(ClashingProperties).FullName}' contains multiple public properties " + - $"with names case-insensitively matching '{nameof(ClashingProperties.PROP1).ToLowerInvariant()}'. " + - $"Such types cannot be used for JSON deserialization.", - ex.Message); - } - - [Fact] - public void RejectsTypesWithAmbiguouslyNamedFields() - { - var ex = Assert.Throws(() => - { - Json.Deserialize("{}"); - }); - - Assert.Equal($"The type '{typeof(ClashingFields).FullName}' contains multiple public fields " + - $"with names case-insensitively matching '{nameof(ClashingFields.Field1).ToLowerInvariant()}'. " + - $"Such types cannot be used for JSON deserialization.", - ex.Message); - } - - [Fact] - public void NonEmptyConstructorThrowsUsefulException() - { - // Arrange - var json = "{\"Property\":1}"; - var type = typeof(NonEmptyConstructorPoco); - - // Act - var exception = Assert.Throws(() => - { - Json.Deserialize(json); - }); - - // Assert - Assert.Equal( - $"Cannot deserialize JSON into type '{type.FullName}' because it does not have a public parameterless constructor.", - exception.Message); - } - - // Test cases based on https://github.com/JamesNK/Newtonsoft.Json/blob/122afba9908832bd5ac207164ee6c303bfd65cf1/Src/Newtonsoft.Json.Tests/Utilities/StringUtilsTests.cs#L41 - // The only difference is that our logic doesn't have to handle space-separated words, - // because we're only use this for camelcasing .NET member names - // - // Not all of the following cases are really valid .NET member names, but we have no reason - // to implement more logic to detect invalid member names besides the basics (null or empty). - [Theory] - [InlineData("URLValue", "urlValue")] - [InlineData("URL", "url")] - [InlineData("ID", "id")] - [InlineData("I", "i")] - [InlineData("Person", "person")] - [InlineData("xPhone", "xPhone")] - [InlineData("XPhone", "xPhone")] - [InlineData("X_Phone", "x_Phone")] - [InlineData("X__Phone", "x__Phone")] - [InlineData("IsCIA", "isCIA")] - [InlineData("VmQ", "vmQ")] - [InlineData("Xml2Json", "xml2Json")] - [InlineData("SnAkEcAsE", "snAkEcAsE")] - [InlineData("SnA__kEcAsE", "snA__kEcAsE")] - [InlineData("already_snake_case_", "already_snake_case_")] - [InlineData("IsJSONProperty", "isJSONProperty")] - [InlineData("SHOUTING_CASE", "shoutinG_CASE")] - [InlineData("9999-12-31T23:59:59.9999999Z", "9999-12-31T23:59:59.9999999Z")] - [InlineData("Hi!! This is text. Time to test.", "hi!! This is text. Time to test.")] - [InlineData("BUILDING", "building")] - [InlineData("BUILDINGProperty", "buildingProperty")] - public void MemberNameToCamelCase_Valid(string input, string expectedOutput) - { - Assert.Equal(expectedOutput, CamelCase.MemberNameToCamelCase(input)); - } - - [Theory] - [InlineData("")] - [InlineData(null)] - public void MemberNameToCamelCase_Invalid(string input) - { - var ex = Assert.Throws(() => - CamelCase.MemberNameToCamelCase(input)); - Assert.Equal("value", ex.ParamName); - Assert.StartsWith($"The value '{input ?? "null"}' is not a valid member name.", ex.Message); - } - - class NonEmptyConstructorPoco - { - public NonEmptyConstructorPoco(int parameter) { } - - public int Property { get; set; } - } - - struct SimpleStruct - { - public string StringProperty { get; set; } - public bool BoolProperty { get; set; } - public int? NullableIntProperty { get; set; } - } - - class Person - { - public int Id { get; set; } - public string Name { get; set; } - public string[] Pets { get; set; } - public Hobbies Hobby { get; set; } - public Hobbies? SecondaryHobby { get; set; } - public Hobbies? NullHobby { get; set; } - public IList Nicknames { get; set; } - public DateTimeOffset BirthInstant { get; set; } - public TimeSpan Age { get; set; } - public IDictionary Allergies { get; set; } - } - - enum Hobbies { Reading = 1, Swordfighting = 2 } - -#pragma warning disable 0649 - class ClashingProperties - { - public string Prop1 { get; set; } - public int PROP1 { get; set; } - } - - class ClashingFields - { - public string Field1; - public int field1; - } - - class PrefersPropertiesOverFields - { - public string member1; - public string Member1 { get; set; } - } -#pragma warning restore 0649 - - class PrivateConstructor - { - public string Name { get; set; } - - private PrivateConstructor() - { - } - - public PrivateConstructor(string name) - { - Name = name; - } - } - - class NonPublicSetterOnPublicProperty - { - public string PrivateSetter { get; private set; } - public string ProtectedSetter { get; protected set; } - public string InternalSetter { get; internal set; } - } - } -} From 73e95c56c18b45d0c513282a549e953f93b5f9c9 Mon Sep 17 00:00:00 2001 From: John Luo Date: Wed, 29 May 2019 16:22:17 -0700 Subject: [PATCH 091/183] Allow flaky attribute on class and assembly (dotnet/extensions#1742) * Allow flaky attribute on class and assembly\n\nCommit migrated from https://github.com/dotnet/extensions/commit/23ba460e278c3335f70595ebc5b19fe42e80da51 --- src/ObjectPool/test/DefaultObjectPoolTest.cs | 2 +- src/Testing/src/xunit/FlakyAttribute.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ObjectPool/test/DefaultObjectPoolTest.cs b/src/ObjectPool/test/DefaultObjectPoolTest.cs index b44aa7e1c743..9bf84dca03b5 100644 --- a/src/ObjectPool/test/DefaultObjectPoolTest.cs +++ b/src/ObjectPool/test/DefaultObjectPoolTest.cs @@ -45,7 +45,7 @@ public void DefaultObjectPool_CreatedByPolicy() { // Arrange var pool = new DefaultObjectPool>(new ListPolicy()); - + // Act var list = pool.Get(); diff --git a/src/Testing/src/xunit/FlakyAttribute.cs b/src/Testing/src/xunit/FlakyAttribute.cs index f58026c7ca22..ab4450e68537 100644 --- a/src/Testing/src/xunit/FlakyAttribute.cs +++ b/src/Testing/src/xunit/FlakyAttribute.cs @@ -50,7 +50,7 @@ namespace Microsoft.AspNetCore.Testing.xunit /// /// [TraitDiscoverer("Microsoft.AspNetCore.Testing.xunit.FlakyTestDiscoverer", "Microsoft.AspNetCore.Testing")] - [AttributeUsage(AttributeTargets.Method)] + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly)] public sealed class FlakyAttribute : Attribute, ITraitAttribute { /// From f402cc566a3fd982999f8a4e157632488865e118 Mon Sep 17 00:00:00 2001 From: Yves Marx Date: Wed, 5 Jun 2019 08:19:46 +0200 Subject: [PATCH 092/183] ActivatorUtilities.CreateInstance: allocation optimization \n\nCommit migrated from https://github.com/dotnet/extensions/commit/689a832cedd7eec1c9643728551b77233f18b08d --- .../ActivatorUtilities/ActivatorUtilities.cs | 51 +++++++++---------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/src/Shared/ActivatorUtilities/ActivatorUtilities.cs b/src/Shared/ActivatorUtilities/ActivatorUtilities.cs index c88914ee2870..3fd2b557ffe2 100644 --- a/src/Shared/ActivatorUtilities/ActivatorUtilities.cs +++ b/src/Shared/ActivatorUtilities/ActivatorUtilities.cs @@ -43,43 +43,45 @@ public static object CreateInstance(IServiceProvider provider, Type instanceType int bestLength = -1; var seenPreferred = false; - ConstructorMatcher bestMatcher = null; + ConstructorMatcher bestMatcher = default; if (!instanceType.GetTypeInfo().IsAbstract) { foreach (var constructor in instanceType .GetTypeInfo() - .DeclaredConstructors - .Where(c => !c.IsStatic && c.IsPublic)) + .DeclaredConstructors) { - var matcher = new ConstructorMatcher(constructor); - var isPreferred = constructor.IsDefined(typeof(ActivatorUtilitiesConstructorAttribute), false); - var length = matcher.Match(parameters); - - if (isPreferred) + if (!constructor.IsStatic && constructor.IsPublic) { - if (seenPreferred) + var matcher = new ConstructorMatcher(constructor); + var isPreferred = constructor.IsDefined(typeof(ActivatorUtilitiesConstructorAttribute), false); + var length = matcher.Match(parameters); + + if (isPreferred) { - ThrowMultipleCtorsMarkedWithAttributeException(); + if (seenPreferred) + { + ThrowMultipleCtorsMarkedWithAttributeException(); + } + + if (length == -1) + { + ThrowMarkedCtorDoesNotTakeAllProvidedArguments(); + } } - if (length == -1) + if (isPreferred || bestLength < length) { - ThrowMarkedCtorDoesNotTakeAllProvidedArguments(); + bestLength = length; + bestMatcher = matcher; } - } - if (isPreferred || bestLength < length) - { - bestLength = length; - bestMatcher = matcher; + seenPreferred |= isPreferred; } - - seenPreferred |= isPreferred; } } - if (bestMatcher == null) + if (bestLength == -1) { var message = $"A suitable constructor for type '{instanceType}' could not be located. Ensure the type is concrete and services are registered for all parameters of a public constructor."; throw new InvalidOperationException(message); @@ -327,18 +329,16 @@ private static bool TryCreateParameterMap(ParameterInfo[] constructorParameters, return true; } - private class ConstructorMatcher + private struct ConstructorMatcher { private readonly ConstructorInfo _constructor; private readonly ParameterInfo[] _parameters; private readonly object[] _parameterValues; - private readonly bool[] _parameterValuesSet; public ConstructorMatcher(ConstructorInfo constructor) { _constructor = constructor; _parameters = _constructor.GetParameters(); - _parameterValuesSet = new bool[_parameters.Length]; _parameterValues = new object[_parameters.Length]; } @@ -353,11 +353,10 @@ public int Match(object[] givenParameters) for (var applyIndex = applyIndexStart; givenMatched == false && applyIndex != _parameters.Length; ++applyIndex) { - if (_parameterValuesSet[applyIndex] == false && + if (_parameterValues[applyIndex] == null && _parameters[applyIndex].ParameterType.GetTypeInfo().IsAssignableFrom(givenType)) { givenMatched = true; - _parameterValuesSet[applyIndex] = true; _parameterValues[applyIndex] = givenParameters[givenIndex]; if (applyIndexStart == applyIndex) { @@ -382,7 +381,7 @@ public object CreateInstance(IServiceProvider provider) { for (var index = 0; index != _parameters.Length; index++) { - if (_parameterValuesSet[index] == false) + if (_parameterValues[index] == null) { var value = provider.GetService(_parameters[index].ParameterType); if (value == null) From 3875b0f292d6db6b7d5a51366449004ac0caa713 Mon Sep 17 00:00:00 2001 From: Justin Kotalik Date: Fri, 7 Jun 2019 11:32:18 -0700 Subject: [PATCH 093/183] Add retries to HttpClientSlim on macOS. (dotnet/extensions#1807) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/d4d110a8381de00d8754b8efd2c21012263f73a5 --- src/Testing/src/HttpClientSlim.cs | 74 ++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 20 deletions(-) diff --git a/src/Testing/src/HttpClientSlim.cs b/src/Testing/src/HttpClientSlim.cs index 6214ffefc18f..890ec2d160a7 100644 --- a/src/Testing/src/HttpClientSlim.cs +++ b/src/Testing/src/HttpClientSlim.cs @@ -8,6 +8,7 @@ using System.Net.Http; using System.Net.Security; using System.Net.Sockets; +using System.Runtime.InteropServices; using System.Security.Authentication; using System.Text; using System.Threading.Tasks; @@ -24,17 +25,20 @@ public static async Task GetStringAsync(string requestUri, bool validate public static async Task GetStringAsync(Uri requestUri, bool validateCertificate = true) { - using (var stream = await GetStream(requestUri, validateCertificate).ConfigureAwait(false)) + return await RetryRequest(async () => { - using (var writer = new StreamWriter(stream, Encoding.ASCII, bufferSize: 1024, leaveOpen: true)) + using (var stream = await GetStream(requestUri, validateCertificate).ConfigureAwait(false)) { - await writer.WriteAsync($"GET {requestUri.PathAndQuery} HTTP/1.0\r\n").ConfigureAwait(false); - await writer.WriteAsync($"Host: {GetHost(requestUri)}\r\n").ConfigureAwait(false); - await writer.WriteAsync("\r\n").ConfigureAwait(false); + using (var writer = new StreamWriter(stream, Encoding.ASCII, bufferSize: 1024, leaveOpen: true)) + { + await writer.WriteAsync($"GET {requestUri.PathAndQuery} HTTP/1.0\r\n").ConfigureAwait(false); + await writer.WriteAsync($"Host: {GetHost(requestUri)}\r\n").ConfigureAwait(false); + await writer.WriteAsync("\r\n").ConfigureAwait(false); + } + + return await ReadResponse(stream).ConfigureAwait(false); } - - return await ReadResponse(stream).ConfigureAwait(false); - } + }); } internal static string GetHost(Uri requestUri) @@ -62,21 +66,24 @@ public static async Task PostAsync(string requestUri, HttpContent conten public static async Task PostAsync(Uri requestUri, HttpContent content, bool validateCertificate = true) { - using (var stream = await GetStream(requestUri, validateCertificate)) + return await RetryRequest(async () => { - using (var writer = new StreamWriter(stream, Encoding.ASCII, bufferSize: 1024, leaveOpen: true)) + using (var stream = await GetStream(requestUri, validateCertificate)) { - await writer.WriteAsync($"POST {requestUri.PathAndQuery} HTTP/1.0\r\n").ConfigureAwait(false); - await writer.WriteAsync($"Host: {requestUri.Authority}\r\n").ConfigureAwait(false); - await writer.WriteAsync($"Content-Type: {content.Headers.ContentType}\r\n").ConfigureAwait(false); - await writer.WriteAsync($"Content-Length: {content.Headers.ContentLength}\r\n").ConfigureAwait(false); - await writer.WriteAsync("\r\n").ConfigureAwait(false); + using (var writer = new StreamWriter(stream, Encoding.ASCII, bufferSize: 1024, leaveOpen: true)) + { + await writer.WriteAsync($"POST {requestUri.PathAndQuery} HTTP/1.0\r\n").ConfigureAwait(false); + await writer.WriteAsync($"Host: {requestUri.Authority}\r\n").ConfigureAwait(false); + await writer.WriteAsync($"Content-Type: {content.Headers.ContentType}\r\n").ConfigureAwait(false); + await writer.WriteAsync($"Content-Length: {content.Headers.ContentLength}\r\n").ConfigureAwait(false); + await writer.WriteAsync("\r\n").ConfigureAwait(false); + } + + await content.CopyToAsync(stream).ConfigureAwait(false); + + return await ReadResponse(stream).ConfigureAwait(false); } - - await content.CopyToAsync(stream).ConfigureAwait(false); - - return await ReadResponse(stream).ConfigureAwait(false); - } + }); } private static async Task ReadResponse(Stream stream) @@ -94,6 +101,33 @@ private static async Task ReadResponse(Stream stream) } } + private static async Task RetryRequest(Func> retryBlock) + { + var retryCount = 1; + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + retryCount = 3; + } + + for (var retry = 0; retry < retryCount; retry++) + { + try + { + return await retryBlock().ConfigureAwait(false); + } + catch (InvalidDataException) + { + if (retry == retryCount - 1) + { + throw; + } + } + } + + // This will never be hit. + throw new NotSupportedException(); + } + private static HttpStatusCode GetStatus(string response) { var statusStart = response.IndexOf(' ') + 1; From e31f189c71ad5940147934a991418d75ef2d80bc Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Fri, 7 Jun 2019 19:08:51 +0000 Subject: [PATCH 094/183] [master] Update dependencies from dotnet/core-setup (dotnet/extensions#1803) * Update dependencies from https://github.com/dotnet/core-setup build 20190605.02 - Microsoft.NETCore.App - 3.0.0-preview7-27805-02 - NETStandard.Library.Ref - 2.1.0-preview7-27805-02 Dependency coherency updates - Microsoft.Win32.Registry - 4.6.0-preview7.19305.9 (parent: Microsoft.NETCore.App) - System.ComponentModel.Annotations - 4.6.0-preview7.19305.9 (parent: Microsoft.NETCore.App) - System.Data.SqlClient - 4.7.0-preview7.19305.9 (parent: Microsoft.NETCore.App) - System.Diagnostics.DiagnosticSource - 4.6.0-preview7.19305.9 (parent: Microsoft.NETCore.App) - System.Diagnostics.EventLog - 4.6.0-preview7.19305.9 (parent: Microsoft.NETCore.App) - System.IO.Pipelines - 4.6.0-preview7.19305.9 (parent: Microsoft.NETCore.App) - System.Reflection.Metadata - 1.7.0-preview7.19305.9 (parent: Microsoft.NETCore.App) - System.Runtime.CompilerServices.Unsafe - 4.6.0-preview7.19305.9 (parent: Microsoft.NETCore.App) - System.Security.Cryptography.Cng - 4.6.0-preview7.19305.9 (parent: Microsoft.NETCore.App) - System.Security.Cryptography.Xml - 4.6.0-preview7.19305.9 (parent: Microsoft.NETCore.App) - System.ServiceProcess.ServiceController - 4.6.0-preview7.19305.9 (parent: Microsoft.NETCore.App) - System.Text.Encodings.Web - 4.6.0-preview7.19305.9 (parent: Microsoft.NETCore.App) - System.Text.Json - 4.6.0-preview7.19305.9 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.Platforms - 3.0.0-preview7.19305.9 (parent: Microsoft.NETCore.App) * Update dependencies from https://github.com/dotnet/core-setup build 20190523.07 - Microsoft.NETCore.App - 3.0.0-preview6-27723-07 - NETStandard.Library.Ref - 2.1.0-preview6-27723-07 Dependency coherency updates - Microsoft.Win32.Registry - 4.6.0-preview6.19273.4 (parent: Microsoft.NETCore.App) - System.ComponentModel.Annotations - 4.6.0-preview6.19273.4 (parent: Microsoft.NETCore.App) - System.Data.SqlClient - 4.7.0-preview6.19273.4 (parent: Microsoft.NETCore.App) - System.Diagnostics.DiagnosticSource - 4.6.0-preview6.19273.4 (parent: Microsoft.NETCore.App) - System.Diagnostics.EventLog - 4.6.0-preview6.19273.4 (parent: Microsoft.NETCore.App) - System.IO.Pipelines - 4.6.0-preview6.19273.4 (parent: Microsoft.NETCore.App) - System.Reflection.Metadata - 1.7.0-preview6.19273.4 (parent: Microsoft.NETCore.App) - System.Runtime.CompilerServices.Unsafe - 4.6.0-preview6.19273.4 (parent: Microsoft.NETCore.App) - System.Security.Cryptography.Cng - 4.6.0-preview6.19273.4 (parent: Microsoft.NETCore.App) - System.Security.Cryptography.Xml - 4.6.0-preview6.19273.4 (parent: Microsoft.NETCore.App) - System.ServiceProcess.ServiceController - 4.6.0-preview6.19273.4 (parent: Microsoft.NETCore.App) - System.Text.Encodings.Web - 4.6.0-preview6.19273.4 (parent: Microsoft.NETCore.App) - System.Text.Json - 4.6.0-preview6.19273.4 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.Platforms - 3.0.0-preview6.19273.4 (parent: Microsoft.NETCore.App) * Update dependencies from https://github.com/dotnet/core-setup build 20190606.03 - Microsoft.NETCore.App - 3.0.0-preview7-27806-03 - NETStandard.Library.Ref - 2.1.0-preview7-27806-03 Dependency coherency updates - Microsoft.Win32.Registry - 4.6.0-preview7.19306.6 (parent: Microsoft.NETCore.App) - System.ComponentModel.Annotations - 4.6.0-preview7.19306.6 (parent: Microsoft.NETCore.App) - System.Data.SqlClient - 4.7.0-preview7.19306.6 (parent: Microsoft.NETCore.App) - System.Diagnostics.DiagnosticSource - 4.6.0-preview7.19306.6 (parent: Microsoft.NETCore.App) - System.Diagnostics.EventLog - 4.6.0-preview7.19306.6 (parent: Microsoft.NETCore.App) - System.IO.Pipelines - 4.6.0-preview7.19306.6 (parent: Microsoft.NETCore.App) - System.Reflection.Metadata - 1.7.0-preview7.19306.6 (parent: Microsoft.NETCore.App) - System.Runtime.CompilerServices.Unsafe - 4.6.0-preview7.19306.6 (parent: Microsoft.NETCore.App) - System.Security.Cryptography.Cng - 4.6.0-preview7.19306.6 (parent: Microsoft.NETCore.App) - System.Security.Cryptography.Xml - 4.6.0-preview7.19306.6 (parent: Microsoft.NETCore.App) - System.ServiceProcess.ServiceController - 4.6.0-preview7.19306.6 (parent: Microsoft.NETCore.App) - System.Text.Encodings.Web - 4.6.0-preview7.19306.6 (parent: Microsoft.NETCore.App) - System.Text.Json - 4.6.0-preview7.19306.6 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.Platforms - 3.0.0-preview7.19306.6 (parent: Microsoft.NETCore.App) * namespace \n\nCommit migrated from https://github.com/dotnet/extensions/commit/537f0c2db7f52b50f7589d0c6db026d50d856863 --- src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntimeBase.cs | 2 +- src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs | 2 +- .../Microsoft.JSInterop/src/JsonSerializerOptionsProvider.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntimeBase.cs b/src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntimeBase.cs index 7e6dcdd46207..2565496a54c9 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntimeBase.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntimeBase.cs @@ -1,7 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System.Text.Json.Serialization; +using System.Text.Json; namespace Microsoft.JSInterop { diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs b/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs index 70ab856a89d6..3e39e42e9a4e 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Concurrent; using System.Runtime.ExceptionServices; -using System.Text.Json.Serialization; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Microsoft.JSInterop.Internal; diff --git a/src/JSInterop/Microsoft.JSInterop/src/JsonSerializerOptionsProvider.cs b/src/JSInterop/Microsoft.JSInterop/src/JsonSerializerOptionsProvider.cs index 0292039eaf7c..fb299aaf026e 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/JsonSerializerOptionsProvider.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/JsonSerializerOptionsProvider.cs @@ -1,7 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System.Text.Json.Serialization; +using System.Text.Json; namespace Microsoft.JSInterop { From 5868b659346509abbe58ab631d644e9381f9b7ae Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Wed, 12 Jun 2019 22:39:41 -0700 Subject: [PATCH 095/183] Use even more Arcade and other csproj cleanups (dotnet/extensions#1833) * Use Arcade's convention for setting IsPackable (must be explicitly set) * Use Arcade conventions for using DebugType and eng/Versions.props * Remove dead code * Update restore feeds in daily builds.md * Disable UsingToolNetFrameworkReferenceAssemblies in analyzer tests * Remove usage of TestGroupName (an obsolete KoreBuild setting) * Use IVT as a .csproj attribute \n\nCommit migrated from https://github.com/dotnet/extensions/commit/f12d709976e382672ce100bc00381a8847f06489 --- ...rosoft.Extensions.Configuration.KeyPerFile.csproj | 1 + ...icrosoft.Extensions.FileProviders.Embedded.csproj | 4 ++-- ...ions.Diagnostics.HealthChecks.Abstractions.csproj | 1 + ...rosoft.Extensions.Diagnostics.HealthChecks.csproj | 1 + .../src/Microsoft.JSInterop.csproj | 3 ++- .../src/Mono.WebAssembly.Interop.csproj | 1 + ...osoft.Extensions.Localization.Abstractions.csproj | 1 + .../src/Microsoft.Extensions.Localization.csproj | 1 + .../src/Microsoft.Extensions.ObjectPool.csproj | 5 +++++ src/ObjectPool/src/Properties/AssemblyInfo.cs | 3 --- src/Shared/ActivatorUtilities/sharedsources.props | 12 ------------ .../src/Microsoft.Extensions.WebEncoders.csproj | 1 + 12 files changed, 16 insertions(+), 18 deletions(-) delete mode 100644 src/ObjectPool/src/Properties/AssemblyInfo.cs delete mode 100644 src/Shared/ActivatorUtilities/sharedsources.props diff --git a/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj b/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj index 5bd7b2c7ef0f..8c76c174ad14 100644 --- a/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj +++ b/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj @@ -3,6 +3,7 @@ Configuration provider that uses files in a directory for Microsoft.Extensions.Configuration. netstandard2.0 + true true diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj index 7d84c19f9ddc..8119c2633903 100644 --- a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj @@ -5,6 +5,7 @@ File provider for files in embedded resources for Microsoft.Extensions.FileProviders. netstandard2.0 $(MSBuildProjectName).nuspec + true true @@ -24,7 +25,6 @@ - $(PackageTags.Replace(';',' ')) <_OutputBinary>@(BuiltProjectOutputGroupOutput) <_OutputSymbol>@(DebugSymbolsProjectOutputGroupOutput) <_OutputDocumentation>@(DocumentationProjectOutputGroupOutput) @@ -37,7 +37,7 @@ - + diff --git a/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj b/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj index 2bba5959a32f..306a303421eb 100644 --- a/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj +++ b/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj @@ -11,6 +11,7 @@ Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck $(NoWarn);CS1591 true diagnostics;healthchecks + true true diff --git a/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj b/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj index 463e5b36325e..9670e8bb7b84 100644 --- a/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj +++ b/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj @@ -10,6 +10,7 @@ Microsoft.Extensions.Diagnostics.HealthChecks.IHealthChecksBuilder $(NoWarn);CS1591 true diagnostics;healthchecks + true true diff --git a/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj b/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj index bc912b97cc55..8dfb1ef22edb 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj +++ b/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj @@ -5,13 +5,14 @@ Abstractions and features for interop between .NET and JavaScript code. javascript;interop true + true true - + diff --git a/src/JSInterop/Mono.WebAssembly.Interop/src/Mono.WebAssembly.Interop.csproj b/src/JSInterop/Mono.WebAssembly.Interop/src/Mono.WebAssembly.Interop.csproj index 413d084e482f..fc927084563b 100644 --- a/src/JSInterop/Mono.WebAssembly.Interop/src/Mono.WebAssembly.Interop.csproj +++ b/src/JSInterop/Mono.WebAssembly.Interop/src/Mono.WebAssembly.Interop.csproj @@ -5,6 +5,7 @@ Abstractions and features for interop between Mono WebAssembly and JavaScript code. wasm;javascript;interop true + true true diff --git a/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj b/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj index 33f58b63587b..86af6dfde9fe 100644 --- a/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj +++ b/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj @@ -10,6 +10,7 @@ Microsoft.Extensions.Localization.IStringLocalizer<T> $(NoWarn);CS1591 true localization + true true diff --git a/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj b/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj index f16cbd9dab3f..5949cc5842ec 100644 --- a/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj +++ b/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj @@ -7,6 +7,7 @@ $(NoWarn);CS1591 true localization + true true diff --git a/src/ObjectPool/src/Microsoft.Extensions.ObjectPool.csproj b/src/ObjectPool/src/Microsoft.Extensions.ObjectPool.csproj index 71e9abed79c7..c193ad6e1f16 100644 --- a/src/ObjectPool/src/Microsoft.Extensions.ObjectPool.csproj +++ b/src/ObjectPool/src/Microsoft.Extensions.ObjectPool.csproj @@ -6,7 +6,12 @@ $(NoWarn);CS1591 true pooling + true true + + + + diff --git a/src/ObjectPool/src/Properties/AssemblyInfo.cs b/src/ObjectPool/src/Properties/AssemblyInfo.cs deleted file mode 100644 index 121f8990b1cc..000000000000 --- a/src/ObjectPool/src/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Microsoft.Extensions.ObjectPool.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] diff --git a/src/Shared/ActivatorUtilities/sharedsources.props b/src/Shared/ActivatorUtilities/sharedsources.props deleted file mode 100644 index f75467753195..000000000000 --- a/src/Shared/ActivatorUtilities/sharedsources.props +++ /dev/null @@ -1,12 +0,0 @@ - - - false - - - - - true - $(ContentTargetFolders)\cs\netstandard1.0\ - - - diff --git a/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj b/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj index 8f60f8f98305..cec4a31d8678 100644 --- a/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj +++ b/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj @@ -6,6 +6,7 @@ $(NoWarn);CS1591 true aspnetcore + true true From 2dd8eb61baae0a90013409ddb429382f99e99265 Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Thu, 13 Jun 2019 16:27:19 -0700 Subject: [PATCH 096/183] Disable transitive project references in test projects (dotnet/extensions#1834) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/e7d5bea3bbd9c942c523e9068b17f11bb35f332b --- ...rosoft.Extensions.Configuration.KeyPerFile.Tests.csproj | 6 ++++++ ...icrosoft.Extensions.FileProviders.Embedded.Tests.csproj | 2 ++ ...rosoft.Extensions.Diagnostics.HealthChecks.Tests.csproj | 7 +++++++ .../test/Microsoft.JSInterop.Tests.csproj | 2 +- .../test/Microsoft.Extensions.Localization.Tests.csproj | 7 +++++++ .../test/Microsoft.Extensions.ObjectPool.Tests.csproj | 2 +- src/Testing/test/Microsoft.AspNetCore.Testing.Tests.csproj | 5 +---- .../test/Microsoft.Extensions.WebEncoders.Tests.csproj | 3 ++- 8 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/Configuration.KeyPerFile/test/Microsoft.Extensions.Configuration.KeyPerFile.Tests.csproj b/src/Configuration.KeyPerFile/test/Microsoft.Extensions.Configuration.KeyPerFile.Tests.csproj index aee7e7a7bf4e..95a73ae6cfdf 100644 --- a/src/Configuration.KeyPerFile/test/Microsoft.Extensions.Configuration.KeyPerFile.Tests.csproj +++ b/src/Configuration.KeyPerFile/test/Microsoft.Extensions.Configuration.KeyPerFile.Tests.csproj @@ -9,7 +9,13 @@ + + + + + + diff --git a/src/FileProviders/Embedded/test/Microsoft.Extensions.FileProviders.Embedded.Tests.csproj b/src/FileProviders/Embedded/test/Microsoft.Extensions.FileProviders.Embedded.Tests.csproj index 8024fb44fd86..837774770263 100644 --- a/src/FileProviders/Embedded/test/Microsoft.Extensions.FileProviders.Embedded.Tests.csproj +++ b/src/FileProviders/Embedded/test/Microsoft.Extensions.FileProviders.Embedded.Tests.csproj @@ -9,7 +9,9 @@ + + diff --git a/src/HealthChecks/HealthChecks/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests.csproj b/src/HealthChecks/HealthChecks/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests.csproj index 08cd6a35f1b2..21763e516510 100644 --- a/src/HealthChecks/HealthChecks/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests.csproj +++ b/src/HealthChecks/HealthChecks/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests.csproj @@ -8,8 +8,15 @@ + + + + + + + diff --git a/src/JSInterop/Microsoft.JSInterop/test/Microsoft.JSInterop.Tests.csproj b/src/JSInterop/Microsoft.JSInterop/test/Microsoft.JSInterop.Tests.csproj index a39b08218068..34ac70b80120 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/Microsoft.JSInterop.Tests.csproj +++ b/src/JSInterop/Microsoft.JSInterop/test/Microsoft.JSInterop.Tests.csproj @@ -5,7 +5,7 @@ - + diff --git a/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests.csproj b/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests.csproj index 32c6263ac386..17254e211053 100644 --- a/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests.csproj +++ b/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests.csproj @@ -5,9 +5,16 @@ + + + + + + + diff --git a/src/ObjectPool/test/Microsoft.Extensions.ObjectPool.Tests.csproj b/src/ObjectPool/test/Microsoft.Extensions.ObjectPool.Tests.csproj index 8c14917a65c1..1f2ad67664db 100644 --- a/src/ObjectPool/test/Microsoft.Extensions.ObjectPool.Tests.csproj +++ b/src/ObjectPool/test/Microsoft.Extensions.ObjectPool.Tests.csproj @@ -5,7 +5,7 @@ - + diff --git a/src/Testing/test/Microsoft.AspNetCore.Testing.Tests.csproj b/src/Testing/test/Microsoft.AspNetCore.Testing.Tests.csproj index acfb34b32028..e9d0dca4aebc 100644 --- a/src/Testing/test/Microsoft.AspNetCore.Testing.Tests.csproj +++ b/src/Testing/test/Microsoft.AspNetCore.Testing.Tests.csproj @@ -14,10 +14,7 @@ - - - - + diff --git a/src/WebEncoders/test/Microsoft.Extensions.WebEncoders.Tests.csproj b/src/WebEncoders/test/Microsoft.Extensions.WebEncoders.Tests.csproj index 5a28d3806578..68ad589e3f64 100755 --- a/src/WebEncoders/test/Microsoft.Extensions.WebEncoders.Tests.csproj +++ b/src/WebEncoders/test/Microsoft.Extensions.WebEncoders.Tests.csproj @@ -1,4 +1,4 @@ - + netcoreapp3.0;net472 @@ -6,6 +6,7 @@ + From 867453a06dfcffedb24cbd39bbb513773ce26377 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Thu, 13 Jun 2019 21:54:05 -0700 Subject: [PATCH 097/183] Resolve the JSInterop call if deserialization fails (dotnet/extensions#1802) * Resolve the JSInterop call if deserialization fails \n\nCommit migrated from https://github.com/dotnet/extensions/commit/26cf0c9f4eb73237a460b6cc6a4912675869a0b5 --- src/JSInterop/JSInterop.slnf | 10 +++++++ .../ref/Microsoft.JSInterop.netstandard2.0.cs | 1 + .../src/DotNetDispatcher.cs | 3 +- .../Microsoft.JSInterop/src/JSException.cs | 9 ++++++ .../Microsoft.JSInterop/src/JSRuntimeBase.cs | 16 ++++++++--- .../test/DotNetDispatcherTest.cs | 18 ++++++++++++ .../test/JSRuntimeBaseTest.cs | 28 +++++++++++++++++++ 7 files changed, 79 insertions(+), 6 deletions(-) create mode 100644 src/JSInterop/JSInterop.slnf diff --git a/src/JSInterop/JSInterop.slnf b/src/JSInterop/JSInterop.slnf new file mode 100644 index 000000000000..c6cbd4cb3af2 --- /dev/null +++ b/src/JSInterop/JSInterop.slnf @@ -0,0 +1,10 @@ +{ + "solution": { + "path": "..\\..\\Extensions.sln", + "projects": [ + "src\\JSInterop\\Microsoft.JSInterop\\src\\Microsoft.JSInterop.csproj", + "src\\JSInterop\\Microsoft.JSInterop\\test\\Microsoft.JSInterop.Tests.csproj", + "src\\JSInterop\\Mono.WebAssembly.Interop\\src\\Mono.WebAssembly.Interop.csproj", + ] + } +} diff --git a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs index 1db3385bd7f8..c2884dc64a48 100644 --- a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs +++ b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs @@ -37,6 +37,7 @@ public partial interface IJSRuntime public partial class JSException : System.Exception { public JSException(string message) { } + public JSException(string message, System.Exception innerException) { } } public abstract partial class JSInProcessRuntimeBase : Microsoft.JSInterop.JSRuntimeBase, Microsoft.JSInterop.IJSInProcessRuntime, Microsoft.JSInterop.IJSRuntime { diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs index 5ecd3506e093..d7fc99f48436 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs @@ -227,11 +227,10 @@ private static object[] ParseArguments(string methodIdentifier, string argsJson, catch { // Always dispose the JsonDocument in case of an error. - jsonDocument.Dispose(); + jsonDocument?.Dispose(); throw; } - static bool IsIncorrectDotNetObjectRefUse(JsonElement item, Type parameterType) { // Check for incorrect use of DotNetObjectRef at the top level. We know it's diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSException.cs b/src/JSInterop/Microsoft.JSInterop/src/JSException.cs index 2929f693114e..c2e4f1b7aece 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/JSException.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/JSException.cs @@ -17,5 +17,14 @@ public class JSException : Exception public JSException(string message) : base(message) { } + + /// + /// Constructs an instance of . + /// + /// The exception message. + /// The inner exception. + public JSException(string message, Exception innerException) : base(message, innerException) + { + } } } diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs b/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs index 3e39e42e9a4e..e2b3875a505f 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs @@ -94,10 +94,18 @@ internal void EndInvokeJS(long asyncHandle, bool succeeded, JSAsyncCallResult as if (succeeded) { var resultType = TaskGenericsUtil.GetTaskCompletionSourceResultType(tcs); - var result = asyncCallResult != null ? - JsonSerializer.Parse(asyncCallResult.JsonElement.GetRawText(), resultType, JsonSerializerOptionsProvider.Options) : - null; - TaskGenericsUtil.SetTaskCompletionSourceResult(tcs, result); + try + { + var result = asyncCallResult != null ? + JsonSerializer.Parse(asyncCallResult.JsonElement.GetRawText(), resultType, JsonSerializerOptionsProvider.Options) : + null; + TaskGenericsUtil.SetTaskCompletionSourceResult(tcs, result); + } + catch (Exception exception) + { + var message = $"An exception occurred executing JS interop: {exception.Message}. See InnerException for more details."; + TaskGenericsUtil.SetTaskCompletionSourceException(tcs, new JSException(message, exception)); + } } else { diff --git a/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs b/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs index c65b4e4680d3..7854122febd0 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs @@ -363,6 +363,24 @@ public Task CanInvokeAsyncThrowingMethod() => WithJSRuntime(async jsRuntime => Assert.Contains(nameof(ThrowingClass.AsyncThrowingMethod), exception); }); + [Fact] + public Task BeginInvoke_ThrowsWithInvalidArgsJson_WithCallId() => WithJSRuntime(async jsRuntime => + { + // Arrange + var callId = "123"; + var resultTask = jsRuntime.NextInvocationTask; + DotNetDispatcher.BeginInvoke(callId, thisAssemblyName, "InvocableStaticWithParams", default, "not json"); + + await resultTask; // This won't throw, it sets properties on the jsRuntime. + + // Assert + using var jsonDocument = JsonDocument.Parse(jsRuntime.LastInvocationArgsJson); + var result = jsonDocument.RootElement; + Assert.Equal(callId, result[0].GetString()); + Assert.False(result[1].GetBoolean()); // Fails + Assert.Contains("JsonReaderException: '<' is an invalid start of a value.", result[2].GetString()); + }); + Task WithJSRuntime(Action testCode) { return WithJSRuntime(jsRuntime => diff --git a/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs index 82126836d8bb..4e0818219b88 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using System.Text.Json; +using System.Threading.Tasks; using Microsoft.JSInterop.Internal; using Xunit; @@ -86,6 +87,33 @@ public void CanCompleteAsyncCallsAsFailure() Assert.Equal("This is a test exception", ((JSException)task.Exception.InnerException).Message); } + [Fact] + public async Task CanCompleteAsyncCallsWithErrorsDuringDeserialization() + { + // Arrange + var runtime = new TestJSRuntime(); + + // Act/Assert: Tasks not initially completed + var unrelatedTask = runtime.InvokeAsync("unrelated call", Array.Empty()); + var task = runtime.InvokeAsync("test identifier", Array.Empty()); + Assert.False(unrelatedTask.IsCompleted); + Assert.False(task.IsCompleted); + using var jsonDocument = JsonDocument.Parse("\"Not a string\""); + + // Act/Assert: Task can be failed + runtime.OnEndInvoke( + runtime.BeginInvokeCalls[1].AsyncHandle, + /* succeeded: */ true, + new JSAsyncCallResult(jsonDocument, jsonDocument.RootElement)); + Assert.False(unrelatedTask.IsCompleted); + + var jsException = await Assert.ThrowsAsync(() => task); + Assert.IsType(jsException.InnerException); + + // Verify we've disposed the JsonDocument. + Assert.Throws(() => jsonDocument.RootElement.Type); + } + [Fact] public void CannotCompleteSameAsyncCallMoreThanOnce() { From 2a890a978f99a6ea23839eb722b39c0a51919c3e Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Fri, 14 Jun 2019 12:13:49 -0700 Subject: [PATCH 098/183] Set ReferenceAssemblyAttribute in reference assemblies (dotnet/extensions#1845) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/9107db48230c79bca89db510f6306d92ee451e5d --- .../test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj b/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj index 222c88bc679e..daf7eef7ea60 100644 --- a/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj +++ b/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj @@ -8,6 +8,7 @@ From b39ec064676bb12553252502a99ba8e06443cd4c Mon Sep 17 00:00:00 2001 From: Brennan Date: Thu, 27 Jun 2019 10:21:07 -0700 Subject: [PATCH 099/183] Update corefx dependencies (dotnet/extensions#1898) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/b398a5db3d2cd1ed6700867edaecfe41ac721918 --- .../src/DotNetDispatcher.cs | 10 +++++----- .../src/JSInProcessRuntimeBase.cs | 4 ++-- .../Microsoft.JSInterop/src/JSRuntimeBase.cs | 6 +++--- .../test/DotNetDispatcherTest.cs | 18 +++++++++--------- .../test/JSRuntimeBaseTest.cs | 2 +- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs index d7fc99f48436..360efae5d14e 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs @@ -51,7 +51,7 @@ public static string Invoke(string assemblyName, string methodIdentifier, long d return null; } - return JsonSerializer.ToString(syncResult, JsonSerializerOptionsProvider.Options); + return JsonSerializer.Serialize(syncResult, JsonSerializerOptionsProvider.Options); } /// @@ -177,9 +177,9 @@ private static object[] ParseArguments(string methodIdentifier, string argsJson, var shouldDisposeJsonDocument = true; try { - if (jsonDocument.RootElement.Type != JsonValueType.Array) + if (jsonDocument.RootElement.ValueKind != JsonValueKind.Array) { - throw new ArgumentException($"Expected a JSON array but got {jsonDocument.RootElement.Type}."); + throw new ArgumentException($"Expected a JSON array but got {jsonDocument.RootElement.ValueKind}."); } var suppliedArgsLength = jsonDocument.RootElement.GetArrayLength(); @@ -211,7 +211,7 @@ private static object[] ParseArguments(string methodIdentifier, string argsJson, } else { - suppliedArgs[index] = JsonSerializer.Parse(item.GetRawText(), parameterType, JsonSerializerOptionsProvider.Options); + suppliedArgs[index] = JsonSerializer.Deserialize(item.GetRawText(), parameterType, JsonSerializerOptionsProvider.Options); } index++; @@ -236,7 +236,7 @@ static bool IsIncorrectDotNetObjectRefUse(JsonElement item, Type parameterType) // Check for incorrect use of DotNetObjectRef at the top level. We know it's // an incorrect use if there's a object that looks like { '__dotNetObject': }, // but we aren't assigning to DotNetObjectRef{T}. - return item.Type == JsonValueType.Object && + return item.ValueKind == JsonValueKind.Object && item.TryGetProperty(DotNetObjectRefKey, out _) && !typeof(IDotNetObjectRef).IsAssignableFrom(parameterType); } diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntimeBase.cs b/src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntimeBase.cs index 2565496a54c9..7606f1865fc4 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntimeBase.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntimeBase.cs @@ -19,13 +19,13 @@ public abstract class JSInProcessRuntimeBase : JSRuntimeBase, IJSInProcessRuntim /// An instance of obtained by JSON-deserializing the return value. public TValue Invoke(string identifier, params object[] args) { - var resultJson = InvokeJS(identifier, JsonSerializer.ToString(args, JsonSerializerOptionsProvider.Options)); + var resultJson = InvokeJS(identifier, JsonSerializer.Serialize(args, JsonSerializerOptionsProvider.Options)); if (resultJson is null) { return default; } - return JsonSerializer.Parse(resultJson, JsonSerializerOptionsProvider.Options); + return JsonSerializer.Deserialize(resultJson, JsonSerializerOptionsProvider.Options); } /// diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs b/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs index e2b3875a505f..50fd7e28392e 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs @@ -42,7 +42,7 @@ public Task InvokeAsync(string identifier, params object[] args) try { var argsJson = args?.Length > 0 ? - JsonSerializer.ToString(args, JsonSerializerOptionsProvider.Options) : + JsonSerializer.Serialize(args, JsonSerializerOptionsProvider.Options) : null; BeginInvokeJS(taskId, identifier, argsJson); return tcs.Task; @@ -78,7 +78,7 @@ internal void EndInvokeDotNet(string callId, bool success, object resultOrExcept // We pass 0 as the async handle because we don't want the JS-side code to // send back any notification (we're just providing a result for an existing async call) - var args = JsonSerializer.ToString(new[] { callId, success, resultOrException }, JsonSerializerOptionsProvider.Options); + var args = JsonSerializer.Serialize(new[] { callId, success, resultOrException }, JsonSerializerOptionsProvider.Options); BeginInvokeJS(0, "DotNet.jsCallDispatcher.endInvokeDotNetFromJS", args); } @@ -97,7 +97,7 @@ internal void EndInvokeJS(long asyncHandle, bool succeeded, JSAsyncCallResult as try { var result = asyncCallResult != null ? - JsonSerializer.Parse(asyncCallResult.JsonElement.GetRawText(), resultType, JsonSerializerOptionsProvider.Options) : + JsonSerializer.Deserialize(asyncCallResult.JsonElement.GetRawText(), resultType, JsonSerializerOptionsProvider.Options) : null; TaskGenericsUtil.SetTaskCompletionSourceResult(tcs, result); } diff --git a/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs b/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs index 7854122febd0..4e8733e0672f 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs @@ -89,7 +89,7 @@ public Task CanInvokeStaticNonVoidMethod() => WithJSRuntime(jsRuntime => { // Arrange/Act var resultJson = DotNetDispatcher.Invoke(thisAssemblyName, "InvocableStaticNonVoid", default, null); - var result = JsonSerializer.Parse(resultJson, JsonSerializerOptionsProvider.Options); + var result = JsonSerializer.Deserialize(resultJson, JsonSerializerOptionsProvider.Options); // Assert Assert.Equal("Test", result.StringVal); @@ -101,7 +101,7 @@ public Task CanInvokeStaticNonVoidMethodWithoutCustomIdentifier() => WithJSRunti { // Arrange/Act var resultJson = DotNetDispatcher.Invoke(thisAssemblyName, nameof(SomePublicType.InvokableMethodWithoutCustomIdentifier), default, null); - var result = JsonSerializer.Parse(resultJson, JsonSerializerOptionsProvider.Options); + var result = JsonSerializer.Deserialize(resultJson, JsonSerializerOptionsProvider.Options); // Assert Assert.Equal("InvokableMethodWithoutCustomIdentifier", result.StringVal); @@ -117,7 +117,7 @@ public Task CanInvokeStaticWithParams() => WithJSRuntime(jsRuntime => jsRuntime.Invoke("unimportant", objectRef); // Arrange: Remaining args - var argsJson = JsonSerializer.ToString(new object[] + var argsJson = JsonSerializer.Serialize(new object[] { new TestDTO { StringVal = "Another string", IntVal = 456 }, new[] { 100, 200 }, @@ -130,7 +130,7 @@ public Task CanInvokeStaticWithParams() => WithJSRuntime(jsRuntime => var root = result.RootElement; // Assert: First result value marshalled via JSON - var resultDto1 = JsonSerializer.Parse(root[0].GetRawText(), JsonSerializerOptionsProvider.Options); + var resultDto1 = JsonSerializer.Deserialize(root[0].GetRawText(), JsonSerializerOptionsProvider.Options); Assert.Equal("ANOTHER STRING", resultDto1.StringVal); Assert.Equal(756, resultDto1.IntVal); @@ -156,7 +156,7 @@ public Task InvokingWithIncorrectUseOfDotNetObjectRefThrows() => WithJSRuntime(j jsRuntime.Invoke("unimportant", objectRef); // Arrange: Remaining args - var argsJson = JsonSerializer.ToString(new object[] + var argsJson = JsonSerializer.Serialize(new object[] { new TestDTO { StringVal = "Another string", IntVal = 456 }, new[] { 100, 200 }, @@ -262,7 +262,7 @@ public Task CanInvokeInstanceMethodWithParams() => WithJSRuntime(jsRuntime => public void CannotInvokeWithIncorrectNumberOfParams() { // Arrange - var argsJson = JsonSerializer.ToString(new object[] { 1, 2, 3, 4 }, JsonSerializerOptionsProvider.Options); + var argsJson = JsonSerializer.Serialize(new object[] { 1, 2, 3, 4 }, JsonSerializerOptionsProvider.Options); // Act/Assert var ex = Assert.Throws(() => @@ -284,7 +284,7 @@ public Task CanInvokeAsyncMethod() => WithJSRuntime(async jsRuntime => jsRuntime.Invoke("unimportant", arg1Ref, arg2Ref); // Arrange: all args - var argsJson = JsonSerializer.ToString(new object[] + var argsJson = JsonSerializer.Serialize(new object[] { new TestDTO { IntVal = 1000, StringVal = "String via JSON" }, arg2Ref, @@ -306,12 +306,12 @@ public Task CanInvokeAsyncMethod() => WithJSRuntime(async jsRuntime => Assert.True(result[1].GetBoolean()); // Success flag // Assert: First result value marshalled via JSON - var resultDto1 = JsonSerializer.Parse(resultValue[0].GetRawText(), JsonSerializerOptionsProvider.Options); + var resultDto1 = JsonSerializer.Deserialize(resultValue[0].GetRawText(), JsonSerializerOptionsProvider.Options); Assert.Equal("STRING VIA JSON", resultDto1.StringVal); Assert.Equal(2000, resultDto1.IntVal); // Assert: Second result value marshalled by ref - var resultDto2Ref = JsonSerializer.Parse>(resultValue[1].GetRawText(), JsonSerializerOptionsProvider.Options); + var resultDto2Ref = JsonSerializer.Deserialize>(resultValue[1].GetRawText(), JsonSerializerOptionsProvider.Options); var resultDto2 = resultDto2Ref.Value; Assert.Equal("MY STRING", resultDto2.StringVal); Assert.Equal(2468, resultDto2.IntVal); diff --git a/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs index 4e0818219b88..afbe5d0595b8 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs @@ -111,7 +111,7 @@ public async Task CanCompleteAsyncCallsWithErrorsDuringDeserialization() Assert.IsType(jsException.InnerException); // Verify we've disposed the JsonDocument. - Assert.Throws(() => jsonDocument.RootElement.Type); + Assert.Throws(() => jsonDocument.RootElement.ValueKind); } [Fact] From 244ed547644047a0696eba8955f6a407fb30a338 Mon Sep 17 00:00:00 2001 From: Javier Calvarro Nelson Date: Tue, 2 Jul 2019 12:40:50 +0200 Subject: [PATCH 100/183] Allow sanitization of dotnet interop exceptions (dotnet/extensions#1879) Adds the ability to sanitize exceptions during JS to .NET interop calls by overriding OnDotNetInvocationException method and returning an object to be returned to JavaScript that describes the exception.\n\nCommit migrated from https://github.com/dotnet/extensions/commit/d7eab7c0830382d435aef2cc22faf90c39b8fb54 --- .../ref/Microsoft.JSInterop.netstandard2.0.cs | 1 + .../src/DotNetDispatcher.cs | 9 ++-- .../Microsoft.JSInterop/src/JSRuntimeBase.cs | 21 +++++++-- .../test/JSRuntimeBaseTest.cs | 44 +++++++++++++++++++ 4 files changed, 67 insertions(+), 8 deletions(-) diff --git a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs index c2884dc64a48..cd383df0df3b 100644 --- a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs +++ b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs @@ -61,6 +61,7 @@ public abstract partial class JSRuntimeBase : Microsoft.JSInterop.IJSRuntime protected JSRuntimeBase() { } protected abstract void BeginInvokeJS(long asyncHandle, string identifier, string argsJson); public System.Threading.Tasks.Task InvokeAsync(string identifier, params object[] args) { throw null; } + protected virtual object OnDotNetInvocationException(System.Exception exception, string assemblyName, string methodIdentifier) { throw null; } } } namespace Microsoft.JSInterop.Internal diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs index 360efae5d14e..4359cd76c72e 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs @@ -103,7 +103,7 @@ public static void BeginInvoke(string callId, string assemblyName, string method else if (syncException != null) { // Threw synchronously, let's respond. - jsRuntimeBaseInstance.EndInvokeDotNet(callId, false, syncException); + jsRuntimeBaseInstance.EndInvokeDotNet(callId, false, syncException, assemblyName, methodIdentifier); } else if (syncResult is Task task) { @@ -114,16 +114,17 @@ public static void BeginInvoke(string callId, string assemblyName, string method if (t.Exception != null) { var exception = t.Exception.GetBaseException(); - jsRuntimeBaseInstance.EndInvokeDotNet(callId, false, ExceptionDispatchInfo.Capture(exception)); + + jsRuntimeBaseInstance.EndInvokeDotNet(callId, false, ExceptionDispatchInfo.Capture(exception), assemblyName, methodIdentifier); } var result = TaskGenericsUtil.GetTaskResult(task); - jsRuntimeBaseInstance.EndInvokeDotNet(callId, true, result); + jsRuntimeBaseInstance.EndInvokeDotNet(callId, true, result, assemblyName, methodIdentifier); }, TaskScheduler.Current); } else { - jsRuntimeBaseInstance.EndInvokeDotNet(callId, true, syncResult); + jsRuntimeBaseInstance.EndInvokeDotNet(callId, true, syncResult, assemblyName, methodIdentifier); } } diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs b/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs index 50fd7e28392e..694b0cb6256d 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs @@ -62,18 +62,31 @@ public Task InvokeAsync(string identifier, params object[] args) /// A JSON representation of the arguments. protected abstract void BeginInvokeJS(long asyncHandle, string identifier, string argsJson); - internal void EndInvokeDotNet(string callId, bool success, object resultOrException) + /// + /// Allows derived classes to configure the information about an exception in a JS interop call that gets sent to JavaScript. + /// + /// + /// This callback can be used in remote JS interop scenarios to sanitize exceptions that happen on the server to avoid disclosing + /// sensitive information to remote browser clients. + /// + /// The exception that occurred. + /// The assembly for the invoked .NET method. + /// The identifier for the invoked .NET method. + /// An object containing information about the exception. + protected virtual object OnDotNetInvocationException(Exception exception, string assemblyName, string methodIdentifier) => exception.ToString(); + + internal void EndInvokeDotNet(string callId, bool success, object resultOrException, string assemblyName, string methodIdentifier) { // For failures, the common case is to call EndInvokeDotNet with the Exception object. // For these we'll serialize as something that's useful to receive on the JS side. // If the value is not an Exception, we'll just rely on it being directly JSON-serializable. - if (!success && resultOrException is Exception) + if (!success && resultOrException is Exception ex) { - resultOrException = resultOrException.ToString(); + resultOrException = OnDotNetInvocationException(ex, assemblyName, methodIdentifier); } else if (!success && resultOrException is ExceptionDispatchInfo edi) { - resultOrException = edi.SourceException.ToString(); + resultOrException = OnDotNetInvocationException(edi.SourceException, assemblyName, methodIdentifier); } // We pass 0 as the async handle because we don't want the JS-side code to diff --git a/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs index afbe5d0595b8..b01ef5999896 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs @@ -168,6 +168,38 @@ public void SerializesDotNetObjectWrappersInKnownFormat() Assert.Same(obj3, runtime.ObjectRefManager.FindDotNetObject(4)); } + [Fact] + public void CanSanitizeDotNetInteropExceptions() + { + // Arrange + var expectedMessage = "An error ocurred while invoking '[Assembly]::Method'. Swapping to 'Development' environment will " + + "display more detailed information about the error that occurred."; + + string GetMessage(string assembly, string method) => $"An error ocurred while invoking '[{assembly}]::{method}'. Swapping to 'Development' environment will " + + "display more detailed information about the error that occurred."; + + var runtime = new TestJSRuntime() + { + OnDotNetException = (e, a, m) => new JSError { Message = GetMessage(a, m) } + }; + + var exception = new Exception("Some really sensitive data in here"); + + // Act + runtime.EndInvokeDotNet("0", false, exception, "Assembly", "Method"); + + // Assert + var call = runtime.BeginInvokeCalls.Single(); + Assert.Equal(0, call.AsyncHandle); + Assert.Equal("DotNet.jsCallDispatcher.endInvokeDotNetFromJS", call.Identifier); + Assert.Equal($"[\"0\",false,{{\"message\":\"{expectedMessage.Replace("'", "\\u0027")}\"}}]", call.ArgsJson); + } + + private class JSError + { + public string Message { get; set; } + } + class TestJSRuntime : JSRuntimeBase { public List BeginInvokeCalls = new List(); @@ -179,6 +211,18 @@ public class BeginInvokeAsyncArgs public string ArgsJson { get; set; } } + public Func OnDotNetException { get; set; } + + protected override object OnDotNetInvocationException(Exception exception, string assemblyName, string methodName) + { + if (OnDotNetException != null) + { + return OnDotNetException(exception, assemblyName, methodName); + } + + return base.OnDotNetInvocationException(exception, assemblyName, methodName); + } + protected override void BeginInvokeJS(long asyncHandle, string identifier, string argsJson) { BeginInvokeCalls.Add(new BeginInvokeAsyncArgs From 5e4f1f5fb2e70651aef0dee4068eac7df9504c31 Mon Sep 17 00:00:00 2001 From: Javier Calvarro Nelson Date: Tue, 2 Jul 2019 13:24:29 +0200 Subject: [PATCH 101/183] Adds an opt-in default timeout for async JS calls (dotnet/extensions#1880) * Adds the ability to configure a default timeout for JavaScript interop calls. * Adds the ability to pass in a cancellation token to InvokeAsync that will be used instead of the default timeout. * A default cancellation token can be passed to signal no cancellation, but it is not recommended in remote interop scenarios.\n\nCommit migrated from https://github.com/dotnet/extensions/commit/e04faac7e4a71ae03e31bcbe923f8743eefad9f7 --- .../ref/Microsoft.JSInterop.netstandard2.0.cs | 4 +- .../Microsoft.JSInterop/src/JSRuntimeBase.cs | 91 ++++++++++++++++--- .../test/JSRuntimeBaseTest.cs | 88 ++++++++++++++++-- 3 files changed, 160 insertions(+), 23 deletions(-) diff --git a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs index cd383df0df3b..b0f187bcd194 100644 --- a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs +++ b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs @@ -59,7 +59,9 @@ public static void SetCurrentJSRuntime(Microsoft.JSInterop.IJSRuntime instance) public abstract partial class JSRuntimeBase : Microsoft.JSInterop.IJSRuntime { protected JSRuntimeBase() { } - protected abstract void BeginInvokeJS(long asyncHandle, string identifier, string argsJson); + protected System.TimeSpan? DefaultAsyncTimeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + protected abstract void BeginInvokeJS(long taskId, string identifier, string argsJson); + public System.Threading.Tasks.Task InvokeAsync(string identifier, System.Collections.Generic.IEnumerable args, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public System.Threading.Tasks.Task InvokeAsync(string identifier, params object[] args) { throw null; } protected virtual object OnDotNetInvocationException(System.Exception exception, string assemblyName, string methodIdentifier) { throw null; } } diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs b/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs index 694b0cb6256d..562a6a27bab1 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs @@ -3,6 +3,8 @@ using System; using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; using System.Runtime.ExceptionServices; using System.Text.Json; using System.Threading; @@ -20,47 +22,106 @@ public abstract class JSRuntimeBase : IJSRuntime private readonly ConcurrentDictionary _pendingTasks = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _cancellationRegistrations = + new ConcurrentDictionary(); + internal DotNetObjectRefManager ObjectRefManager { get; } = new DotNetObjectRefManager(); + /// + /// Gets or sets the default timeout for asynchronous JavaScript calls. + /// + protected TimeSpan? DefaultAsyncTimeout { get; set; } + /// /// Invokes the specified JavaScript function asynchronously. /// /// The JSON-serializable return type. /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. /// JSON-serializable arguments. + /// A cancellation token to signal the cancellation of the operation. /// An instance of obtained by JSON-deserializing the return value. - public Task InvokeAsync(string identifier, params object[] args) + public Task InvokeAsync(string identifier, IEnumerable args, CancellationToken cancellationToken = default) { - // We might consider also adding a default timeout here in case we don't want to - // risk a memory leak in the scenario where the JS-side code is failing to complete - // the operation. - var taskId = Interlocked.Increment(ref _nextPendingTaskId); - var tcs = new TaskCompletionSource(); + var tcs = new TaskCompletionSource(TaskContinuationOptions.RunContinuationsAsynchronously); + if (cancellationToken != default) + { + _cancellationRegistrations[taskId] = cancellationToken.Register(() => + { + tcs.TrySetCanceled(cancellationToken); + CleanupTasksAndRegistrations(taskId); + }); + } _pendingTasks[taskId] = tcs; try { - var argsJson = args?.Length > 0 ? + if (cancellationToken.IsCancellationRequested) + { + tcs.TrySetCanceled(cancellationToken); + CleanupTasksAndRegistrations(taskId); + + return tcs.Task; + } + + var argsJson = args?.Any() == true ? JsonSerializer.Serialize(args, JsonSerializerOptionsProvider.Options) : null; BeginInvokeJS(taskId, identifier, argsJson); + return tcs.Task; } catch { - _pendingTasks.TryRemove(taskId, out _); + CleanupTasksAndRegistrations(taskId); throw; } } + private void CleanupTasksAndRegistrations(long taskId) + { + _pendingTasks.TryRemove(taskId, out _); + if (_cancellationRegistrations.TryRemove(taskId, out var registration)) + { + registration.Dispose(); + } + } + + /// + /// Invokes the specified JavaScript function asynchronously. + /// + /// The JSON-serializable return type. + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. + /// JSON-serializable arguments. + /// An instance of obtained by JSON-deserializing the return value. + public Task InvokeAsync(string identifier, params object[] args) + { + if (!DefaultAsyncTimeout.HasValue) + { + return InvokeAsync(identifier, args, default); + } + else + { + return InvokeWithDefaultCancellation(identifier, args); + } + } + + private async Task InvokeWithDefaultCancellation(string identifier, IEnumerable args) + { + using (var cts = new CancellationTokenSource(DefaultAsyncTimeout.Value)) + { + // We need to await here due to the using + return await InvokeAsync(identifier, args, cts.Token); + } + } + /// /// Begins an asynchronous function invocation. /// - /// The identifier for the function invocation, or zero if no async callback is required. + /// The identifier for the function invocation, or zero if no async callback is required. /// The identifier for the function to invoke. /// A JSON representation of the arguments. - protected abstract void BeginInvokeJS(long asyncHandle, string identifier, string argsJson); + protected abstract void BeginInvokeJS(long taskId, string identifier, string argsJson); /// /// Allows derived classes to configure the information about an exception in a JS interop call that gets sent to JavaScript. @@ -95,15 +156,19 @@ internal void EndInvokeDotNet(string callId, bool success, object resultOrExcept BeginInvokeJS(0, "DotNet.jsCallDispatcher.endInvokeDotNetFromJS", args); } - internal void EndInvokeJS(long asyncHandle, bool succeeded, JSAsyncCallResult asyncCallResult) + internal void EndInvokeJS(long taskId, bool succeeded, JSAsyncCallResult asyncCallResult) { using (asyncCallResult?.JsonDocument) { - if (!_pendingTasks.TryRemove(asyncHandle, out var tcs)) + if (!_pendingTasks.TryRemove(taskId, out var tcs)) { - throw new ArgumentException($"There is no pending task with handle '{asyncHandle}'."); + // We should simply return if we can't find an id for the invocation. + // This likely means that the method that initiated the call defined a timeout and stopped waiting. + return; } + CleanupTasksAndRegistrations(taskId); + if (succeeded) { var resultType = TaskGenericsUtil.GetTaskCompletionSourceResultType(tcs); diff --git a/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs index b01ef5999896..3ad78c303f8e 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using System.Text.Json; +using System.Threading; using System.Threading.Tasks; using Microsoft.JSInterop.Internal; using Xunit; @@ -38,6 +39,69 @@ public void DispatchesAsyncCallsWithDistinctAsyncHandles() }); } + [Fact] + public async Task InvokeAsync_CancelsAsyncTask_AfterDefaultTimeout() + { + // Arrange + var runtime = new TestJSRuntime(); + runtime.DefaultTimeout = TimeSpan.FromSeconds(1); + + // Act + var task = runtime.InvokeAsync("test identifier 1", "arg1", 123, true); + + // Assert + await Assert.ThrowsAsync(async () => await task); + } + + [Fact] + public async Task InvokeAsync_CompletesSuccessfullyBeforeTimeout() + { + // Arrange + var runtime = new TestJSRuntime(); + runtime.DefaultTimeout = TimeSpan.FromSeconds(10); + + // Act + var task = runtime.InvokeAsync("test identifier 1", "arg1", 123, true); + runtime.EndInvokeJS(2, succeeded: true, null); + + // Assert + await task; + } + + [Fact] + public async Task InvokeAsync_CancelsAsyncTasksWhenCancellationTokenFires() + { + // Arrange + using var cts = new CancellationTokenSource(); + var runtime = new TestJSRuntime(); + + // Act + var task = runtime.InvokeAsync("test identifier 1", new object[] { "arg1", 123, true }, cts.Token); + + cts.Cancel(); + + // Assert + await Assert.ThrowsAsync(async () => await task); + } + + [Fact] + public async Task InvokeAsync_DoesNotStartWorkWhenCancellationHasBeenRequested() + { + // Arrange + using var cts = new CancellationTokenSource(); + cts.Cancel(); + var runtime = new TestJSRuntime(); + + // Act + var task = runtime.InvokeAsync("test identifier 1", new object[] { "arg1", 123, true }, cts.Token); + + cts.Cancel(); + + // Assert + await Assert.ThrowsAsync(async () => await task); + Assert.Empty(runtime.BeginInvokeCalls); + } + [Fact] public void CanCompleteAsyncCallsAsSuccess() { @@ -115,21 +179,19 @@ public async Task CanCompleteAsyncCallsWithErrorsDuringDeserialization() } [Fact] - public void CannotCompleteSameAsyncCallMoreThanOnce() + public async Task CompletingSameAsyncCallMoreThanOnce_IgnoresSecondResultAsync() { // Arrange var runtime = new TestJSRuntime(); // Act/Assert - runtime.InvokeAsync("test identifier", Array.Empty()); + var task = runtime.InvokeAsync("test identifier", Array.Empty()); var asyncHandle = runtime.BeginInvokeCalls[0].AsyncHandle; - runtime.OnEndInvoke(asyncHandle, true, null); - var ex = Assert.Throws(() => - { - // Second "end invoke" will fail - runtime.OnEndInvoke(asyncHandle, true, null); - }); - Assert.Equal($"There is no pending task with handle '{asyncHandle}'.", ex.Message); + runtime.OnEndInvoke(asyncHandle, true, new JSAsyncCallResult(JsonDocument.Parse("{}"), JsonDocument.Parse("{\"Message\": \"Some data\"}").RootElement.GetProperty("Message"))); + runtime.OnEndInvoke(asyncHandle, false, new JSAsyncCallResult(null, JsonDocument.Parse("{\"Message\": \"Exception\"}").RootElement.GetProperty("Message"))); + + var result = await task; + Assert.Equal("Some data", result); } [Fact] @@ -204,6 +266,14 @@ class TestJSRuntime : JSRuntimeBase { public List BeginInvokeCalls = new List(); + public TimeSpan? DefaultTimeout + { + set + { + base.DefaultAsyncTimeout = value; + } + } + public class BeginInvokeAsyncArgs { public long AsyncHandle { get; set; } From 6069a897588812da5f895e7c4a982f14732c1c48 Mon Sep 17 00:00:00 2001 From: Javier Calvarro Nelson Date: Tue, 2 Jul 2019 19:21:16 +0200 Subject: [PATCH 102/183] Add InvokeAsync with cancellation token overload to IJSRuntime (dotnet/extensions#1915) Adds the InvokeAsync with cancellation token overload to IJSRuntime\n\nCommit migrated from https://github.com/dotnet/extensions/commit/55f0b77464408036d99c4fbdd2465ea3e38a4e9b --- .../ref/Microsoft.JSInterop.netstandard2.0.cs | 1 + src/JSInterop/Microsoft.JSInterop/src/IJSRuntime.cs | 13 ++++++++++++- .../Microsoft.JSInterop/test/JSRuntimeTest.cs | 5 +++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs index b0f187bcd194..ad95b7342b09 100644 --- a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs +++ b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs @@ -32,6 +32,7 @@ public partial interface IJSInProcessRuntime : Microsoft.JSInterop.IJSRuntime } public partial interface IJSRuntime { + System.Threading.Tasks.Task InvokeAsync(string identifier, System.Collections.Generic.IEnumerable args, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); System.Threading.Tasks.Task InvokeAsync(string identifier, params object[] args); } public partial class JSException : System.Exception diff --git a/src/JSInterop/Microsoft.JSInterop/src/IJSRuntime.cs b/src/JSInterop/Microsoft.JSInterop/src/IJSRuntime.cs index 97713bb3c136..d7ee372a732f 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/IJSRuntime.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/IJSRuntime.cs @@ -1,7 +1,8 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; +using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; namespace Microsoft.JSInterop @@ -19,5 +20,15 @@ public interface IJSRuntime /// JSON-serializable arguments. /// An instance of obtained by JSON-deserializing the return value. Task InvokeAsync(string identifier, params object[] args); + + /// + /// Invokes the specified JavaScript function asynchronously. + /// + /// The JSON-serializable return type. + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. + /// JSON-serializable arguments. + /// A cancellation token to signal the cancellation of the operation. + /// An instance of obtained by JSON-deserializing the return value. + Task InvokeAsync(string identifier, IEnumerable args, CancellationToken cancellationToken = default); } } diff --git a/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeTest.cs index ca8c96df6010..f2fab5d74110 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeTest.cs @@ -2,7 +2,9 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Generic; using System.Linq; +using System.Threading; using System.Threading.Tasks; using Xunit; @@ -29,6 +31,9 @@ private class FakeJSRuntime : IJSRuntime { public Task InvokeAsync(string identifier, params object[] args) => throw new NotImplementedException(); + + public Task InvokeAsync(string identifier, IEnumerable args, CancellationToken cancellationToken = default) => + throw new NotImplementedException(); } } } From 919734cbb2fdb3e412ce0379f0395e219effc5d3 Mon Sep 17 00:00:00 2001 From: Javier Calvarro Nelson Date: Fri, 5 Jul 2019 10:16:39 +0200 Subject: [PATCH 103/183] Invoking a method with an invalid dotnetobjectref bubbles up through the wrong path (dotnet/extensions#1940) * Handle non existing dotnet object references and returns the error through JSInterop instead of bubbling it up to the caller. * Every other error in JSInterop is returned that way. * It makes sure that the error gets to the client and is sanitized appropriately if necessary.\n\nCommit migrated from https://github.com/dotnet/extensions/commit/a30f2cbb278bb8490b32818b9af51ba64d8fba9d --- .../Microsoft.JSInterop/src/DotNetDispatcher.cs | 11 ++++++----- .../test/DotNetDispatcherTest.cs | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs index 4359cd76c72e..952a6061aba3 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs @@ -75,19 +75,20 @@ public static void BeginInvoke(string callId, string assemblyName, string method // code has to implement its own way of returning async results. var jsRuntimeBaseInstance = (JSRuntimeBase)JSRuntime.Current; - var targetInstance = (object)null; - if (dotNetObjectId != default) - { - targetInstance = DotNetObjectRefManager.Current.FindDotNetObject(dotNetObjectId); - } // Using ExceptionDispatchInfo here throughout because we want to always preserve // original stack traces. object syncResult = null; ExceptionDispatchInfo syncException = null; + object targetInstance = null; try { + if (dotNetObjectId != default) + { + targetInstance = DotNetObjectRefManager.Current.FindDotNetObject(dotNetObjectId); + } + syncResult = InvokeSynchronously(assemblyName, methodIdentifier, targetInstance, argsJson); } catch (Exception ex) diff --git a/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs b/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs index 4e8733e0672f..9e8a7b672e9a 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs @@ -381,6 +381,23 @@ public Task BeginInvoke_ThrowsWithInvalidArgsJson_WithCallId() => WithJSRuntime( Assert.Contains("JsonReaderException: '<' is an invalid start of a value.", result[2].GetString()); }); + [Fact] + public Task BeginInvoke_ThrowsWithInvalid_DotNetObjectRef() => WithJSRuntime(jsRuntime => + { + // Arrange + var callId = "123"; + var resultTask = jsRuntime.NextInvocationTask; + DotNetDispatcher.BeginInvoke(callId, null, "InvokableInstanceVoid", 1, null); + + // Assert + using var jsonDocument = JsonDocument.Parse(jsRuntime.LastInvocationArgsJson); + var result = jsonDocument.RootElement; + Assert.Equal(callId, result[0].GetString()); + Assert.False(result[1].GetBoolean()); // Fails + + Assert.StartsWith("System.ArgumentException: There is no tracked object with id '1'. Perhaps the DotNetObjectRef instance was already disposed.", result[2].GetString()); + }); + Task WithJSRuntime(Action testCode) { return WithJSRuntime(jsRuntime => From a61d9b6921c0637581f50e5945f4be759d9bab44 Mon Sep 17 00:00:00 2001 From: Ryan Brandenburg Date: Thu, 27 Jun 2019 15:58:43 -0700 Subject: [PATCH 104/183] Add some missing doc comments \n\nCommit migrated from https://github.com/dotnet/extensions/commit/238b272238ff4382df9a2fc5c7d92a0d68aafa97 --- .../Abstractions/src/LocalizedString.cs | 10 +++++++--- .../src/StringLocalizerExtensions.cs | 7 +++++-- .../Localization/src/LocalizationOptions.cs | 6 ++++++ .../Localization/src/ResourceNamesCache.cs | 11 +++++++++-- src/ObjectPool/src/DefaultObjectPool.cs | 13 +++++++++++++ src/ObjectPool/src/DefaultObjectPoolProvider.cs | 7 +++++++ src/ObjectPool/src/IPooledObjectPolicy.cs | 13 +++++++++++++ src/ObjectPool/src/ObjectPool.cs | 16 ++++++++++++++++ src/ObjectPool/src/ObjectPoolProvider.cs | 11 +++++++++++ 9 files changed, 87 insertions(+), 7 deletions(-) diff --git a/src/Localization/Abstractions/src/LocalizedString.cs b/src/Localization/Abstractions/src/LocalizedString.cs index 6556da40a0a5..7cac58d16add 100644 --- a/src/Localization/Abstractions/src/LocalizedString.cs +++ b/src/Localization/Abstractions/src/LocalizedString.cs @@ -1,5 +1,5 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; @@ -56,6 +56,10 @@ public LocalizedString(string name, string value, bool resourceNotFound, string SearchedLocation = searchedLocation; } + /// + /// Implicitly converts the to a . + /// + /// The string to be implicitly converted. public static implicit operator string(LocalizedString localizedString) { return localizedString?.Value; @@ -87,4 +91,4 @@ public static implicit operator string(LocalizedString localizedString) /// The actual string. public override string ToString() => Value; } -} \ No newline at end of file +} diff --git a/src/Localization/Abstractions/src/StringLocalizerExtensions.cs b/src/Localization/Abstractions/src/StringLocalizerExtensions.cs index bde47f74f33a..3a5747533475 100644 --- a/src/Localization/Abstractions/src/StringLocalizerExtensions.cs +++ b/src/Localization/Abstractions/src/StringLocalizerExtensions.cs @@ -1,11 +1,14 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; namespace Microsoft.Extensions.Localization { + /// + /// Extension methods for operating on 's. + /// public static class StringLocalizerExtensions { /// diff --git a/src/Localization/Localization/src/LocalizationOptions.cs b/src/Localization/Localization/src/LocalizationOptions.cs index 1b7408fe6774..1c0b82d21042 100644 --- a/src/Localization/Localization/src/LocalizationOptions.cs +++ b/src/Localization/Localization/src/LocalizationOptions.cs @@ -8,6 +8,12 @@ namespace Microsoft.Extensions.Localization /// public class LocalizationOptions { + /// + /// Creates a new . + /// + public LocalizationOptions() + { } + /// /// The relative path under application root where resource files are located. /// diff --git a/src/Localization/Localization/src/ResourceNamesCache.cs b/src/Localization/Localization/src/ResourceNamesCache.cs index 86e94c102a8c..72b7986d9f5e 100644 --- a/src/Localization/Localization/src/ResourceNamesCache.cs +++ b/src/Localization/Localization/src/ResourceNamesCache.cs @@ -1,5 +1,5 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Concurrent; @@ -14,6 +14,13 @@ public class ResourceNamesCache : IResourceNamesCache { private readonly ConcurrentDictionary> _cache = new ConcurrentDictionary>(); + /// + /// Creates a new + /// + public ResourceNamesCache() + { + } + /// public IList GetOrAdd(string name, Func> valueFactory) { diff --git a/src/ObjectPool/src/DefaultObjectPool.cs b/src/ObjectPool/src/DefaultObjectPool.cs index 070508a014b4..0ab6ed760881 100644 --- a/src/ObjectPool/src/DefaultObjectPool.cs +++ b/src/ObjectPool/src/DefaultObjectPool.cs @@ -8,6 +8,10 @@ namespace Microsoft.Extensions.ObjectPool { + /// + /// Default implementation of . + /// + /// The type to pool objects for. public class DefaultObjectPool : ObjectPool where T : class { private protected readonly ObjectWrapper[] _items; @@ -18,11 +22,20 @@ public class DefaultObjectPool : ObjectPool where T : class // This class was introduced in 2.1 to avoid the interface call where possible private protected readonly PooledObjectPolicy _fastPolicy; + /// + /// Creates an instance of . + /// + /// The pooling policy to use. public DefaultObjectPool(IPooledObjectPolicy policy) : this(policy, Environment.ProcessorCount * 2) { } + /// + /// Creates an instance of . + /// + /// The pooling policy to use. + /// The maximum number of objects to retain in the pool. public DefaultObjectPool(IPooledObjectPolicy policy, int maximumRetained) { _policy = policy ?? throw new ArgumentNullException(nameof(policy)); diff --git a/src/ObjectPool/src/DefaultObjectPoolProvider.cs b/src/ObjectPool/src/DefaultObjectPoolProvider.cs index 2e7767ab356d..f5085c26ef49 100644 --- a/src/ObjectPool/src/DefaultObjectPoolProvider.cs +++ b/src/ObjectPool/src/DefaultObjectPoolProvider.cs @@ -5,10 +5,17 @@ namespace Microsoft.Extensions.ObjectPool { + /// + /// A provider of 's. + /// public class DefaultObjectPoolProvider : ObjectPoolProvider { + /// + /// The maximum number of objects to retain in the pool. + /// public int MaximumRetained { get; set; } = Environment.ProcessorCount * 2; + /// public override ObjectPool Create(IPooledObjectPolicy policy) { if (policy == null) diff --git a/src/ObjectPool/src/IPooledObjectPolicy.cs b/src/ObjectPool/src/IPooledObjectPolicy.cs index 54611bad30ed..7bd9b9394a23 100644 --- a/src/ObjectPool/src/IPooledObjectPolicy.cs +++ b/src/ObjectPool/src/IPooledObjectPolicy.cs @@ -3,10 +3,23 @@ namespace Microsoft.Extensions.ObjectPool { + /// + /// Represents a policy for managing pooled objects. + /// + /// The type of object which is being pooled. public interface IPooledObjectPolicy { + /// + /// Create a . + /// + /// The which was created. T Create(); + /// + /// Put an object in the pool. + /// + /// The object to put in the pool. + /// true if put in the pool. bool Return(T obj); } } diff --git a/src/ObjectPool/src/ObjectPool.cs b/src/ObjectPool/src/ObjectPool.cs index 691beae60cf9..f7065c222391 100644 --- a/src/ObjectPool/src/ObjectPool.cs +++ b/src/ObjectPool/src/ObjectPool.cs @@ -3,15 +3,31 @@ namespace Microsoft.Extensions.ObjectPool { + /// + /// A pool of objects. + /// + /// The type of objects to pool. public abstract class ObjectPool where T : class { + /// + /// Returns an object from the pool. + /// + /// An object from the pool. public abstract T Get(); + /// + /// Put an object in the pool. + /// + /// The object to add to the pool. public abstract void Return(T obj); } + /// + /// Methods for creating 's. + /// public static class ObjectPool { + /// public static ObjectPool Create(IPooledObjectPolicy policy = null) where T : class, new() { var provider = new DefaultObjectPoolProvider(); diff --git a/src/ObjectPool/src/ObjectPoolProvider.cs b/src/ObjectPool/src/ObjectPoolProvider.cs index 909795dd35fe..36db247bfe97 100644 --- a/src/ObjectPool/src/ObjectPoolProvider.cs +++ b/src/ObjectPool/src/ObjectPoolProvider.cs @@ -3,13 +3,24 @@ namespace Microsoft.Extensions.ObjectPool { + /// + /// A provider of 's. + /// public abstract class ObjectPoolProvider { + /// + /// Creates an . + /// + /// The type to create a pool for. public ObjectPool Create() where T : class, new() { return Create(new DefaultPooledObjectPolicy()); } + /// + /// Creates an with the given . + /// + /// The type to create a pool for. public abstract ObjectPool Create(IPooledObjectPolicy policy) where T : class; } } From bd0623bf7265048ccd813dbc02cdf731c13923a9 Mon Sep 17 00:00:00 2001 From: Ryan Brandenburg Date: Thu, 27 Jun 2019 16:25:14 -0700 Subject: [PATCH 105/183] Fix generic - /// Default implementation of . + /// Default implementation of . /// /// The type to pool objects for. public class DefaultObjectPool : ObjectPool where T : class @@ -23,7 +23,7 @@ public class DefaultObjectPool : ObjectPool where T : class private protected readonly PooledObjectPolicy _fastPolicy; /// - /// Creates an instance of . + /// Creates an instance of . /// /// The pooling policy to use. public DefaultObjectPool(IPooledObjectPolicy policy) @@ -32,7 +32,7 @@ public DefaultObjectPool(IPooledObjectPolicy policy) } /// - /// Creates an instance of . + /// Creates an instance of . /// /// The pooling policy to use. /// The maximum number of objects to retain in the pool. diff --git a/src/ObjectPool/src/DefaultObjectPoolProvider.cs b/src/ObjectPool/src/DefaultObjectPoolProvider.cs index f5085c26ef49..a264cb65d62b 100644 --- a/src/ObjectPool/src/DefaultObjectPoolProvider.cs +++ b/src/ObjectPool/src/DefaultObjectPoolProvider.cs @@ -6,7 +6,7 @@ namespace Microsoft.Extensions.ObjectPool { /// - /// A provider of 's. + /// A provider of 's. /// public class DefaultObjectPoolProvider : ObjectPoolProvider { diff --git a/src/ObjectPool/src/ObjectPool.cs b/src/ObjectPool/src/ObjectPool.cs index f7065c222391..9dfe1ca55a23 100644 --- a/src/ObjectPool/src/ObjectPool.cs +++ b/src/ObjectPool/src/ObjectPool.cs @@ -23,7 +23,7 @@ public abstract class ObjectPool where T : class } /// - /// Methods for creating 's. + /// Methods for creating 's. /// public static class ObjectPool { diff --git a/src/ObjectPool/src/ObjectPoolProvider.cs b/src/ObjectPool/src/ObjectPoolProvider.cs index 36db247bfe97..48be729f7a05 100644 --- a/src/ObjectPool/src/ObjectPoolProvider.cs +++ b/src/ObjectPool/src/ObjectPoolProvider.cs @@ -4,7 +4,7 @@ namespace Microsoft.Extensions.ObjectPool { /// - /// A provider of 's. + /// A provider of 's. /// public abstract class ObjectPoolProvider { @@ -18,7 +18,7 @@ public abstract class ObjectPoolProvider } /// - /// Creates an with the given . + /// Creates an with the given . /// /// The type to create a pool for. public abstract ObjectPool Create(IPooledObjectPolicy policy) where T : class; From 109d388e5ec64c5e83c0e0c8e6725c7503c57d9f Mon Sep 17 00:00:00 2001 From: Ryan Brandenburg Date: Thu, 27 Jun 2019 16:57:47 -0700 Subject: [PATCH 106/183] More doc comments \n\nCommit migrated from https://github.com/dotnet/extensions/commit/d3cad4c7872ea742be8299aa454ab15da6fb5623 --- .../Localization/src/ResourceManagerStringLocalizer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Localization/Localization/src/ResourceManagerStringLocalizer.cs b/src/Localization/Localization/src/ResourceManagerStringLocalizer.cs index 90f8e077b429..a8321fca0ade 100644 --- a/src/Localization/Localization/src/ResourceManagerStringLocalizer.cs +++ b/src/Localization/Localization/src/ResourceManagerStringLocalizer.cs @@ -177,7 +177,7 @@ public virtual IEnumerable GetAllStrings(bool includeParentCult /// /// Returns all strings in the specified culture. /// - /// + /// Whether to include parent cultures in the search for a resource. /// The to get strings for. /// The strings. protected IEnumerable GetAllStrings(bool includeParentCultures, CultureInfo culture) From 25cad453f462c35ae4d4ae3e22c871c2b303562a Mon Sep 17 00:00:00 2001 From: Ryan Brandenburg Date: Fri, 28 Jun 2019 15:59:10 -0700 Subject: [PATCH 107/183] More doc comment additions \n\nCommit migrated from https://github.com/dotnet/extensions/commit/551e7c269a9050c3a45bd0b7e24e05d5b90ff122 --- src/ObjectPool/src/ObjectPool.cs | 4 ++-- src/Shared/CommandLineUtils/Utilities/ArgumentEscaper.cs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ObjectPool/src/ObjectPool.cs b/src/ObjectPool/src/ObjectPool.cs index 9dfe1ca55a23..bfc495528adf 100644 --- a/src/ObjectPool/src/ObjectPool.cs +++ b/src/ObjectPool/src/ObjectPool.cs @@ -10,9 +10,9 @@ namespace Microsoft.Extensions.ObjectPool public abstract class ObjectPool where T : class { /// - /// Returns an object from the pool. + /// Gets an object from the pool is one is available, otherwise creates one. /// - /// An object from the pool. + /// A . public abstract T Get(); /// diff --git a/src/Shared/CommandLineUtils/Utilities/ArgumentEscaper.cs b/src/Shared/CommandLineUtils/Utilities/ArgumentEscaper.cs index 7b696c5175d1..92543e7f237e 100644 --- a/src/Shared/CommandLineUtils/Utilities/ArgumentEscaper.cs +++ b/src/Shared/CommandLineUtils/Utilities/ArgumentEscaper.cs @@ -19,8 +19,8 @@ internal static class ArgumentEscaper /// /// See https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/ /// - /// - /// + /// The arguments to concatenate. + /// The escaped arguments, concatenated. public static string EscapeAndConcatenate(IEnumerable args) => string.Join(" ", args.Select(EscapeSingleArg)); @@ -104,6 +104,6 @@ private static bool IsSurroundedWithQuotes(string argument) } private static bool ContainsWhitespace(string argument) - => argument.IndexOfAny(new [] { ' ', '\t', '\n' }) >= 0; + => argument.IndexOfAny(new[] { ' ', '\t', '\n' }) >= 0; } } From f8b525f02d84ebb40193d611650d683128bc7acd Mon Sep 17 00:00:00 2001 From: Ryan Brandenburg Date: Mon, 1 Jul 2019 14:24:06 -0700 Subject: [PATCH 108/183] PR feedback \n\nCommit migrated from https://github.com/dotnet/extensions/commit/2e1cfcd1f91c260ce1be99a6f11dc1386f0c3e58 --- src/ObjectPool/src/IPooledObjectPolicy.cs | 6 +++--- src/ObjectPool/src/ObjectPool.cs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ObjectPool/src/IPooledObjectPolicy.cs b/src/ObjectPool/src/IPooledObjectPolicy.cs index 7bd9b9394a23..dc285ca5efd3 100644 --- a/src/ObjectPool/src/IPooledObjectPolicy.cs +++ b/src/ObjectPool/src/IPooledObjectPolicy.cs @@ -16,10 +16,10 @@ public interface IPooledObjectPolicy T Create(); /// - /// Put an object in the pool. + /// Return an object to the pool. /// - /// The object to put in the pool. - /// true if put in the pool. + /// The object to return to the pool. + /// true if returned to the pool. bool Return(T obj); } } diff --git a/src/ObjectPool/src/ObjectPool.cs b/src/ObjectPool/src/ObjectPool.cs index bfc495528adf..c1b98ae6c03a 100644 --- a/src/ObjectPool/src/ObjectPool.cs +++ b/src/ObjectPool/src/ObjectPool.cs @@ -10,13 +10,13 @@ namespace Microsoft.Extensions.ObjectPool public abstract class ObjectPool where T : class { /// - /// Gets an object from the pool is one is available, otherwise creates one. + /// Gets an object from the pool if one is available, otherwise creates one. /// /// A . public abstract T Get(); /// - /// Put an object in the pool. + /// Return an object to the pool. /// /// The object to add to the pool. public abstract void Return(T obj); From 97a039f154890e088520be5fa52660e8c03a28ab Mon Sep 17 00:00:00 2001 From: Ryan Brandenburg Date: Tue, 2 Jul 2019 16:39:42 -0700 Subject: [PATCH 109/183] PR feedback \n\nCommit migrated from https://github.com/dotnet/extensions/commit/d656c4f7e22d1c0b84cab1b453c50ce73c89a071 --- .../Abstractions/src/StringLocalizerExtensions.cs | 2 +- src/ObjectPool/src/DefaultObjectPool.cs | 1 + src/ObjectPool/src/DefaultObjectPoolProvider.cs | 2 +- src/ObjectPool/src/IPooledObjectPolicy.cs | 4 ++-- src/ObjectPool/src/ObjectPool.cs | 2 +- src/ObjectPool/src/ObjectPoolProvider.cs | 2 +- 6 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Localization/Abstractions/src/StringLocalizerExtensions.cs b/src/Localization/Abstractions/src/StringLocalizerExtensions.cs index 3a5747533475..2e19db7c18ee 100644 --- a/src/Localization/Abstractions/src/StringLocalizerExtensions.cs +++ b/src/Localization/Abstractions/src/StringLocalizerExtensions.cs @@ -7,7 +7,7 @@ namespace Microsoft.Extensions.Localization { /// - /// Extension methods for operating on 's. + /// Extension methods for operating on instances. /// public static class StringLocalizerExtensions { diff --git a/src/ObjectPool/src/DefaultObjectPool.cs b/src/ObjectPool/src/DefaultObjectPool.cs index 61cfe365e78c..f5627b7898bb 100644 --- a/src/ObjectPool/src/DefaultObjectPool.cs +++ b/src/ObjectPool/src/DefaultObjectPool.cs @@ -12,6 +12,7 @@ namespace Microsoft.Extensions.ObjectPool /// Default implementation of . /// /// The type to pool objects for. + /// This implementation keeps a cache of retained objects. This means that if objects are returned when the pool has already reached "maximumRetained" objects they will be available to be Garbage Collected. public class DefaultObjectPool : ObjectPool where T : class { private protected readonly ObjectWrapper[] _items; diff --git a/src/ObjectPool/src/DefaultObjectPoolProvider.cs b/src/ObjectPool/src/DefaultObjectPoolProvider.cs index a264cb65d62b..b37a946d6d55 100644 --- a/src/ObjectPool/src/DefaultObjectPoolProvider.cs +++ b/src/ObjectPool/src/DefaultObjectPoolProvider.cs @@ -6,7 +6,7 @@ namespace Microsoft.Extensions.ObjectPool { /// - /// A provider of 's. + /// The default . /// public class DefaultObjectPoolProvider : ObjectPoolProvider { diff --git a/src/ObjectPool/src/IPooledObjectPolicy.cs b/src/ObjectPool/src/IPooledObjectPolicy.cs index dc285ca5efd3..75193980300f 100644 --- a/src/ObjectPool/src/IPooledObjectPolicy.cs +++ b/src/ObjectPool/src/IPooledObjectPolicy.cs @@ -16,10 +16,10 @@ public interface IPooledObjectPolicy T Create(); /// - /// Return an object to the pool. + /// Runs some processing when an object was returned to the pool. Can be used to reset the state of an object and indicate if the object should be returned to the pool. /// /// The object to return to the pool. - /// true if returned to the pool. + /// true if the object should be returned to the pool. false if it's not possible/desirable for the pool to keep the object. bool Return(T obj); } } diff --git a/src/ObjectPool/src/ObjectPool.cs b/src/ObjectPool/src/ObjectPool.cs index c1b98ae6c03a..0a82ed6f5392 100644 --- a/src/ObjectPool/src/ObjectPool.cs +++ b/src/ObjectPool/src/ObjectPool.cs @@ -23,7 +23,7 @@ public abstract class ObjectPool where T : class } /// - /// Methods for creating 's. + /// Methods for creating instances. /// public static class ObjectPool { diff --git a/src/ObjectPool/src/ObjectPoolProvider.cs b/src/ObjectPool/src/ObjectPoolProvider.cs index 48be729f7a05..6b8ca219eac6 100644 --- a/src/ObjectPool/src/ObjectPoolProvider.cs +++ b/src/ObjectPool/src/ObjectPoolProvider.cs @@ -4,7 +4,7 @@ namespace Microsoft.Extensions.ObjectPool { /// - /// A provider of 's. + /// A provider of instances. /// public abstract class ObjectPoolProvider { From 86b55046c96d9c450a31397cac6465c6c9224962 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Tue, 9 Jul 2019 12:40:20 +0100 Subject: [PATCH 110/183] In JSInterop calls, use expected value for 'this'. Fixes dotnet/extensions#1477 (dotnet/extensions#1990) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/a664ecb5b3bcf9b2fdeb272e5e2705e8e43eeb9c --- .../src/src/Microsoft.JSInterop.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts b/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts index 5386b92bb50c..2b3f51300c12 100644 --- a/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts +++ b/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts @@ -141,14 +141,6 @@ module DotNet { * Receives incoming calls from .NET and dispatches them to JavaScript. */ export const jsCallDispatcher = { - /** - * Finds the JavaScript function matching the specified identifier. - * - * @param identifier Identifies the globally-reachable function to be returned. - * @returns A Function instance. - */ - findJSFunction, - /** * Invokes the specified synchronous JavaScript function. * @@ -227,8 +219,10 @@ module DotNet { let result: any = window; let resultIdentifier = 'window'; + let lastSegmentValue: any; identifier.split('.').forEach(segment => { if (segment in result) { + lastSegmentValue = result; result = result[segment]; resultIdentifier += '.' + segment; } else { @@ -237,6 +231,8 @@ module DotNet { }); if (result instanceof Function) { + result = result.bind(lastSegmentValue); + cachedJSFunctions[identifier] = result; return result; } else { throw new Error(`The value '${resultIdentifier}' is not a function.`); From 3c42f4436f5645359d37f95394283ff708775558 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Wed, 10 Jul 2019 12:12:11 +0100 Subject: [PATCH 111/183] Bring back required export jsCallDispatcher.findJSFunction (dotnet/extensions#2002) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/5350efd2f4faed6c2e2141176e3493d78e0ac27e --- .../Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts b/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts index 2b3f51300c12..27d22b0536e5 100644 --- a/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts +++ b/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts @@ -141,6 +141,14 @@ module DotNet { * Receives incoming calls from .NET and dispatches them to JavaScript. */ export const jsCallDispatcher = { + /** + * Finds the JavaScript function matching the specified identifier. + * + * @param identifier Identifies the globally-reachable function to be returned. + * @returns A Function instance. + */ + findJSFunction, // Note that this is used by the JS interop code inside Mono WebAssembly itself + /** * Invokes the specified synchronous JavaScript function. * From d0a69f986069011ebab0d1d757ce326d2fd22743 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Wed, 10 Jul 2019 07:54:15 -0700 Subject: [PATCH 112/183] Json Options (dotnet/extensions#1993) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/912ab186945c8622e2446eb041c786e99c92bd7e --- .../Microsoft.JSInterop/src/JsonSerializerOptionsProvider.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/JSInterop/Microsoft.JSInterop/src/JsonSerializerOptionsProvider.cs b/src/JSInterop/Microsoft.JSInterop/src/JsonSerializerOptionsProvider.cs index fb299aaf026e..62244270e38b 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/JsonSerializerOptionsProvider.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/JsonSerializerOptionsProvider.cs @@ -9,7 +9,9 @@ internal static class JsonSerializerOptionsProvider { public static readonly JsonSerializerOptions Options = new JsonSerializerOptions { + MaxDepth = 32, PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + PropertyNameCaseInsensitive = true, }; } } From 4c0a9d9a6bd36e8e2b0d2764b82c647b444b6303 Mon Sep 17 00:00:00 2001 From: Andrew Stanton-Nurse Date: Tue, 16 Jul 2019 14:42:37 -0700 Subject: [PATCH 113/183] update version of SqlClient used by sql cache (dotnet/extensions#2006) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/b1ff0d9dc87b3ad6c555d6abe6ac57e12a673836 --- ...vironmentVariableSkipConditionAttribute.cs | 10 ++--- .../EnvironmentVariableSkipConditionTest.cs | 41 +++++++++++-------- 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/src/Testing/src/xunit/EnvironmentVariableSkipConditionAttribute.cs b/src/Testing/src/xunit/EnvironmentVariableSkipConditionAttribute.cs index 8bf1bfd15eee..fe215a8e0ba1 100644 --- a/src/Testing/src/xunit/EnvironmentVariableSkipConditionAttribute.cs +++ b/src/Testing/src/xunit/EnvironmentVariableSkipConditionAttribute.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; @@ -52,9 +52,9 @@ internal EnvironmentVariableSkipConditionAttribute( } /// - /// Skips the test only if the value of the variable matches any of the supplied values. Default is True. + /// Runs the test only if the value of the variable matches any of the supplied values. Default is True. /// - public bool SkipOnMatch { get; set; } = true; + public bool RunOnMatch { get; set; } = true; public bool IsMet { @@ -63,7 +63,7 @@ public bool IsMet _currentValue = _environmentVariable.Get(_variableName); var hasMatched = _values.Any(value => string.Compare(value, _currentValue, ignoreCase: true) == 0); - if (SkipOnMatch) + if (RunOnMatch) { return hasMatched; } @@ -80,7 +80,7 @@ public string SkipReason { var value = _currentValue == null ? "(null)" : _currentValue; return $"Test skipped on environment variable with name '{_variableName}' and value '{value}' " + - $"for the '{nameof(SkipOnMatch)}' value of '{SkipOnMatch}'."; + $"for the '{nameof(RunOnMatch)}' value of '{RunOnMatch}'."; } } diff --git a/src/Testing/test/EnvironmentVariableSkipConditionTest.cs b/src/Testing/test/EnvironmentVariableSkipConditionTest.cs index b536ae56f709..d5e7b6342b31 100644 --- a/src/Testing/test/EnvironmentVariableSkipConditionTest.cs +++ b/src/Testing/test/EnvironmentVariableSkipConditionTest.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Xunit; @@ -8,7 +8,7 @@ namespace Microsoft.AspNetCore.Testing.xunit public class EnvironmentVariableSkipConditionTest { private readonly string _skipReason = "Test skipped on environment variable with name '{0}' and value '{1}'" + - $" for the '{nameof(EnvironmentVariableSkipConditionAttribute.SkipOnMatch)}' value of '{{2}}'."; + $" for the '{nameof(EnvironmentVariableSkipConditionAttribute.RunOnMatch)}' value of '{{2}}'."; [Theory] [InlineData("false")] @@ -18,7 +18,7 @@ public void IsMet_DoesNotMatch(string environmentVariableValue) { // Arrange var attribute = new EnvironmentVariableSkipConditionAttribute( - new TestEnvironmentVariable(environmentVariableValue), + new TestEnvironmentVariable("Run", environmentVariableValue), "Run", "true"); @@ -37,7 +37,7 @@ public void IsMet_DoesCaseInsensitiveMatch_OnValue(string environmentVariableVal { // Arrange var attribute = new EnvironmentVariableSkipConditionAttribute( - new TestEnvironmentVariable(environmentVariableValue), + new TestEnvironmentVariable("Run", environmentVariableValue), "Run", "true"); @@ -47,7 +47,7 @@ public void IsMet_DoesCaseInsensitiveMatch_OnValue(string environmentVariableVal // Assert Assert.True(isMet); Assert.Equal( - string.Format(_skipReason, "Run", environmentVariableValue, attribute.SkipOnMatch), + string.Format(_skipReason, "Run", environmentVariableValue, attribute.RunOnMatch), attribute.SkipReason); } @@ -56,7 +56,7 @@ public void IsMet_DoesSuccessfulMatch_OnNull() { // Arrange var attribute = new EnvironmentVariableSkipConditionAttribute( - new TestEnvironmentVariable(null), + new TestEnvironmentVariable("Run", null), "Run", "true", null); // skip the test when the variable 'Run' is explicitly set to 'true' or is null (default) @@ -66,7 +66,7 @@ public void IsMet_DoesSuccessfulMatch_OnNull() // Assert Assert.True(isMet); Assert.Equal( - string.Format(_skipReason, "Run", "(null)", attribute.SkipOnMatch), + string.Format(_skipReason, "Run", "(null)", attribute.RunOnMatch), attribute.SkipReason); } @@ -78,7 +78,7 @@ public void IsMet_MatchesOnMultipleSkipValues(string environmentVariableValue) { // Arrange var attribute = new EnvironmentVariableSkipConditionAttribute( - new TestEnvironmentVariable(environmentVariableValue), + new TestEnvironmentVariable("Run", environmentVariableValue), "Run", "false", "", null); @@ -94,7 +94,7 @@ public void IsMet_DoesNotMatch_OnMultipleSkipValues() { // Arrange var attribute = new EnvironmentVariableSkipConditionAttribute( - new TestEnvironmentVariable("100"), + new TestEnvironmentVariable("Build", "100"), "Build", "125", "126"); @@ -109,16 +109,16 @@ public void IsMet_DoesNotMatch_OnMultipleSkipValues() [InlineData("CentOS")] [InlineData(null)] [InlineData("")] - public void IsMet_Matches_WhenSkipOnMatchIsFalse(string environmentVariableValue) + public void IsMet_Matches_WhenRunOnMatchIsFalse(string environmentVariableValue) { // Arrange var attribute = new EnvironmentVariableSkipConditionAttribute( - new TestEnvironmentVariable(environmentVariableValue), + new TestEnvironmentVariable("LinuxFlavor", environmentVariableValue), "LinuxFlavor", "Ubuntu14.04") { // Example: Run this test on all OSes except on "Ubuntu14.04" - SkipOnMatch = false + RunOnMatch = false }; // Act @@ -129,16 +129,16 @@ public void IsMet_Matches_WhenSkipOnMatchIsFalse(string environmentVariableValue } [Fact] - public void IsMet_DoesNotMatch_WhenSkipOnMatchIsFalse() + public void IsMet_DoesNotMatch_WhenRunOnMatchIsFalse() { // Arrange var attribute = new EnvironmentVariableSkipConditionAttribute( - new TestEnvironmentVariable("Ubuntu14.04"), + new TestEnvironmentVariable("LinuxFlavor", "Ubuntu14.04"), "LinuxFlavor", "Ubuntu14.04") { // Example: Run this test on all OSes except on "Ubuntu14.04" - SkipOnMatch = false + RunOnMatch = false }; // Act @@ -150,8 +150,11 @@ public void IsMet_DoesNotMatch_WhenSkipOnMatchIsFalse() private struct TestEnvironmentVariable : IEnvironmentVariable { - public TestEnvironmentVariable(string value) + private readonly string _varName; + + public TestEnvironmentVariable(string varName, string value) { + _varName = varName; Value = value; } @@ -159,7 +162,11 @@ public TestEnvironmentVariable(string value) public string Get(string name) { - return Value; + if(string.Equals(name, _varName, System.StringComparison.OrdinalIgnoreCase)) + { + return Value; + } + return string.Empty; } } } From b4bcb1fd15d19eb008079bd4344edc05d920e51b Mon Sep 17 00:00:00 2001 From: Javier Calvarro Nelson Date: Wed, 17 Jul 2019 17:12:37 +0200 Subject: [PATCH 114/183] [JSInterop] Updates the JSInterop abstractions to better support logging and diagnostics. (dotnet/extensions#2043) * Add an overload to handle .NET completion calls without going through JS interop. * Add endInvokeHook on the client-side. * Replace OnDotNetInvocationException with EndInvokeDotNet.\n\nCommit migrated from https://github.com/dotnet/extensions/commit/4312b9a050700833e01a01cf9381ff9033660bc1 --- .../src/src/Microsoft.JSInterop.ts | 25 ++++--- .../ref/Microsoft.JSInterop.netstandard2.0.cs | 13 +--- .../src/DotNetDispatcher.cs | 41 ++++++++--- .../src/JSAsyncCallResult.cs | 6 +- .../Microsoft.JSInterop/src/JSRuntimeBase.cs | 45 ++++-------- .../test/DotNetDispatcherTest.cs | 71 +++++++++++-------- .../test/DotNetObjectRefTest.cs | 5 ++ .../test/JSInProcessRuntimeBaseTest.cs | 3 + .../test/JSRuntimeBaseTest.cs | 36 +++++++--- ...Mono.WebAssembly.Interop.netstandard2.0.cs | 1 + .../src/MonoWebAssemblyJSRuntime.cs | 33 +++++++++ 11 files changed, 173 insertions(+), 106 deletions(-) diff --git a/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts b/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts index 27d22b0536e5..60f6f800a6b5 100644 --- a/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts +++ b/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts @@ -75,7 +75,7 @@ module DotNet { try { const argsJson = JSON.stringify(args, argReplacer); getRequiredDispatcher().beginInvokeDotNetFromJS(asyncCallId, assemblyName, methodIdentifier, dotNetObjectId, argsJson); - } catch(ex) { + } catch (ex) { // Synchronous failure completePendingCall(asyncCallId, false, ex); } @@ -116,7 +116,7 @@ module DotNet { export interface DotNetCallDispatcher { /** * Optional. If implemented, invoked by the runtime to perform a synchronous call to a .NET method. - * + * * @param assemblyName The short name (without key/version or .dll extension) of the .NET assembly holding the method to invoke. The value may be null when invoking instance methods. * @param methodIdentifier The identifier of the method to invoke. The method must have a [JSInvokable] attribute specifying this identifier. * @param dotNetObjectId If given, the call will be to an instance method on the specified DotNetObject. Pass null or undefined to call static methods. @@ -135,6 +135,15 @@ module DotNet { * @param argsJson JSON representation of arguments to pass to the method. */ beginInvokeDotNetFromJS(callId: number, assemblyName: string | null, methodIdentifier: string, dotNetObjectId: number | null, argsJson: string): void; + + /** + * Invoked by the runtime to complete an asynchronous JavaScript function call started from .NET + * + * @param callId A value identifying the asynchronous operation. + * @param succeded Whether the operation succeeded or not. + * @param resultOrError The serialized result or the serialized error from the async operation. + */ + endInvokeJSFromDotNet(callId: number, succeeded: boolean, resultOrError: any): void; } /** @@ -183,8 +192,8 @@ module DotNet { // On completion, dispatch result back to .NET // Not using "await" because it codegens a lot of boilerplate promise.then( - result => getRequiredDispatcher().beginInvokeDotNetFromJS(0, 'Microsoft.JSInterop', 'DotNetDispatcher.EndInvoke', null, JSON.stringify([asyncHandle, true, result], argReplacer)), - error => getRequiredDispatcher().beginInvokeDotNetFromJS(0, 'Microsoft.JSInterop', 'DotNetDispatcher.EndInvoke', null, JSON.stringify([asyncHandle, false, formatError(error)])) + result => getRequiredDispatcher().endInvokeJSFromDotNet(asyncHandle, true, JSON.stringify([asyncHandle, true, result], argReplacer)), + error => getRequiredDispatcher().endInvokeJSFromDotNet(asyncHandle, false, JSON.stringify([asyncHandle, false, formatError(error)])) ); } }, @@ -219,7 +228,7 @@ module DotNet { return error ? error.toString() : 'null'; } } - + function findJSFunction(identifier: string): Function { if (cachedJSFunctions.hasOwnProperty(identifier)) { return cachedJSFunctions[identifier]; @@ -247,7 +256,7 @@ module DotNet { } } - class DotNetObject { + class DotNetObject { constructor(private _id: number) { } @@ -268,14 +277,14 @@ module DotNet { } public serializeAsArg() { - return {__dotNetObject: this._id}; + return { __dotNetObject: this._id }; } } const dotNetObjectRefKey = '__dotNetObject'; attachReviver(function reviveDotNetObject(key: any, value: any) { if (value && typeof value === 'object' && value.hasOwnProperty(dotNetObjectRefKey)) { - return new DotNetObject(value.__dotNetObject); + return new DotNetObject(value.__dotNetObject); } // Unrecognized - let another reviver handle it diff --git a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs index ad95b7342b09..125a555c9d2d 100644 --- a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs +++ b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs @@ -6,8 +6,7 @@ namespace Microsoft.JSInterop public static partial class DotNetDispatcher { public static void BeginInvoke(string callId, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { } - [Microsoft.JSInterop.JSInvokableAttribute("DotNetDispatcher.EndInvoke")] - public static void EndInvoke(long asyncHandle, bool succeeded, Microsoft.JSInterop.Internal.JSAsyncCallResult result) { } + public static void EndInvoke(string arguments) { } public static string Invoke(string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { throw null; } [Microsoft.JSInterop.JSInvokableAttribute("DotNetDispatcher.ReleaseDotNetObject")] public static void ReleaseDotNetObject(long dotNetObjectId) { } @@ -62,16 +61,8 @@ public abstract partial class JSRuntimeBase : Microsoft.JSInterop.IJSRuntime protected JSRuntimeBase() { } protected System.TimeSpan? DefaultAsyncTimeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } protected abstract void BeginInvokeJS(long taskId, string identifier, string argsJson); + protected internal abstract void EndInvokeDotNet(string callId, bool success, object resultOrError, string assemblyName, string methodIdentifier, long dotNetObjectId); public System.Threading.Tasks.Task InvokeAsync(string identifier, System.Collections.Generic.IEnumerable args, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public System.Threading.Tasks.Task InvokeAsync(string identifier, params object[] args) { throw null; } - protected virtual object OnDotNetInvocationException(System.Exception exception, string assemblyName, string methodIdentifier) { throw null; } - } -} -namespace Microsoft.JSInterop.Internal -{ - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] - public sealed partial class JSAsyncCallResult - { - internal JSAsyncCallResult() { } } } diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs index 952a6061aba3..60b6ad9ab01e 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs @@ -8,9 +8,7 @@ using System.Reflection; using System.Runtime.ExceptionServices; using System.Text.Json; -using System.Text.Json.Serialization; using System.Threading.Tasks; -using Microsoft.JSInterop.Internal; namespace Microsoft.JSInterop { @@ -20,6 +18,7 @@ namespace Microsoft.JSInterop public static class DotNetDispatcher { internal const string DotNetObjectRefKey = nameof(DotNetObjectRef.__dotNetObject); + private static readonly Type[] EndInvokeParameterTypes = new Type[] { typeof(long), typeof(bool), typeof(JSAsyncCallResult) }; private static readonly ConcurrentDictionary> _cachedMethodsByAssembly = new ConcurrentDictionary>(); @@ -104,7 +103,7 @@ public static void BeginInvoke(string callId, string assemblyName, string method else if (syncException != null) { // Threw synchronously, let's respond. - jsRuntimeBaseInstance.EndInvokeDotNet(callId, false, syncException, assemblyName, methodIdentifier); + jsRuntimeBaseInstance.EndInvokeDotNet(callId, false, syncException, assemblyName, methodIdentifier, dotNetObjectId); } else if (syncResult is Task task) { @@ -116,16 +115,16 @@ public static void BeginInvoke(string callId, string assemblyName, string method { var exception = t.Exception.GetBaseException(); - jsRuntimeBaseInstance.EndInvokeDotNet(callId, false, ExceptionDispatchInfo.Capture(exception), assemblyName, methodIdentifier); + jsRuntimeBaseInstance.EndInvokeDotNet(callId, false, ExceptionDispatchInfo.Capture(exception), assemblyName, methodIdentifier, dotNetObjectId); } var result = TaskGenericsUtil.GetTaskResult(task); - jsRuntimeBaseInstance.EndInvokeDotNet(callId, true, result, assemblyName, methodIdentifier); + jsRuntimeBaseInstance.EndInvokeDotNet(callId, true, result, assemblyName, methodIdentifier, dotNetObjectId); }, TaskScheduler.Current); } else { - jsRuntimeBaseInstance.EndInvokeDotNet(callId, true, syncResult, assemblyName, methodIdentifier); + jsRuntimeBaseInstance.EndInvokeDotNet(callId, true, syncResult, assemblyName, methodIdentifier, dotNetObjectId); } } @@ -248,11 +247,31 @@ static bool IsIncorrectDotNetObjectRefUse(JsonElement item, Type parameterType) /// Receives notification that a call from .NET to JS has finished, marking the /// associated as completed. /// - /// The identifier for the function invocation. - /// A flag to indicate whether the invocation succeeded. - /// If is true, specifies the invocation result. If is false, gives the corresponding to the invocation failure. - [JSInvokable(nameof(DotNetDispatcher) + "." + nameof(EndInvoke))] - public static void EndInvoke(long asyncHandle, bool succeeded, JSAsyncCallResult result) + /// + /// All exceptions from are caught + /// are delivered via JS interop to the JavaScript side when it requests confirmation, as + /// the mechanism to call relies on + /// using JS->.NET interop. This overload is meant for directly triggering completion callbacks + /// for .NET -> JS operations without going through JS interop, so the callsite for this + /// method is responsible for handling any possible exception generated from the arguments + /// passed in as parameters. + /// + /// The serialized arguments for the callback completion. + /// + /// This method can throw any exception either from the argument received or as a result + /// of executing any callback synchronously upon completion. + /// + public static void EndInvoke(string arguments) + { + var parsedArgs = ParseArguments( + nameof(EndInvoke), + arguments, + EndInvokeParameterTypes); + + EndInvoke((long)parsedArgs[0], (bool)parsedArgs[1], (JSAsyncCallResult)parsedArgs[2]); + } + + private static void EndInvoke(long asyncHandle, bool succeeded, JSAsyncCallResult result) => ((JSRuntimeBase)JSRuntime.Current).EndInvokeJS(asyncHandle, succeeded, result); /// diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSAsyncCallResult.cs b/src/JSInterop/Microsoft.JSInterop/src/JSAsyncCallResult.cs index 1ea8c47995e5..209438529b82 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/JSAsyncCallResult.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/JSAsyncCallResult.cs @@ -1,10 +1,9 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System.ComponentModel; using System.Text.Json; -namespace Microsoft.JSInterop.Internal +namespace Microsoft.JSInterop { // This type takes care of a special case in handling the result of an async call from // .NET to JS. The information about what type the result should be exists only on the @@ -23,8 +22,7 @@ namespace Microsoft.JSInterop.Internal /// /// Intended for framework use only. /// - [EditorBrowsable(EditorBrowsableState.Never)] - public sealed class JSAsyncCallResult + internal sealed class JSAsyncCallResult { internal JSAsyncCallResult(JsonDocument document, JsonElement jsonElement) { diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs b/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs index 562a6a27bab1..13a9ccae1c75 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs @@ -9,7 +9,6 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Microsoft.JSInterop.Internal; namespace Microsoft.JSInterop { @@ -124,37 +123,21 @@ private async Task InvokeWithDefaultCancellation(string identifier, IEnume protected abstract void BeginInvokeJS(long taskId, string identifier, string argsJson); /// - /// Allows derived classes to configure the information about an exception in a JS interop call that gets sent to JavaScript. + /// Completes an async JS interop call from JavaScript to .NET /// - /// - /// This callback can be used in remote JS interop scenarios to sanitize exceptions that happen on the server to avoid disclosing - /// sensitive information to remote browser clients. - /// - /// The exception that occurred. - /// The assembly for the invoked .NET method. - /// The identifier for the invoked .NET method. - /// An object containing information about the exception. - protected virtual object OnDotNetInvocationException(Exception exception, string assemblyName, string methodIdentifier) => exception.ToString(); - - internal void EndInvokeDotNet(string callId, bool success, object resultOrException, string assemblyName, string methodIdentifier) - { - // For failures, the common case is to call EndInvokeDotNet with the Exception object. - // For these we'll serialize as something that's useful to receive on the JS side. - // If the value is not an Exception, we'll just rely on it being directly JSON-serializable. - if (!success && resultOrException is Exception ex) - { - resultOrException = OnDotNetInvocationException(ex, assemblyName, methodIdentifier); - } - else if (!success && resultOrException is ExceptionDispatchInfo edi) - { - resultOrException = OnDotNetInvocationException(edi.SourceException, assemblyName, methodIdentifier); - } - - // We pass 0 as the async handle because we don't want the JS-side code to - // send back any notification (we're just providing a result for an existing async call) - var args = JsonSerializer.Serialize(new[] { callId, success, resultOrException }, JsonSerializerOptionsProvider.Options); - BeginInvokeJS(0, "DotNet.jsCallDispatcher.endInvokeDotNetFromJS", args); - } + /// The id of the JavaScript callback to execute on completion. + /// Whether the operation succeeded or not. + /// The result of the operation or an object containing error details. + /// The name of the method assembly if the invocation was for a static method. + /// The identifier for the method within the assembly. + /// The tracking id of the dotnet object if the invocation was for an instance method. + protected internal abstract void EndInvokeDotNet( + string callId, + bool success, + object resultOrError, + string assemblyName, + string methodIdentifier, + long dotNetObjectId); internal void EndInvokeJS(long taskId, bool succeeded, JSAsyncCallResult asyncCallResult) { diff --git a/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs b/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs index 9e8a7b672e9a..a4a0de5a3f9b 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs @@ -2,7 +2,9 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.ComponentModel.DataAnnotations; using System.Linq; +using System.Runtime.ExceptionServices; using System.Text.Json; using System.Text.Json.Serialization; using System.Threading.Tasks; @@ -295,23 +297,18 @@ public Task CanInvokeAsyncMethod() => WithJSRuntime(async jsRuntime => var resultTask = jsRuntime.NextInvocationTask; DotNetDispatcher.BeginInvoke(callId, null, "InvokableAsyncMethod", 1, argsJson); await resultTask; - var result = JsonDocument.Parse(jsRuntime.LastInvocationArgsJson).RootElement; - var resultValue = result[2]; - // Assert: Correct info to complete the async call - Assert.Equal(0, jsRuntime.LastInvocationAsyncHandle); // 0 because it doesn't want a further callback from JS to .NET - Assert.Equal("DotNet.jsCallDispatcher.endInvokeDotNetFromJS", jsRuntime.LastInvocationIdentifier); - Assert.Equal(3, result.GetArrayLength()); - Assert.Equal(callId, result[0].GetString()); - Assert.True(result[1].GetBoolean()); // Success flag + // Assert: Correct completion information + Assert.Equal(callId, jsRuntime.LastCompletionCallId); + Assert.True(jsRuntime.LastCompletionStatus); + var result = Assert.IsType(jsRuntime.LastCompletionResult); + var resultDto1 = Assert.IsType(result[0]); - // Assert: First result value marshalled via JSON - var resultDto1 = JsonSerializer.Deserialize(resultValue[0].GetRawText(), JsonSerializerOptionsProvider.Options); Assert.Equal("STRING VIA JSON", resultDto1.StringVal); Assert.Equal(2000, resultDto1.IntVal); // Assert: Second result value marshalled by ref - var resultDto2Ref = JsonSerializer.Deserialize>(resultValue[1].GetRawText(), JsonSerializerOptionsProvider.Options); + var resultDto2Ref = Assert.IsType>(result[1]); var resultDto2 = resultDto2Ref.Value; Assert.Equal("MY STRING", resultDto2.StringVal); Assert.Equal(2468, resultDto2.IntVal); @@ -330,13 +327,12 @@ public Task CanInvokeSyncThrowingMethod() => WithJSRuntime(async jsRuntime => await resultTask; // This won't throw, it sets properties on the jsRuntime. // Assert - var result = JsonDocument.Parse(jsRuntime.LastInvocationArgsJson).RootElement; - Assert.Equal(callId, result[0].GetString()); - Assert.False(result[1].GetBoolean()); // Fails + Assert.Equal(callId, jsRuntime.LastCompletionCallId); + Assert.False(jsRuntime.LastCompletionStatus); // Fails // Make sure the method that threw the exception shows up in the call stack // https://github.com/aspnet/AspNetCore/issues/8612 - var exception = result[2].GetString(); + var exception = jsRuntime.LastCompletionResult is ExceptionDispatchInfo edi ? edi.SourceException.ToString() : null; Assert.Contains(nameof(ThrowingClass.ThrowingMethod), exception); }); @@ -353,13 +349,12 @@ public Task CanInvokeAsyncThrowingMethod() => WithJSRuntime(async jsRuntime => await resultTask; // This won't throw, it sets properties on the jsRuntime. // Assert - var result = JsonDocument.Parse(jsRuntime.LastInvocationArgsJson).RootElement; - Assert.Equal(callId, result[0].GetString()); - Assert.False(result[1].GetBoolean()); // Fails + Assert.Equal(callId, jsRuntime.LastCompletionCallId); + Assert.False(jsRuntime.LastCompletionStatus); // Fails // Make sure the method that threw the exception shows up in the call stack // https://github.com/aspnet/AspNetCore/issues/8612 - var exception = result[2].GetString(); + var exception = jsRuntime.LastCompletionResult is ExceptionDispatchInfo edi ? edi.SourceException.ToString() : null; Assert.Contains(nameof(ThrowingClass.AsyncThrowingMethod), exception); }); @@ -374,11 +369,10 @@ public Task BeginInvoke_ThrowsWithInvalidArgsJson_WithCallId() => WithJSRuntime( await resultTask; // This won't throw, it sets properties on the jsRuntime. // Assert - using var jsonDocument = JsonDocument.Parse(jsRuntime.LastInvocationArgsJson); - var result = jsonDocument.RootElement; - Assert.Equal(callId, result[0].GetString()); - Assert.False(result[1].GetBoolean()); // Fails - Assert.Contains("JsonReaderException: '<' is an invalid start of a value.", result[2].GetString()); + Assert.Equal(callId, jsRuntime.LastCompletionCallId); + Assert.False(jsRuntime.LastCompletionStatus); // Fails + var result = Assert.IsType(jsRuntime.LastCompletionResult); + Assert.Contains("JsonReaderException: '<' is an invalid start of a value.", result.SourceException.ToString()); }); [Fact] @@ -390,12 +384,10 @@ public Task BeginInvoke_ThrowsWithInvalid_DotNetObjectRef() => WithJSRuntime(jsR DotNetDispatcher.BeginInvoke(callId, null, "InvokableInstanceVoid", 1, null); // Assert - using var jsonDocument = JsonDocument.Parse(jsRuntime.LastInvocationArgsJson); - var result = jsonDocument.RootElement; - Assert.Equal(callId, result[0].GetString()); - Assert.False(result[1].GetBoolean()); // Fails - - Assert.StartsWith("System.ArgumentException: There is no tracked object with id '1'. Perhaps the DotNetObjectRef instance was already disposed.", result[2].GetString()); + Assert.Equal(callId, jsRuntime.LastCompletionCallId); + Assert.False(jsRuntime.LastCompletionStatus); // Fails + var result = Assert.IsType(jsRuntime.LastCompletionResult); + Assert.StartsWith("System.ArgumentException: There is no tracked object with id '1'. Perhaps the DotNetObjectRef instance was already disposed.", result.SourceException.ToString()); }); Task WithJSRuntime(Action testCode) @@ -556,6 +548,10 @@ public class TestJSRuntime : JSInProcessRuntimeBase public string LastInvocationIdentifier { get; private set; } public string LastInvocationArgsJson { get; private set; } + public string LastCompletionCallId { get; private set; } + public bool LastCompletionStatus { get; private set; } + public object LastCompletionResult { get; private set; } + protected override void BeginInvokeJS(long asyncHandle, string identifier, string argsJson) { LastInvocationAsyncHandle = asyncHandle; @@ -574,6 +570,21 @@ protected override string InvokeJS(string identifier, string argsJson) _nextInvocationTcs = new TaskCompletionSource(); return null; } + + protected internal override void EndInvokeDotNet( + string callId, + bool success, + object resultOrError, + string assemblyName, + string methodIdentifier, + long dotNetObjectId) + { + LastCompletionCallId = callId; + LastCompletionStatus = success; + LastCompletionResult = resultOrError; + _nextInvocationTcs.SetResult(null); + _nextInvocationTcs = new TaskCompletionSource(); + } } } } diff --git a/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectRefTest.cs b/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectRefTest.cs index 2b46831a16fb..77af280d940c 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectRefTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectRefTest.cs @@ -37,6 +37,11 @@ protected override void BeginInvokeJS(long asyncHandle, string identifier, strin { throw new NotImplementedException(); } + + protected internal override void EndInvokeDotNet(string callId, bool success, object resultOrError, string assemblyName, string methodIdentifier, long dotNetObjectId) + { + throw new NotImplementedException(); + } } async Task WithJSRuntime(Action testCode) diff --git a/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeBaseTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeBaseTest.cs index dda3f74184d4..a8d551e94e09 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeBaseTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeBaseTest.cs @@ -115,6 +115,9 @@ public class InvokeArgs protected override void BeginInvokeJS(long asyncHandle, string identifier, string argsJson) => throw new NotImplementedException("This test only covers sync calls"); + + protected internal override void EndInvokeDotNet(string callId, bool success, object resultOrError, string assemblyName, string methodIdentifier, long dotNetObjectId) => + throw new NotImplementedException("This test only covers sync calls"); } } } diff --git a/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs index 3ad78c303f8e..fb9227c99611 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs @@ -4,10 +4,10 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.ExceptionServices; using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Microsoft.JSInterop.Internal; using Xunit; namespace Microsoft.JSInterop.Tests @@ -79,7 +79,7 @@ public async Task InvokeAsync_CancelsAsyncTasksWhenCancellationTokenFires() var task = runtime.InvokeAsync("test identifier 1", new object[] { "arg1", 123, true }, cts.Token); cts.Cancel(); - + // Assert await Assert.ThrowsAsync(async () => await task); } @@ -248,13 +248,14 @@ public void CanSanitizeDotNetInteropExceptions() var exception = new Exception("Some really sensitive data in here"); // Act - runtime.EndInvokeDotNet("0", false, exception, "Assembly", "Method"); + runtime.EndInvokeDotNet("0", false, exception, "Assembly", "Method", 0); // Assert - var call = runtime.BeginInvokeCalls.Single(); - Assert.Equal(0, call.AsyncHandle); - Assert.Equal("DotNet.jsCallDispatcher.endInvokeDotNetFromJS", call.Identifier); - Assert.Equal($"[\"0\",false,{{\"message\":\"{expectedMessage.Replace("'", "\\u0027")}\"}}]", call.ArgsJson); + var call = runtime.EndInvokeDotNetCalls.Single(); + Assert.Equal("0", call.CallId); + Assert.False(call.Success); + var jsError = Assert.IsType(call.ResultOrError); + Assert.Equal(expectedMessage, jsError.Message); } private class JSError @@ -265,6 +266,7 @@ private class JSError class TestJSRuntime : JSRuntimeBase { public List BeginInvokeCalls = new List(); + public List EndInvokeDotNetCalls = new List(); public TimeSpan? DefaultTimeout { @@ -281,16 +283,28 @@ public class BeginInvokeAsyncArgs public string ArgsJson { get; set; } } + public class EndInvokeDotNetArgs + { + public string CallId { get; set; } + public bool Success { get; set; } + public object ResultOrError { get; set; } + } + public Func OnDotNetException { get; set; } - protected override object OnDotNetInvocationException(Exception exception, string assemblyName, string methodName) + protected internal override void EndInvokeDotNet(string callId, bool success, object resultOrError, string assemblyName, string methodIdentifier, long dotNetObjectId) { - if (OnDotNetException != null) + if (OnDotNetException != null && !success) { - return OnDotNetException(exception, assemblyName, methodName); + resultOrError = OnDotNetException(resultOrError as Exception, assemblyName, methodIdentifier); } - return base.OnDotNetInvocationException(exception, assemblyName, methodName); + EndInvokeDotNetCalls.Add(new EndInvokeDotNetArgs + { + CallId = callId, + Success = success, + ResultOrError = resultOrError + }); } protected override void BeginInvokeJS(long asyncHandle, string identifier, string argsJson) diff --git a/src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.netstandard2.0.cs b/src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.netstandard2.0.cs index 033e97237a0d..5299370576a3 100644 --- a/src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.netstandard2.0.cs +++ b/src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.netstandard2.0.cs @@ -7,6 +7,7 @@ public partial class MonoWebAssemblyJSRuntime : Microsoft.JSInterop.JSInProcessR { public MonoWebAssemblyJSRuntime() { } protected override void BeginInvokeJS(long asyncHandle, string identifier, string argsJson) { } + protected override void EndInvokeDotNet(string callId, bool success, object resultOrError, string assemblyName, string methodIdentifier, long dotNetObjectId) { } protected override string InvokeJS(string identifier, string argsJson) { throw null; } public TRes InvokeUnmarshalled(string identifier) { throw null; } public TRes InvokeUnmarshalled(string identifier, T0 arg0) { throw null; } diff --git a/src/JSInterop/Mono.WebAssembly.Interop/src/MonoWebAssemblyJSRuntime.cs b/src/JSInterop/Mono.WebAssembly.Interop/src/MonoWebAssemblyJSRuntime.cs index 9a502b8bc842..e65df172f8b9 100644 --- a/src/JSInterop/Mono.WebAssembly.Interop/src/MonoWebAssemblyJSRuntime.cs +++ b/src/JSInterop/Mono.WebAssembly.Interop/src/MonoWebAssemblyJSRuntime.cs @@ -1,6 +1,9 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; +using System.Runtime.ExceptionServices; +using System.Text.Json; using Microsoft.JSInterop; using WebAssembly.JSInterop; @@ -32,6 +35,10 @@ protected override void BeginInvokeJS(long asyncHandle, string identifier, strin private static string InvokeDotNet(string assemblyName, string methodIdentifier, string dotNetObjectId, string argsJson) => DotNetDispatcher.Invoke(assemblyName, methodIdentifier, dotNetObjectId == null ? default : long.Parse(dotNetObjectId), argsJson); + // Invoked via Mono's JS interop mechanism (invoke_method) + private static void EndInvokeJS(string argsJson) + => DotNetDispatcher.EndInvoke(argsJson); + // Invoked via Mono's JS interop mechanism (invoke_method) private static void BeginInvokeDotNet(string callId, string assemblyNameOrDotNetObjectId, string methodIdentifier, string argsJson) { @@ -54,6 +61,32 @@ private static void BeginInvokeDotNet(string callId, string assemblyNameOrDotNet DotNetDispatcher.BeginInvoke(callId, assemblyName, methodIdentifier, dotNetObjectId, argsJson); } + protected override void EndInvokeDotNet( + string callId, + bool success, + object resultOrError, + string assemblyName, + string methodIdentifier, + long dotNetObjectId) + { + // For failures, the common case is to call EndInvokeDotNet with the Exception object. + // For these we'll serialize as something that's useful to receive on the JS side. + // If the value is not an Exception, we'll just rely on it being directly JSON-serializable. + if (!success && resultOrError is Exception ex) + { + resultOrError = ex.ToString(); + } + else if (!success && resultOrError is ExceptionDispatchInfo edi) + { + resultOrError = edi.SourceException.ToString(); + } + + // We pass 0 as the async handle because we don't want the JS-side code to + // send back any notification (we're just providing a result for an existing async call) + var args = JsonSerializer.Serialize(new[] { callId, success, resultOrError }, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); + BeginInvokeJS(0, "DotNet.jsCallDispatcher.endInvokeDotNetFromJS", args); + } + #region Custom MonoWebAssemblyJSRuntime methods /// From 2d2400e7faf5c354c785b606e8a59afadbbb9c19 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Tue, 16 Jul 2019 15:21:28 -0700 Subject: [PATCH 115/183] Use JsonConverter for DotNetObjectRef \n\nCommit migrated from https://github.com/dotnet/extensions/commit/0389bf44d4833843801061c744599b26e32268d8 --- .../ref/Microsoft.JSInterop.netstandard2.0.cs | 6 +- .../src/DotNetDispatcher.cs | 4 +- .../src/DotNetObjectRef.cs | 3 +- .../DotNetObjectRefJsonConverterFactory.cs | 29 +++++++ .../src/DotNetObjectRefManager.cs | 6 +- .../src/DotNetObjectRefOfT.cs | 43 ++-------- .../src/DotNetObjectReferenceJsonConverter.cs | 51 ++++++++++++ .../src/IDotNetObjectRef.cs | 1 - .../Microsoft.JSInterop/test/Class1.cs | 27 +++++++ .../test/DotNetDispatcherTest.cs | 2 +- .../test/DotNetObjectRefTest.cs | 12 +-- .../DotNetObjectReferenceJsonConverterTest.cs | 78 +++++++++++++++++++ 12 files changed, 204 insertions(+), 58 deletions(-) create mode 100644 src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRefJsonConverterFactory.cs create mode 100644 src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceJsonConverter.cs create mode 100644 src/JSInterop/Microsoft.JSInterop/test/Class1.cs create mode 100644 src/JSInterop/Microsoft.JSInterop/test/DotNetObjectReferenceJsonConverterTest.cs diff --git a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs index 125a555c9d2d..654ae9d6173a 100644 --- a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs +++ b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs @@ -17,12 +17,8 @@ public static partial class DotNetObjectRef } public sealed partial class DotNetObjectRef : System.IDisposable where TValue : class { - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] - public DotNetObjectRef() { } - [System.Text.Json.Serialization.JsonIgnoreAttribute] + internal DotNetObjectRef() { } public TValue Value { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] - public long __dotNetObject { get { throw null; } set { } } public void Dispose() { } } public partial interface IJSInProcessRuntime : Microsoft.JSInterop.IJSRuntime diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs index 60b6ad9ab01e..3de8f558822f 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs @@ -17,7 +17,7 @@ namespace Microsoft.JSInterop /// public static class DotNetDispatcher { - internal const string DotNetObjectRefKey = nameof(DotNetObjectRef.__dotNetObject); + internal static readonly JsonEncodedText DotNetObjectRefKey = JsonEncodedText.Encode("__dotNetObject"); private static readonly Type[] EndInvokeParameterTypes = new Type[] { typeof(long), typeof(bool), typeof(JSAsyncCallResult) }; private static readonly ConcurrentDictionary> _cachedMethodsByAssembly @@ -238,7 +238,7 @@ static bool IsIncorrectDotNetObjectRefUse(JsonElement item, Type parameterType) // an incorrect use if there's a object that looks like { '__dotNetObject': }, // but we aren't assigning to DotNetObjectRef{T}. return item.ValueKind == JsonValueKind.Object && - item.TryGetProperty(DotNetObjectRefKey, out _) && + item.TryGetProperty(DotNetObjectRefKey.EncodedUtf8Bytes, out _) && !typeof(IDotNetObjectRef).IsAssignableFrom(parameterType); } } diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRef.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRef.cs index 1aabc5ad5949..af790281e9e6 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRef.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRef.cs @@ -15,7 +15,8 @@ public static class DotNetObjectRef /// An instance of . public static DotNetObjectRef Create(TValue value) where TValue : class { - return new DotNetObjectRef(value); + var objectId = DotNetObjectRefManager.Current.TrackObject(value); + return new DotNetObjectRef(objectId, value); } } } diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRefJsonConverterFactory.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRefJsonConverterFactory.cs new file mode 100644 index 000000000000..3bc1f83c7e16 --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRefJsonConverterFactory.cs @@ -0,0 +1,29 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Diagnostics; +using System.Text.Json.Serialization; + +namespace Microsoft.JSInterop +{ + internal sealed class DotNetObjectReferenceJsonConverterFactory : JsonConverterFactory + { + public override bool CanConvert(Type typeToConvert) + { + Debug.Assert( + typeToConvert.IsGenericType && typeToConvert.GetGenericTypeDefinition() == typeof(DotNetObjectRef<>), + "We expect this to only be called for DotNetObjectRef instances."); + return true; + } + + protected override JsonConverter CreateConverter(Type typeToConvert) + { + // System.Text.Json handles caching the converters per type on our behalf. No caching is required here. + var instanceType = typeToConvert.GetGenericArguments()[0]; + var converterType = typeof(DotNetObjectReferenceJsonConverter<>).MakeGenericType(instanceType); + + return (JsonConverter)Activator.CreateInstance(converterType); + } + } +} diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRefManager.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRefManager.cs index f263716f53f6..ad1469e38f27 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRefManager.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRefManager.cs @@ -10,7 +10,7 @@ namespace Microsoft.JSInterop internal class DotNetObjectRefManager { private long _nextId = 0; // 0 signals no object, but we increment prior to assignment. The first tracked object should have id 1 - private readonly ConcurrentDictionary _trackedRefsById = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _trackedRefsById = new ConcurrentDictionary(); public static DotNetObjectRefManager Current { @@ -25,7 +25,7 @@ public static DotNetObjectRefManager Current } } - public long TrackObject(IDotNetObjectRef dotNetObjectRef) + public long TrackObject(object dotNetObjectRef) { var dotNetObjectId = Interlocked.Increment(ref _nextId); _trackedRefsById[dotNetObjectId] = dotNetObjectRef; @@ -36,7 +36,7 @@ public long TrackObject(IDotNetObjectRef dotNetObjectRef) public object FindDotNetObject(long dotNetObjectId) { return _trackedRefsById.TryGetValue(dotNetObjectId, out var dotNetObjectRef) - ? dotNetObjectRef.Value + ? dotNetObjectRef : throw new ArgumentException($"There is no tracked object with id '{dotNetObjectId}'. Perhaps the DotNetObjectRef instance was already disposed.", nameof(dotNetObjectId)); } diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRefOfT.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRefOfT.cs index 8b7035e957cf..be6bf9166365 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRefOfT.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRefOfT.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.ComponentModel; using System.Text.Json.Serialization; namespace Microsoft.JSInterop @@ -14,54 +13,26 @@ namespace Microsoft.JSInterop /// To avoid leaking memory, the reference must later be disposed by JS code or by .NET code. /// /// The type of the value to wrap. + [JsonConverter(typeof(DotNetObjectReferenceJsonConverterFactory))] public sealed class DotNetObjectRef : IDotNetObjectRef, IDisposable where TValue : class { - private long? _trackingId; - - /// - /// This API is for meant for JSON deserialization and should not be used by user code. - /// - [EditorBrowsable(EditorBrowsableState.Never)] - public DotNetObjectRef() - { - } - /// /// Initializes a new instance of . /// + /// The object Id. /// The value to pass by reference. - internal DotNetObjectRef(TValue value) + internal DotNetObjectRef(long objectId, TValue value) { + ObjectId = objectId; Value = value; - _trackingId = DotNetObjectRefManager.Current.TrackObject(this); } /// /// Gets the object instance represented by this wrapper. /// - [JsonIgnore] - public TValue Value { get; private set; } - - /// - /// This API is for meant for JSON serialization and should not be used by user code. - /// - [EditorBrowsable(EditorBrowsableState.Never)] - public long __dotNetObject - { - get => _trackingId.Value; - set - { - if (_trackingId != null) - { - throw new InvalidOperationException($"{nameof(DotNetObjectRef)} cannot be reinitialized."); - } - - _trackingId = value; - Value = (TValue)DotNetObjectRefManager.Current.FindDotNetObject(value); - } - } + public TValue Value { get; } - object IDotNetObjectRef.Value => Value; + internal long ObjectId { get; } /// /// Stops tracking this object reference, allowing it to be garbage collected @@ -70,7 +41,7 @@ public long __dotNetObject /// public void Dispose() { - DotNetObjectRefManager.Current.ReleaseDotNetObject(_trackingId.Value); + DotNetObjectRefManager.Current.ReleaseDotNetObject(ObjectId); } } } diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceJsonConverter.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceJsonConverter.cs new file mode 100644 index 000000000000..d07735734e5a --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceJsonConverter.cs @@ -0,0 +1,51 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Microsoft.JSInterop +{ + internal sealed class DotNetObjectReferenceJsonConverter : JsonConverter> where TValue : class + { + private static JsonEncodedText DotNetObjectRefKey => DotNetDispatcher.DotNetObjectRefKey; + + public override DotNetObjectRef Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (!reader.Read()) + { + throw new InvalidDataException("Invalid DotNetObjectRef JSON."); + } + + if (reader.TokenType != JsonTokenType.PropertyName || !reader.ValueTextEquals(DotNetObjectRefKey.EncodedUtf8Bytes)) + { + throw new InvalidDataException("Invalid DotNetObjectRef JSON."); + } + + if (!reader.Read()) + { + throw new InvalidDataException("Invalid DotNetObjectRef JSON."); + } + + var dotNetObjectId = reader.GetInt64(); + + if (!reader.Read()) + { + // We need to read all the data that was given to us. + throw new InvalidDataException("Invalid DotNetObjectRef JSON."); + } + + var value = (TValue)DotNetObjectRefManager.Current.FindDotNetObject(dotNetObjectId); + return new DotNetObjectRef(dotNetObjectId, value); + } + + public override void Write(Utf8JsonWriter writer, DotNetObjectRef value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + writer.WriteNumber(DotNetObjectRefKey, value.ObjectId); + writer.WriteEndObject(); + } + } +} diff --git a/src/JSInterop/Microsoft.JSInterop/src/IDotNetObjectRef.cs b/src/JSInterop/Microsoft.JSInterop/src/IDotNetObjectRef.cs index 5f21808a9f24..b082d0ce105a 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/IDotNetObjectRef.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/IDotNetObjectRef.cs @@ -7,6 +7,5 @@ namespace Microsoft.JSInterop { internal interface IDotNetObjectRef : IDisposable { - public object Value { get; } } } diff --git a/src/JSInterop/Microsoft.JSInterop/test/Class1.cs b/src/JSInterop/Microsoft.JSInterop/test/Class1.cs new file mode 100644 index 000000000000..1e58139b369e --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop/test/Class1.cs @@ -0,0 +1,27 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Threading.Tasks; + +namespace Microsoft.JSInterop +{ + internal class TestJSRuntime : JSRuntimeBase + { + protected override void BeginInvokeJS(long asyncHandle, string identifier, string argsJson) + { + throw new NotImplementedException(); + } + + public static async Task WithJSRuntime(Action testCode) + { + // Since the tests rely on the asynclocal JSRuntime.Current, ensure we + // are on a distinct async context with a non-null JSRuntime.Current + await Task.Yield(); + + var runtime = new TestJSRuntime(); + JSRuntime.SetCurrentJSRuntime(runtime); + testCode(runtime); + } + } +} diff --git a/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs b/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs index a4a0de5a3f9b..1c73cc63b25e 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs @@ -142,7 +142,7 @@ public Task CanInvokeStaticWithParams() => WithJSRuntime(jsRuntime => Assert.False(resultDto2Ref.TryGetProperty(nameof(TestDTO.StringVal), out _)); Assert.False(resultDto2Ref.TryGetProperty(nameof(TestDTO.IntVal), out _)); - Assert.True(resultDto2Ref.TryGetProperty(DotNetDispatcher.DotNetObjectRefKey, out var property)); + Assert.True(resultDto2Ref.TryGetProperty(DotNetDispatcher.DotNetObjectRefKey.EncodedUtf8Bytes, out var property)); var resultDto2 = Assert.IsType(DotNetObjectRefManager.Current.FindDotNetObject(property.GetInt64())); Assert.Equal("MY STRING", resultDto2.StringVal); Assert.Equal(1299, resultDto2.IntVal); diff --git a/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectRefTest.cs b/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectRefTest.cs index 77af280d940c..f41798574668 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectRefTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectRefTest.cs @@ -4,8 +4,9 @@ using System; using System.Threading.Tasks; using Xunit; +using static Microsoft.JSInterop.TestJSRuntime; -namespace Microsoft.JSInterop.Tests +namespace Microsoft.JSInterop { public class DotNetObjectRefTest { @@ -21,21 +22,14 @@ public Task NotifiesAssociatedJsRuntimeOfDisposal() => WithJSRuntime(jsRuntime = { // Arrange var objRef = DotNetObjectRef.Create(new object()); - var trackingId = objRef.__dotNetObject; // Act objRef.Dispose(); // Assert - var ex = Assert.Throws(() => jsRuntime.ObjectRefManager.FindDotNetObject(trackingId)); + var ex = Assert.Throws(() => jsRuntime.ObjectRefManager.FindDotNetObject(objRef.ObjectId)); Assert.StartsWith("There is no tracked object with id '1'.", ex.Message); }); - - class TestJSRuntime : JSRuntimeBase - { - protected override void BeginInvokeJS(long asyncHandle, string identifier, string argsJson) - { - throw new NotImplementedException(); } protected internal override void EndInvokeDotNet(string callId, bool success, object resultOrError, string assemblyName, string methodIdentifier, long dotNetObjectId) diff --git a/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectReferenceJsonConverterTest.cs b/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectReferenceJsonConverterTest.cs new file mode 100644 index 000000000000..c2dbd8889554 --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectReferenceJsonConverterTest.cs @@ -0,0 +1,78 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Text.Json; +using System.Threading.Tasks; +using Xunit; +using static Microsoft.JSInterop.TestJSRuntime; + +namespace Microsoft.JSInterop.Tests +{ + public class DotNetObjectReferenceJsonConverterTest + { + [Fact] + public Task Read_ReadsJson() => WithJSRuntime(_ => + { + // Arrange + // Throw-away value + DotNetObjectRef.Create(new TestModel()); + var input = new TestModel(); + var dotNetObjectRef = DotNetObjectRef.Create(input); + var objectId = dotNetObjectRef.ObjectId; + + var json = $"{{\"__dotNetObject\":{objectId}}}"; + + // Act + var deserialized = JsonSerializer.Deserialize>(json); + + // Assert + Assert.Same(input, deserialized.Value); + Assert.Equal(objectId, deserialized.ObjectId); + }); + + [Fact] + public Task Read_ReadsJson_WithFormatting() => WithJSRuntime(_ => + { + // Arrange + // Throw-away value + DotNetObjectRef.Create(new TestModel()); + var input = new TestModel(); + var dotNetObjectRef = DotNetObjectRef.Create(input); + var objectId = dotNetObjectRef.ObjectId; + + var json = +@$"{{ + ""__dotNetObject"": {objectId} +}}"; + + // Act + var deserialized = JsonSerializer.Deserialize>(json); + + // Assert + Assert.Same(input, deserialized.Value); + Assert.Equal(objectId, deserialized.ObjectId); + }); + + [Fact] + public Task WriteJsonTwice_KeepsObjectId() => WithJSRuntime(_ => + { + // Arrange + // Throw-away value + DotNetObjectRef.Create(new TestModel()); + var dotNetObjectRef = DotNetObjectRef.Create(new TestModel()); + + // Act + var json1 = JsonSerializer.Serialize(dotNetObjectRef); + var json2 = JsonSerializer.Serialize(dotNetObjectRef); + + // Assert + Assert.Equal($"{{\"__dotNetObject\":{dotNetObjectRef.ObjectId}}}", json1); + Assert.Equal(json1, json2); + }); + + private class TestModel + { + + } + } +} From 950b3873b138a7f27bc2d41200319785bda093df Mon Sep 17 00:00:00 2001 From: Pranav K Date: Tue, 16 Jul 2019 16:15:48 -0700 Subject: [PATCH 116/183] PR comments \n\nCommit migrated from https://github.com/dotnet/extensions/commit/791c96b143d3f4e1e0b507cd0283cdf09ac0adb6 --- .../DotNetObjectRefJsonConverterFactory.cs | 9 ++-- .../src/DotNetObjectReferenceJsonConverter.cs | 37 +++++++------ .../test/DotNetObjectRefTest.cs | 18 ------- .../DotNetObjectReferenceJsonConverterTest.cs | 52 +++++++++++++++++-- .../test/{Class1.cs => TestJSRuntime.cs} | 5 ++ 5 files changed, 75 insertions(+), 46 deletions(-) rename src/JSInterop/Microsoft.JSInterop/test/{Class1.cs => TestJSRuntime.cs} (78%) diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRefJsonConverterFactory.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRefJsonConverterFactory.cs index 3bc1f83c7e16..5cfec5be9dce 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRefJsonConverterFactory.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRefJsonConverterFactory.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Diagnostics; +using System.Text.Json; using System.Text.Json.Serialization; namespace Microsoft.JSInterop @@ -11,13 +11,10 @@ internal sealed class DotNetObjectReferenceJsonConverterFactory : JsonConverterF { public override bool CanConvert(Type typeToConvert) { - Debug.Assert( - typeToConvert.IsGenericType && typeToConvert.GetGenericTypeDefinition() == typeof(DotNetObjectRef<>), - "We expect this to only be called for DotNetObjectRef instances."); - return true; + return typeToConvert.IsGenericType && typeToConvert.GetGenericTypeDefinition() == typeof(DotNetObjectRef<>); } - protected override JsonConverter CreateConverter(Type typeToConvert) + public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions jsonSerializerOptions) { // System.Text.Json handles caching the converters per type on our behalf. No caching is required here. var instanceType = typeToConvert.GetGenericArguments()[0]; diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceJsonConverter.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceJsonConverter.cs index d07735734e5a..487b4c77b0ae 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceJsonConverter.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceJsonConverter.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.IO; using System.Text.Json; using System.Text.Json.Serialization; @@ -14,27 +13,31 @@ internal sealed class DotNetObjectReferenceJsonConverter : JsonConverter public override DotNetObjectRef Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - if (!reader.Read()) - { - throw new InvalidDataException("Invalid DotNetObjectRef JSON."); - } - - if (reader.TokenType != JsonTokenType.PropertyName || !reader.ValueTextEquals(DotNetObjectRefKey.EncodedUtf8Bytes)) - { - throw new InvalidDataException("Invalid DotNetObjectRef JSON."); - } + long dotNetObjectId = 0; - if (!reader.Read()) + while (reader.Read() && reader.TokenType != JsonTokenType.EndObject) { - throw new InvalidDataException("Invalid DotNetObjectRef JSON."); + if (reader.TokenType == JsonTokenType.PropertyName) + { + if (reader.ValueTextEquals(DotNetObjectRefKey.EncodedUtf8Bytes)) + { + reader.Read(); + dotNetObjectId = reader.GetInt64(); + } + else + { + throw new JsonException($"Unexcepted JSON property {reader.GetString()}."); + } + } + else + { + throw new JsonException($"Unexcepted JSON Token {reader.TokenType}."); + } } - var dotNetObjectId = reader.GetInt64(); - - if (!reader.Read()) + if (dotNetObjectId is 0) { - // We need to read all the data that was given to us. - throw new InvalidDataException("Invalid DotNetObjectRef JSON."); + throw new JsonException($"Required property {DotNetObjectRefKey} not found."); } var value = (TValue)DotNetObjectRefManager.Current.FindDotNetObject(dotNetObjectId); diff --git a/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectRefTest.cs b/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectRefTest.cs index f41798574668..112363869eb7 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectRefTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectRefTest.cs @@ -30,23 +30,5 @@ public Task NotifiesAssociatedJsRuntimeOfDisposal() => WithJSRuntime(jsRuntime = var ex = Assert.Throws(() => jsRuntime.ObjectRefManager.FindDotNetObject(objRef.ObjectId)); Assert.StartsWith("There is no tracked object with id '1'.", ex.Message); }); - } - - protected internal override void EndInvokeDotNet(string callId, bool success, object resultOrError, string assemblyName, string methodIdentifier, long dotNetObjectId) - { - throw new NotImplementedException(); - } - } - - async Task WithJSRuntime(Action testCode) - { - // Since the tests rely on the asynclocal JSRuntime.Current, ensure we - // are on a distinct async context with a non-null JSRuntime.Current - await Task.Yield(); - - var runtime = new TestJSRuntime(); - JSRuntime.SetCurrentJSRuntime(runtime); - testCode(runtime); - } } } diff --git a/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectReferenceJsonConverterTest.cs b/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectReferenceJsonConverterTest.cs index c2dbd8889554..be1d2bf975f6 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectReferenceJsonConverterTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectReferenceJsonConverterTest.cs @@ -10,12 +10,58 @@ namespace Microsoft.JSInterop.Tests { public class DotNetObjectReferenceJsonConverterTest { + [Fact] + public Task Read_Throws_IfJsonIsMissingDotNetObjectProperty() => WithJSRuntime(_ => + { + // Arrange + var dotNetObjectRef = DotNetObjectRef.Create(new TestModel()); + + var json = "{}"; + + // Act & Assert + var ex = Assert.Throws(() => JsonSerializer.Deserialize>(json)); + Assert.Equal("Required property __dotNetObject not found.", ex.Message); + }); + + [Fact] + public Task Read_Throws_IfJsonContainsUnknownContent() => WithJSRuntime(_ => + { + // Arrange + var dotNetObjectRef = DotNetObjectRef.Create(new TestModel()); + + var json = "{\"foo\":2}"; + + // Act & Assert + var ex = Assert.Throws(() => JsonSerializer.Deserialize>(json)); + Assert.Equal("Unexcepted JSON property foo.", ex.Message); + }); + [Fact] public Task Read_ReadsJson() => WithJSRuntime(_ => { // Arrange - // Throw-away value + var input = new TestModel(); + var dotNetObjectRef = DotNetObjectRef.Create(input); + var objectId = dotNetObjectRef.ObjectId; + + var json = $"{{\"__dotNetObject\":{objectId}}}"; + + // Act + var deserialized = JsonSerializer.Deserialize>(json); + + // Assert + Assert.Same(input, deserialized.Value); + Assert.Equal(objectId, deserialized.ObjectId); + }); + + [Fact] + public Task Read_ReturnsTheCorrectInstance() => WithJSRuntime(_ => + { + // Arrange + // Track a few instances and verify that the deserialized value returns the corect value. DotNetObjectRef.Create(new TestModel()); + DotNetObjectRef.Create(new TestModel()); + var input = new TestModel(); var dotNetObjectRef = DotNetObjectRef.Create(input); var objectId = dotNetObjectRef.ObjectId; @@ -34,8 +80,6 @@ public Task Read_ReadsJson() => WithJSRuntime(_ => public Task Read_ReadsJson_WithFormatting() => WithJSRuntime(_ => { // Arrange - // Throw-away value - DotNetObjectRef.Create(new TestModel()); var input = new TestModel(); var dotNetObjectRef = DotNetObjectRef.Create(input); var objectId = dotNetObjectRef.ObjectId; @@ -57,8 +101,6 @@ public Task Read_ReadsJson_WithFormatting() => WithJSRuntime(_ => public Task WriteJsonTwice_KeepsObjectId() => WithJSRuntime(_ => { // Arrange - // Throw-away value - DotNetObjectRef.Create(new TestModel()); var dotNetObjectRef = DotNetObjectRef.Create(new TestModel()); // Act diff --git a/src/JSInterop/Microsoft.JSInterop/test/Class1.cs b/src/JSInterop/Microsoft.JSInterop/test/TestJSRuntime.cs similarity index 78% rename from src/JSInterop/Microsoft.JSInterop/test/Class1.cs rename to src/JSInterop/Microsoft.JSInterop/test/TestJSRuntime.cs index 1e58139b369e..c4e6b05c5ba1 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/Class1.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/TestJSRuntime.cs @@ -13,6 +13,11 @@ protected override void BeginInvokeJS(long asyncHandle, string identifier, strin throw new NotImplementedException(); } + protected internal override void EndInvokeDotNet(string callId, bool success, object resultOrError, string assemblyName, string methodIdentifier, long dotNetObjectId) + { + throw new NotImplementedException(); + } + public static async Task WithJSRuntime(Action testCode) { // Since the tests rely on the asynclocal JSRuntime.Current, ensure we From 39c052b8bce2628a430697f4fb16d4207872ef80 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Tue, 23 Jul 2019 19:36:16 -0700 Subject: [PATCH 117/183] More \n\nCommit migrated from https://github.com/dotnet/extensions/commit/0cf3c01afc54980d5d49d31b233b01eafd60e104 --- .../src/DotNetObjectReferenceJsonConverter.cs | 2 +- .../DotNetObjectReferenceJsonConverterTest.cs | 51 +++++++++++++++---- 2 files changed, 41 insertions(+), 12 deletions(-) diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceJsonConverter.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceJsonConverter.cs index 487b4c77b0ae..eaabdbf9e6e7 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceJsonConverter.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceJsonConverter.cs @@ -19,7 +19,7 @@ public override DotNetObjectRef Read(ref Utf8JsonReader reader, Type typ { if (reader.TokenType == JsonTokenType.PropertyName) { - if (reader.ValueTextEquals(DotNetObjectRefKey.EncodedUtf8Bytes)) + if (dotNetObjectId == 0 && reader.ValueTextEquals(DotNetObjectRefKey.EncodedUtf8Bytes)) { reader.Read(); dotNetObjectId = reader.GetInt64(); diff --git a/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectReferenceJsonConverterTest.cs b/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectReferenceJsonConverterTest.cs index be1d2bf975f6..18f3db55c107 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectReferenceJsonConverterTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectReferenceJsonConverterTest.cs @@ -36,6 +36,36 @@ public Task Read_Throws_IfJsonContainsUnknownContent() => WithJSRuntime(_ => Assert.Equal("Unexcepted JSON property foo.", ex.Message); }); + [Fact] + public Task Read_Throws_IfJsonIsIncomplete() => WithJSRuntime(_ => + { + // Arrange + var input = new TestModel(); + var dotNetObjectRef = DotNetObjectRef.Create(input); + var objectId = dotNetObjectRef.ObjectId; + + var json = $"{{\"__dotNetObject\":{objectId}"; + + // Act & Assert + var ex = Record.Exception(() => JsonSerializer.Deserialize>(json)); + Assert.IsAssignableFrom(ex); + }); + + [Fact] + public Task Read_Throws_IfDotNetObjectIdAppearsMultipleTimes() => WithJSRuntime(_ => + { + // Arrange + var input = new TestModel(); + var dotNetObjectRef = DotNetObjectRef.Create(input); + var objectId = dotNetObjectRef.ObjectId; + + var json = $"{{\"__dotNetObject\":{objectId},\"__dotNetObject\":{objectId}}}"; + + // Act & Assert + var ex = Record.Exception(() => JsonSerializer.Deserialize>(json)); + Assert.IsAssignableFrom(ex); + }); + [Fact] public Task Read_ReadsJson() => WithJSRuntime(_ => { @@ -54,26 +84,25 @@ public Task Read_ReadsJson() => WithJSRuntime(_ => Assert.Equal(objectId, deserialized.ObjectId); }); + [Fact] public Task Read_ReturnsTheCorrectInstance() => WithJSRuntime(_ => { // Arrange - // Track a few instances and verify that the deserialized value returns the corect value. - DotNetObjectRef.Create(new TestModel()); - DotNetObjectRef.Create(new TestModel()); + // Track a few instances and verify that the deserialized value returns the correct value. + var instance1 = new TestModel(); + var instance2 = new TestModel(); + var ref1 = DotNetObjectRef.Create(instance1); + var ref2 = DotNetObjectRef.Create(instance2); - var input = new TestModel(); - var dotNetObjectRef = DotNetObjectRef.Create(input); - var objectId = dotNetObjectRef.ObjectId; - - var json = $"{{\"__dotNetObject\":{objectId}}}"; + var json = $"[{{\"__dotNetObject\":{ref2.ObjectId}}},{{\"__dotNetObject\":{ref1.ObjectId}}}]"; // Act - var deserialized = JsonSerializer.Deserialize>(json); + var deserialized = JsonSerializer.Deserialize[]>(json); // Assert - Assert.Same(input, deserialized.Value); - Assert.Equal(objectId, deserialized.ObjectId); + Assert.Same(instance2, deserialized[0].Value); + Assert.Same(instance1, deserialized[1].Value); }); [Fact] From 557bd8e01116d100d7e86a541f7e0c0ebdc1e8e3 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Wed, 7 Aug 2019 09:44:42 -0700 Subject: [PATCH 118/183] Use Utf8JsonReader in DotNetDispatcher (dotnet/extensions#2061) * Use Utf8JsonReader in DotNetDispatcher Fixes https://github.com/aspnet/AspNetCore/issues/10988 \n\nCommit migrated from https://github.com/dotnet/extensions/commit/c24711c84defc8dfbd496280c2973eab0e88cbcf --- .../src/DotNetDispatcher.cs | 147 +++++----- .../src/JSAsyncCallResult.cs | 36 --- .../Microsoft.JSInterop/src/JSRuntimeBase.cs | 41 ++- .../test/DotNetDispatcherTest.cs | 251 +++++++++++++++++- .../test/JSRuntimeBaseTest.cs | 118 ++++++-- 5 files changed, 430 insertions(+), 163 deletions(-) delete mode 100644 src/JSInterop/Microsoft.JSInterop/src/JSAsyncCallResult.cs diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs index 3de8f558822f..0dfac228a684 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Reflection; using System.Runtime.ExceptionServices; +using System.Text; using System.Text.Json; using System.Threading.Tasks; @@ -18,7 +19,6 @@ namespace Microsoft.JSInterop public static class DotNetDispatcher { internal static readonly JsonEncodedText DotNetObjectRefKey = JsonEncodedText.Encode("__dotNetObject"); - private static readonly Type[] EndInvokeParameterTypes = new Type[] { typeof(long), typeof(bool), typeof(JSAsyncCallResult) }; private static readonly ConcurrentDictionary> _cachedMethodsByAssembly = new ConcurrentDictionary>(); @@ -74,7 +74,6 @@ public static void BeginInvoke(string callId, string assemblyName, string method // code has to implement its own way of returning async results. var jsRuntimeBaseInstance = (JSRuntimeBase)JSRuntime.Current; - // Using ExceptionDispatchInfo here throughout because we want to always preserve // original stack traces. object syncResult = null; @@ -165,81 +164,64 @@ private static object InvokeSynchronously(string assemblyName, string methodIden } } - private static object[] ParseArguments(string methodIdentifier, string argsJson, Type[] parameterTypes) + internal static object[] ParseArguments(string methodIdentifier, string arguments, Type[] parameterTypes) { if (parameterTypes.Length == 0) { return Array.Empty(); } - // There's no direct way to say we want to deserialize as an array with heterogenous - // entry types (e.g., [string, int, bool]), so we need to deserialize in two phases. - var jsonDocument = JsonDocument.Parse(argsJson); - var shouldDisposeJsonDocument = true; - try + var utf8JsonBytes = Encoding.UTF8.GetBytes(arguments); + var reader = new Utf8JsonReader(utf8JsonBytes); + if (!reader.Read() || reader.TokenType != JsonTokenType.StartArray) { - if (jsonDocument.RootElement.ValueKind != JsonValueKind.Array) - { - throw new ArgumentException($"Expected a JSON array but got {jsonDocument.RootElement.ValueKind}."); - } + throw new JsonException("Invalid JSON"); + } - var suppliedArgsLength = jsonDocument.RootElement.GetArrayLength(); + var suppliedArgs = new object[parameterTypes.Length]; - if (suppliedArgsLength != parameterTypes.Length) + var index = 0; + while (index < parameterTypes.Length && reader.Read() && reader.TokenType != JsonTokenType.EndArray) + { + var parameterType = parameterTypes[index]; + if (reader.TokenType == JsonTokenType.StartObject && IsIncorrectDotNetObjectRefUse(parameterType, reader)) { - throw new ArgumentException($"In call to '{methodIdentifier}', expected {parameterTypes.Length} parameters but received {suppliedArgsLength}."); + throw new InvalidOperationException($"In call to '{methodIdentifier}', parameter of type '{parameterType.Name}' at index {(index + 1)} must be declared as type 'DotNetObjectRef<{parameterType.Name}>' to receive the incoming value."); } - // Second, convert each supplied value to the type expected by the method - var suppliedArgs = new object[parameterTypes.Length]; - var index = 0; - foreach (var item in jsonDocument.RootElement.EnumerateArray()) - { - var parameterType = parameterTypes[index]; - - if (parameterType == typeof(JSAsyncCallResult)) - { - // We will pass the JsonDocument instance to JAsyncCallResult and make JSRuntimeBase - // responsible for disposing it. - shouldDisposeJsonDocument = false; - // For JS async call results, we have to defer the deserialization until - // later when we know what type it's meant to be deserialized as - suppliedArgs[index] = new JSAsyncCallResult(jsonDocument, item); - } - else if (IsIncorrectDotNetObjectRefUse(item, parameterType)) - { - throw new InvalidOperationException($"In call to '{methodIdentifier}', parameter of type '{parameterType.Name}' at index {(index + 1)} must be declared as type 'DotNetObjectRef<{parameterType.Name}>' to receive the incoming value."); - } - else - { - suppliedArgs[index] = JsonSerializer.Deserialize(item.GetRawText(), parameterType, JsonSerializerOptionsProvider.Options); - } - - index++; - } - - if (shouldDisposeJsonDocument) - { - jsonDocument.Dispose(); - } + suppliedArgs[index] = JsonSerializer.Deserialize(ref reader, parameterType, JsonSerializerOptionsProvider.Options); + index++; + } - return suppliedArgs; + if (index < parameterTypes.Length) + { + // If we parsed fewer parameters, we can always make a definitive claim about how many parameters were received. + throw new ArgumentException($"The call to '{methodIdentifier}' expects '{parameterTypes.Length}' parameters, but received '{index}'."); } - catch + + if (!reader.Read() || reader.TokenType != JsonTokenType.EndArray) { - // Always dispose the JsonDocument in case of an error. - jsonDocument?.Dispose(); - throw; + // Either we received more parameters than we expected or the JSON is malformed. + throw new JsonException($"Unexpected JSON token {reader.TokenType}. Ensure that the call to `{methodIdentifier}' is supplied with exactly '{parameterTypes.Length}' parameters."); } - static bool IsIncorrectDotNetObjectRefUse(JsonElement item, Type parameterType) + return suppliedArgs; + + // Note that the JsonReader instance is intentionally not passed by ref (or an in parameter) since we want a copy of the original reader. + static bool IsIncorrectDotNetObjectRefUse(Type parameterType, Utf8JsonReader jsonReader) { // Check for incorrect use of DotNetObjectRef at the top level. We know it's // an incorrect use if there's a object that looks like { '__dotNetObject': }, // but we aren't assigning to DotNetObjectRef{T}. - return item.ValueKind == JsonValueKind.Object && - item.TryGetProperty(DotNetObjectRefKey.EncodedUtf8Bytes, out _) && - !typeof(IDotNetObjectRef).IsAssignableFrom(parameterType); + if (jsonReader.Read() && + jsonReader.TokenType == JsonTokenType.PropertyName && + jsonReader.ValueTextEquals(DotNetObjectRefKey.EncodedUtf8Bytes)) + { + // The JSON payload has the shape we expect from a DotNetObjectRef instance. + return !parameterType.IsGenericType || parameterType.GetGenericTypeDefinition() != typeof(DotNetObjectRef<>); + } + + return false; } } @@ -248,9 +230,9 @@ static bool IsIncorrectDotNetObjectRefUse(JsonElement item, Type parameterType) /// associated as completed. /// /// - /// All exceptions from are caught + /// All exceptions from are caught /// are delivered via JS interop to the JavaScript side when it requests confirmation, as - /// the mechanism to call relies on + /// the mechanism to call relies on /// using JS->.NET interop. This overload is meant for directly triggering completion callbacks /// for .NET -> JS operations without going through JS interop, so the callsite for this /// method is responsible for handling any possible exception generated from the arguments @@ -263,16 +245,40 @@ static bool IsIncorrectDotNetObjectRefUse(JsonElement item, Type parameterType) /// public static void EndInvoke(string arguments) { - var parsedArgs = ParseArguments( - nameof(EndInvoke), - arguments, - EndInvokeParameterTypes); - - EndInvoke((long)parsedArgs[0], (bool)parsedArgs[1], (JSAsyncCallResult)parsedArgs[2]); + var jsRuntimeBase = (JSRuntimeBase)JSRuntime.Current; + ParseEndInvokeArguments(jsRuntimeBase, arguments); } - private static void EndInvoke(long asyncHandle, bool succeeded, JSAsyncCallResult result) - => ((JSRuntimeBase)JSRuntime.Current).EndInvokeJS(asyncHandle, succeeded, result); + internal static void ParseEndInvokeArguments(JSRuntimeBase jsRuntimeBase, string arguments) + { + var utf8JsonBytes = Encoding.UTF8.GetBytes(arguments); + + // The payload that we're trying to parse is of the format + // [ taskId: long, success: boolean, value: string? | object ] + // where value is the .NET type T originally specified on InvokeAsync or the error string if success is false. + // We parse the first two arguments and call in to JSRuntimeBase to deserialize the actual value. + + var reader = new Utf8JsonReader(utf8JsonBytes); + + if (!reader.Read() || reader.TokenType != JsonTokenType.StartArray) + { + throw new JsonException("Invalid JSON"); + } + + reader.Read(); + var taskId = reader.GetInt64(); + + reader.Read(); + var success = reader.GetBoolean(); + + reader.Read(); + jsRuntimeBase.EndInvokeJS(taskId, success, ref reader); + + if (!reader.Read() || reader.TokenType != JsonTokenType.EndArray) + { + throw new JsonException("Invalid JSON"); + } + } /// /// Releases the reference to the specified .NET object. This allows the .NET runtime @@ -362,7 +368,13 @@ private static Assembly GetRequiredLoadedAssembly(AssemblyKey assemblyKey) // In some edge cases this might force developers to explicitly call something on the // target assembly (from .NET) before they can invoke its allowed methods from JS. var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies(); - return loadedAssemblies.FirstOrDefault(a => new AssemblyKey(a).Equals(assemblyKey)) + + // Using LastOrDefault to workaround for https://github.com/dotnet/arcade/issues/2816. + // In most ordinary scenarios, we wouldn't have two instances of the same Assembly in the AppDomain + // so this doesn't change the outcome. + var assembly = loadedAssemblies.LastOrDefault(a => new AssemblyKey(a).Equals(assemblyKey)); + + return assembly ?? throw new ArgumentException($"There is no loaded assembly with the name '{assemblyKey.AssemblyName}'."); } @@ -396,6 +408,5 @@ public bool Equals(AssemblyKey other) public override int GetHashCode() => StringComparer.Ordinal.GetHashCode(AssemblyName); } - } } diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSAsyncCallResult.cs b/src/JSInterop/Microsoft.JSInterop/src/JSAsyncCallResult.cs deleted file mode 100644 index 209438529b82..000000000000 --- a/src/JSInterop/Microsoft.JSInterop/src/JSAsyncCallResult.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Text.Json; - -namespace Microsoft.JSInterop -{ - // This type takes care of a special case in handling the result of an async call from - // .NET to JS. The information about what type the result should be exists only on the - // corresponding TaskCompletionSource. We don't have that information at the time - // that we deserialize the incoming argsJson before calling DotNetDispatcher.EndInvoke. - // Declaring the EndInvoke parameter type as JSAsyncCallResult defers the deserialization - // until later when we have access to the TaskCompletionSource. - // - // There's no reason why developers would need anything similar to this in user code, - // because this is the mechanism by which we resolve the incoming argsJson to the correct - // user types before completing calls. - // - // It's marked as 'public' only because it has to be for use as an argument on a - // [JSInvokable] method. - - /// - /// Intended for framework use only. - /// - internal sealed class JSAsyncCallResult - { - internal JSAsyncCallResult(JsonDocument document, JsonElement jsonElement) - { - JsonDocument = document; - JsonElement = jsonElement; - } - - internal JsonElement JsonElement { get; } - internal JsonDocument JsonDocument { get; } - } -} diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs b/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs index 13a9ccae1c75..2121df052343 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs @@ -5,7 +5,6 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; -using System.Runtime.ExceptionServices; using System.Text.Json; using System.Threading; using System.Threading.Tasks; @@ -139,41 +138,37 @@ protected internal abstract void EndInvokeDotNet( string methodIdentifier, long dotNetObjectId); - internal void EndInvokeJS(long taskId, bool succeeded, JSAsyncCallResult asyncCallResult) + internal void EndInvokeJS(long taskId, bool succeeded, ref Utf8JsonReader jsonReader) { - using (asyncCallResult?.JsonDocument) + if (!_pendingTasks.TryRemove(taskId, out var tcs)) { - if (!_pendingTasks.TryRemove(taskId, out var tcs)) - { - // We should simply return if we can't find an id for the invocation. - // This likely means that the method that initiated the call defined a timeout and stopped waiting. - return; - } + // We should simply return if we can't find an id for the invocation. + // This likely means that the method that initiated the call defined a timeout and stopped waiting. + return; + } - CleanupTasksAndRegistrations(taskId); + CleanupTasksAndRegistrations(taskId); + try + { if (succeeded) { var resultType = TaskGenericsUtil.GetTaskCompletionSourceResultType(tcs); - try - { - var result = asyncCallResult != null ? - JsonSerializer.Deserialize(asyncCallResult.JsonElement.GetRawText(), resultType, JsonSerializerOptionsProvider.Options) : - null; - TaskGenericsUtil.SetTaskCompletionSourceResult(tcs, result); - } - catch (Exception exception) - { - var message = $"An exception occurred executing JS interop: {exception.Message}. See InnerException for more details."; - TaskGenericsUtil.SetTaskCompletionSourceException(tcs, new JSException(message, exception)); - } + + var result = JsonSerializer.Deserialize(ref jsonReader, resultType, JsonSerializerOptionsProvider.Options); + TaskGenericsUtil.SetTaskCompletionSourceResult(tcs, result); } else { - var exceptionText = asyncCallResult?.JsonElement.ToString() ?? string.Empty; + var exceptionText = jsonReader.GetString() ?? string.Empty; TaskGenericsUtil.SetTaskCompletionSourceException(tcs, new JSException(exceptionText)); } } + catch (Exception exception) + { + var message = $"An exception occurred executing JS interop: {exception.Message}. See InnerException for more details."; + TaskGenericsUtil.SetTaskCompletionSourceException(tcs, new JSException(message, exception)); + } } } } diff --git a/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs b/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs index 1c73cc63b25e..8e9d0dc3b988 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs @@ -2,15 +2,14 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.ComponentModel.DataAnnotations; using System.Linq; using System.Runtime.ExceptionServices; using System.Text.Json; -using System.Text.Json.Serialization; +using System.Threading; using System.Threading.Tasks; using Xunit; -namespace Microsoft.JSInterop.Tests +namespace Microsoft.JSInterop { public class DotNetDispatcherTest { @@ -239,6 +238,72 @@ public Task CannotUseDotNetObjectRefAfterReleaseDotNetObject() => WithJSRuntime( Assert.StartsWith("There is no tracked object with id '1'.", ex.Message); }); + [Fact] + public Task EndInvoke_WithSuccessValue() => WithJSRuntime(jsRuntime => + { + // Arrange + var testDTO = new TestDTO { StringVal = "Hello", IntVal = 4 }; + var task = jsRuntime.InvokeAsync("unimportant"); + var argsJson = JsonSerializer.Serialize(new object[] { jsRuntime.LastInvocationAsyncHandle, true, testDTO }, JsonSerializerOptionsProvider.Options); + + // Act + DotNetDispatcher.EndInvoke(argsJson); + + // Assert + Assert.True(task.IsCompletedSuccessfully); + var result = task.Result; + Assert.Equal(testDTO.StringVal, result.StringVal); + Assert.Equal(testDTO.IntVal, result.IntVal); + }); + + [Fact] + public Task EndInvoke_WithErrorString() => WithJSRuntime(async jsRuntime => + { + // Arrange + var expected = "Some error"; + var task = jsRuntime.InvokeAsync("unimportant"); + var argsJson = JsonSerializer.Serialize(new object[] { jsRuntime.LastInvocationAsyncHandle, false, expected }, JsonSerializerOptionsProvider.Options); + + // Act + DotNetDispatcher.EndInvoke(argsJson); + + // Assert + var ex = await Assert.ThrowsAsync(() => task); + Assert.Equal(expected, ex.Message); + }); + + [Fact(Skip = "https://github.com/aspnet/AspNetCore/issues/12357")] + public Task EndInvoke_AfterCancel() => WithJSRuntime(jsRuntime => + { + // Arrange + var testDTO = new TestDTO { StringVal = "Hello", IntVal = 4 }; + var cts = new CancellationTokenSource(); + var task = jsRuntime.InvokeAsync("unimportant", cts.Token); + var argsJson = JsonSerializer.Serialize(new object[] { jsRuntime.LastInvocationAsyncHandle, true, testDTO }, JsonSerializerOptionsProvider.Options); + + // Act + cts.Cancel(); + DotNetDispatcher.EndInvoke(argsJson); + + // Assert + Assert.True(task.IsCanceled); + }); + + [Fact] + public Task EndInvoke_WithNullError() => WithJSRuntime(async jsRuntime => + { + // Arrange + var task = jsRuntime.InvokeAsync("unimportant"); + var argsJson = JsonSerializer.Serialize(new object[] { jsRuntime.LastInvocationAsyncHandle, false, null }, JsonSerializerOptionsProvider.Options); + + // Act + DotNetDispatcher.EndInvoke(argsJson); + + // Assert + var ex = await Assert.ThrowsAsync(() => task); + Assert.Empty(ex.Message); + }); + [Fact] public Task CanInvokeInstanceMethodWithParams() => WithJSRuntime(jsRuntime => { @@ -261,10 +326,14 @@ public Task CanInvokeInstanceMethodWithParams() => WithJSRuntime(jsRuntime => }); [Fact] - public void CannotInvokeWithIncorrectNumberOfParams() + public Task CannotInvokeWithFewerNumberOfParameters() => WithJSRuntime(jsRuntime => { // Arrange - var argsJson = JsonSerializer.Serialize(new object[] { 1, 2, 3, 4 }, JsonSerializerOptionsProvider.Options); + var argsJson = JsonSerializer.Serialize(new object[] + { + new TestDTO { StringVal = "Another string", IntVal = 456 }, + new[] { 100, 200 }, + }, JsonSerializerOptionsProvider.Options); // Act/Assert var ex = Assert.Throws(() => @@ -272,8 +341,30 @@ public void CannotInvokeWithIncorrectNumberOfParams() DotNetDispatcher.Invoke(thisAssemblyName, "InvocableStaticWithParams", default, argsJson); }); - Assert.Equal("In call to 'InvocableStaticWithParams', expected 3 parameters but received 4.", ex.Message); - } + Assert.Equal("The call to 'InvocableStaticWithParams' expects '3' parameters, but received '2'.", ex.Message); + }); + + [Fact] + public Task CannotInvokeWithMoreParameters() => WithJSRuntime(jsRuntime => + { + // Arrange + var objectRef = DotNetObjectRef.Create(new TestDTO { IntVal = 4 }); + var argsJson = JsonSerializer.Serialize(new object[] + { + new TestDTO { StringVal = "Another string", IntVal = 456 }, + new[] { 100, 200 }, + objectRef, + 7, + }, JsonSerializerOptionsProvider.Options); + + // Act/Assert + var ex = Assert.Throws(() => + { + DotNetDispatcher.Invoke(thisAssemblyName, "InvocableStaticWithParams", default, argsJson); + }); + + Assert.Equal("Unexpected JSON token Number. Ensure that the call to `InvocableStaticWithParams' is supplied with exactly '3' parameters.", ex.Message); + }); [Fact] public Task CanInvokeAsyncMethod() => WithJSRuntime(async jsRuntime => @@ -301,7 +392,7 @@ public Task CanInvokeAsyncMethod() => WithJSRuntime(async jsRuntime => // Assert: Correct completion information Assert.Equal(callId, jsRuntime.LastCompletionCallId); Assert.True(jsRuntime.LastCompletionStatus); - var result = Assert.IsType(jsRuntime.LastCompletionResult); + var result = Assert.IsType(jsRuntime.LastCompletionResult); var resultDto1 = Assert.IsType(result[0]); Assert.Equal("STRING VIA JSON", resultDto1.StringVal); @@ -390,6 +481,150 @@ public Task BeginInvoke_ThrowsWithInvalid_DotNetObjectRef() => WithJSRuntime(jsR Assert.StartsWith("System.ArgumentException: There is no tracked object with id '1'. Perhaps the DotNetObjectRef instance was already disposed.", result.SourceException.ToString()); }); + [Theory] + [InlineData("")] + [InlineData("")] + public void ParseArguments_ThrowsIfJsonIsInvalid(string arguments) + { + Assert.ThrowsAny(() => DotNetDispatcher.ParseArguments("SomeMethod", arguments, new[] { typeof(string) })); + } + + [Theory] + [InlineData("{\"key\":\"value\"}")] + [InlineData("\"Test\"")] + public void ParseArguments_ThrowsIfTheArgsJsonIsNotArray(string arguments) + { + // Act & Assert + Assert.ThrowsAny(() => DotNetDispatcher.ParseArguments("SomeMethod", arguments, new[] { typeof(string) })); + } + + [Theory] + [InlineData("[\"hello\"")] + [InlineData("[\"hello\",")] + public void ParseArguments_ThrowsIfTheArgsJsonIsInvalidArray(string arguments) + { + // Act & Assert + Assert.ThrowsAny(() => DotNetDispatcher.ParseArguments("SomeMethod", arguments, new[] { typeof(string) })); + } + + [Fact] + public void ParseArguments_Works() + { + // Arrange + var arguments = "[\"Hello\", 2]"; + + // Act + var result = DotNetDispatcher.ParseArguments("SomeMethod", arguments, new[] { typeof(string), typeof(int), }); + + // Assert + Assert.Equal(new object[] { "Hello", 2 }, result); + } + + [Fact] + public void ParseArguments_SingleArgument() + { + // Arrange + var arguments = "[{\"IntVal\": 7}]"; + + // Act + var result = DotNetDispatcher.ParseArguments("SomeMethod", arguments, new[] { typeof(TestDTO), }); + + // Assert + var value = Assert.IsType(Assert.Single(result)); + Assert.Equal(7, value.IntVal); + Assert.Null(value.StringVal); + } + + [Fact] + public void ParseArguments_NullArgument() + { + // Arrange + var arguments = "[4, null]"; + + // Act + var result = DotNetDispatcher.ParseArguments("SomeMethod", arguments, new[] { typeof(int), typeof(TestDTO), }); + + // Assert + Assert.Collection( + result, + v => Assert.Equal(4, v), + v => Assert.Null(v)); + } + + [Fact] + public void ParseArguments_Throws_WithIncorrectDotNetObjectRefUsage() + { + // Arrange + var method = "SomeMethod"; + var arguments = "[4, {\"__dotNetObject\": 7}]"; + + // Act + var ex = Assert.Throws(() => DotNetDispatcher.ParseArguments(method, arguments, new[] { typeof(int), typeof(TestDTO), })); + + // Assert + Assert.Equal($"In call to '{method}', parameter of type '{nameof(TestDTO)}' at index 2 must be declared as type 'DotNetObjectRef' to receive the incoming value.", ex.Message); + } + + [Fact] + public void ParseEndInvokeArguments_ThrowsIfJsonIsEmptyString() + { + Assert.ThrowsAny(() => DotNetDispatcher.ParseEndInvokeArguments(new TestJSRuntime(), "")); + } + + [Fact] + public void ParseEndInvokeArguments_ThrowsIfJsonIsNotArray() + { + Assert.ThrowsAny(() => DotNetDispatcher.ParseEndInvokeArguments(new TestJSRuntime(), "{\"key\": \"value\"}")); + } + + [Fact] + public void ParseEndInvokeArguments_ThrowsIfJsonArrayIsInComplete() + { + Assert.ThrowsAny(() => DotNetDispatcher.ParseEndInvokeArguments(new TestJSRuntime(), "[7, false")); + } + + [Fact] + public void ParseEndInvokeArguments_ThrowsIfJsonArrayHasMoreThan3Arguments() + { + Assert.ThrowsAny(() => DotNetDispatcher.ParseEndInvokeArguments(new TestJSRuntime(), "[7, false, \"Hello\", 5]")); + } + + [Fact] + public void ParseEndInvokeArguments_Works() + { + var jsRuntime = new TestJSRuntime(); + var task = jsRuntime.InvokeAsync("somemethod"); + + DotNetDispatcher.ParseEndInvokeArguments(jsRuntime, $"[{jsRuntime.LastInvocationAsyncHandle}, true, {{\"intVal\": 7}}]"); + + Assert.True(task.IsCompletedSuccessfully); + Assert.Equal(7, task.Result.IntVal); + } + + [Fact] + public void ParseEndInvokeArguments_WithArrayValue() + { + var jsRuntime = new TestJSRuntime(); + var task = jsRuntime.InvokeAsync("somemethod"); + + DotNetDispatcher.ParseEndInvokeArguments(jsRuntime, $"[{jsRuntime.LastInvocationAsyncHandle}, true, [1, 2, 3]]"); + + Assert.True(task.IsCompletedSuccessfully); + Assert.Equal(new[] { 1, 2, 3 }, task.Result); + } + + [Fact] + public void ParseEndInvokeArguments_WithNullValue() + { + var jsRuntime = new TestJSRuntime(); + var task = jsRuntime.InvokeAsync("somemethod"); + + DotNetDispatcher.ParseEndInvokeArguments(jsRuntime, $"[{jsRuntime.LastInvocationAsyncHandle}, true, null]"); + + Assert.True(task.IsCompletedSuccessfully); + Assert.Null(task.Result); + } + Task WithJSRuntime(Action testCode) { return WithJSRuntime(jsRuntime => diff --git a/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs index fb9227c99611..468f8efba36d 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs @@ -4,13 +4,13 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Runtime.ExceptionServices; +using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Xunit; -namespace Microsoft.JSInterop.Tests +namespace Microsoft.JSInterop { public class JSRuntimeBaseTest { @@ -54,18 +54,19 @@ public async Task InvokeAsync_CancelsAsyncTask_AfterDefaultTimeout() } [Fact] - public async Task InvokeAsync_CompletesSuccessfullyBeforeTimeout() + public void InvokeAsync_CompletesSuccessfullyBeforeTimeout() { // Arrange var runtime = new TestJSRuntime(); runtime.DefaultTimeout = TimeSpan.FromSeconds(10); + var reader = new Utf8JsonReader(Encoding.UTF8.GetBytes("null")); // Act var task = runtime.InvokeAsync("test identifier 1", "arg1", 123, true); - runtime.EndInvokeJS(2, succeeded: true, null); - // Assert - await task; + runtime.EndInvokeJS(2, succeeded: true, ref reader); + + Assert.True(task.IsCompletedSuccessfully); } [Fact] @@ -113,18 +114,62 @@ public void CanCompleteAsyncCallsAsSuccess() var task = runtime.InvokeAsync("test identifier", Array.Empty()); Assert.False(unrelatedTask.IsCompleted); Assert.False(task.IsCompleted); - using var jsonDocument = JsonDocument.Parse("\"my result\""); + var bytes = Encoding.UTF8.GetBytes("\"my result\""); + var reader = new Utf8JsonReader(bytes); // Act/Assert: Task can be completed - runtime.OnEndInvoke( + runtime.EndInvokeJS( runtime.BeginInvokeCalls[1].AsyncHandle, /* succeeded: */ true, - new JSAsyncCallResult(jsonDocument, jsonDocument.RootElement)); + ref reader); Assert.False(unrelatedTask.IsCompleted); Assert.True(task.IsCompleted); Assert.Equal("my result", task.Result); } + [Fact] + public void CanCompleteAsyncCallsWithComplexType() + { + // Arrange + var runtime = new TestJSRuntime(); + + var task = runtime.InvokeAsync("test identifier", Array.Empty()); + var bytes = Encoding.UTF8.GetBytes("{\"id\":10, \"name\": \"Test\"}"); + var reader = new Utf8JsonReader(bytes); + + // Act/Assert: Task can be completed + runtime.EndInvokeJS( + runtime.BeginInvokeCalls[0].AsyncHandle, + /* succeeded: */ true, + ref reader); + Assert.True(task.IsCompleted); + var poco = task.Result; + Assert.Equal(10, poco.Id); + Assert.Equal("Test", poco.Name); + } + + [Fact] + public void CanCompleteAsyncCallsWithComplexTypeUsingPropertyCasing() + { + // Arrange + var runtime = new TestJSRuntime(); + + var task = runtime.InvokeAsync("test identifier", Array.Empty()); + var bytes = Encoding.UTF8.GetBytes("{\"Id\":10, \"Name\": \"Test\"}"); + var reader = new Utf8JsonReader(bytes); + reader.Read(); + + // Act/Assert: Task can be completed + runtime.EndInvokeJS( + runtime.BeginInvokeCalls[0].AsyncHandle, + /* succeeded: */ true, + ref reader); + Assert.True(task.IsCompleted); + var poco = task.Result; + Assert.Equal(10, poco.Id); + Assert.Equal("Test", poco.Name); + } + [Fact] public void CanCompleteAsyncCallsAsFailure() { @@ -136,13 +181,15 @@ public void CanCompleteAsyncCallsAsFailure() var task = runtime.InvokeAsync("test identifier", Array.Empty()); Assert.False(unrelatedTask.IsCompleted); Assert.False(task.IsCompleted); - using var jsonDocument = JsonDocument.Parse("\"This is a test exception\""); + var bytes = Encoding.UTF8.GetBytes("\"This is a test exception\""); + var reader = new Utf8JsonReader(bytes); + reader.Read(); // Act/Assert: Task can be failed - runtime.OnEndInvoke( + runtime.EndInvokeJS( runtime.BeginInvokeCalls[1].AsyncHandle, /* succeeded: */ false, - new JSAsyncCallResult(jsonDocument, jsonDocument.RootElement)); + ref reader); Assert.False(unrelatedTask.IsCompleted); Assert.True(task.IsCompleted); @@ -152,7 +199,7 @@ public void CanCompleteAsyncCallsAsFailure() } [Fact] - public async Task CanCompleteAsyncCallsWithErrorsDuringDeserialization() + public Task CanCompleteAsyncCallsWithErrorsDuringDeserialization() { // Arrange var runtime = new TestJSRuntime(); @@ -162,24 +209,27 @@ public async Task CanCompleteAsyncCallsWithErrorsDuringDeserialization() var task = runtime.InvokeAsync("test identifier", Array.Empty()); Assert.False(unrelatedTask.IsCompleted); Assert.False(task.IsCompleted); - using var jsonDocument = JsonDocument.Parse("\"Not a string\""); + var bytes = Encoding.UTF8.GetBytes("Not a string"); + var reader = new Utf8JsonReader(bytes); // Act/Assert: Task can be failed - runtime.OnEndInvoke( + runtime.EndInvokeJS( runtime.BeginInvokeCalls[1].AsyncHandle, /* succeeded: */ true, - new JSAsyncCallResult(jsonDocument, jsonDocument.RootElement)); + ref reader); Assert.False(unrelatedTask.IsCompleted); - var jsException = await Assert.ThrowsAsync(() => task); - Assert.IsType(jsException.InnerException); + return AssertTask(); - // Verify we've disposed the JsonDocument. - Assert.Throws(() => jsonDocument.RootElement.ValueKind); + async Task AssertTask() + { + var jsException = await Assert.ThrowsAsync(() => task); + Assert.IsAssignableFrom(jsException.InnerException); + } } [Fact] - public async Task CompletingSameAsyncCallMoreThanOnce_IgnoresSecondResultAsync() + public Task CompletingSameAsyncCallMoreThanOnce_IgnoresSecondResultAsync() { // Arrange var runtime = new TestJSRuntime(); @@ -187,11 +237,19 @@ public async Task CompletingSameAsyncCallMoreThanOnce_IgnoresSecondResultAsync() // Act/Assert var task = runtime.InvokeAsync("test identifier", Array.Empty()); var asyncHandle = runtime.BeginInvokeCalls[0].AsyncHandle; - runtime.OnEndInvoke(asyncHandle, true, new JSAsyncCallResult(JsonDocument.Parse("{}"), JsonDocument.Parse("{\"Message\": \"Some data\"}").RootElement.GetProperty("Message"))); - runtime.OnEndInvoke(asyncHandle, false, new JSAsyncCallResult(null, JsonDocument.Parse("{\"Message\": \"Exception\"}").RootElement.GetProperty("Message"))); + var firstReader = new Utf8JsonReader(Encoding.UTF8.GetBytes("\"Some data\"")); + var secondReader = new Utf8JsonReader(Encoding.UTF8.GetBytes("\"Exception\"")); + + runtime.EndInvokeJS(asyncHandle, true, ref firstReader); + runtime.EndInvokeJS(asyncHandle, false, ref secondReader); - var result = await task; - Assert.Equal("Some data", result); + return AssertTask(); + + async Task AssertTask() + { + var result = await task; + Assert.Equal("Some data", result); + } } [Fact] @@ -263,6 +321,13 @@ private class JSError public string Message { get; set; } } + private class TestPoco + { + public int Id { get; set; } + + public string Name { get; set; } + } + class TestJSRuntime : JSRuntimeBase { public List BeginInvokeCalls = new List(); @@ -316,9 +381,6 @@ protected override void BeginInvokeJS(long asyncHandle, string identifier, strin ArgsJson = argsJson, }); } - - public void OnEndInvoke(long asyncHandle, bool succeeded, JSAsyncCallResult callResult) - => EndInvokeJS(asyncHandle, succeeded, callResult); } } } From 95746c596e33437674c11f56db7beebe4e053f6a Mon Sep 17 00:00:00 2001 From: John Luo Date: Wed, 7 Aug 2019 13:26:14 -0700 Subject: [PATCH 119/183] Multitarget extensions packages to remove dependencies for source build (dotnet/extensions#2090) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/3ec8c35e450c54ca6703e7ac6136ae8635a2cb90 --- ...Extensions.Configuration.KeyPerFile.csproj | 7 +- ....Configuration.KeyPerFile.netcoreapp3.0.cs | 29 ++++++ ...Extensions.Configuration.KeyPerFile.csproj | 3 +- ...t.Extensions.FileProviders.Embedded.csproj | 6 +- ...ns.FileProviders.Embedded.netcoreapp3.0.cs | 39 ++++++++ ...t.Extensions.FileProviders.Embedded.csproj | 31 +++---- ....FileProviders.Embedded.multitarget.nuspec | 24 +++++ ...leProviders.Embedded.netcoreapp3.0.nuspec} | 8 +- ...agnostics.HealthChecks.Abstractions.csproj | 6 +- ...HealthChecks.Abstractions.netcoreapp3.0.cs | 72 ++++++++++++++ ...agnostics.HealthChecks.Abstractions.csproj | 3 +- ...Extensions.Diagnostics.HealthChecks.csproj | 8 +- ....Diagnostics.HealthChecks.netcoreapp3.0.cs | 59 ++++++++++++ ...Extensions.Diagnostics.HealthChecks.csproj | 3 +- .../ref/Microsoft.JSInterop.csproj | 6 +- .../ref/Microsoft.JSInterop.netcoreapp3.0.cs | 64 +++++++++++++ .../src/Microsoft.JSInterop.csproj | 5 +- .../test/Microsoft.JSInterop.Tests.csproj | 2 +- .../src/Mono.WebAssembly.Interop.csproj | 2 + ...xtensions.Localization.Abstractions.csproj | 6 +- ...Localization.Abstractions.netcoreapp3.0.cs | 49 ++++++++++ ...xtensions.Localization.Abstractions.csproj | 3 +- .../Microsoft.Extensions.Localization.csproj | 9 +- ...t.Extensions.Localization.netcoreapp3.0.cs | 93 +++++++++++++++++++ .../Microsoft.Extensions.Localization.csproj | 3 +- .../Microsoft.Extensions.WebEncoders.csproj | 7 +- ...ft.Extensions.WebEncoders.netcoreapp3.0.cs | 55 +++++++++++ .../Microsoft.Extensions.WebEncoders.csproj | 6 +- 28 files changed, 568 insertions(+), 40 deletions(-) create mode 100644 src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.netcoreapp3.0.cs create mode 100644 src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.netcoreapp3.0.cs create mode 100644 src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.multitarget.nuspec rename src/FileProviders/Embedded/src/{Microsoft.Extensions.FileProviders.Embedded.nuspec => Microsoft.Extensions.FileProviders.Embedded.netcoreapp3.0.nuspec} (73%) create mode 100644 src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netcoreapp3.0.cs create mode 100644 src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netcoreapp3.0.cs create mode 100644 src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netcoreapp3.0.cs create mode 100644 src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netcoreapp3.0.cs create mode 100644 src/Localization/Localization/ref/Microsoft.Extensions.Localization.netcoreapp3.0.cs create mode 100644 src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.netcoreapp3.0.cs diff --git a/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj b/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj index 21f0053e59be..4e074ab493c9 100644 --- a/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj +++ b/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj @@ -1,11 +1,16 @@ - netstandard2.0 + netstandard2.0;netcoreapp3.0 + + + + + diff --git a/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.netcoreapp3.0.cs b/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.netcoreapp3.0.cs new file mode 100644 index 000000000000..e26ca1909d18 --- /dev/null +++ b/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.netcoreapp3.0.cs @@ -0,0 +1,29 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.Extensions.Configuration +{ + public static partial class KeyPerFileConfigurationBuilderExtensions + { + public static Microsoft.Extensions.Configuration.IConfigurationBuilder AddKeyPerFile(this Microsoft.Extensions.Configuration.IConfigurationBuilder builder, System.Action configureSource) { throw null; } + public static Microsoft.Extensions.Configuration.IConfigurationBuilder AddKeyPerFile(this Microsoft.Extensions.Configuration.IConfigurationBuilder builder, string directoryPath, bool optional) { throw null; } + } +} +namespace Microsoft.Extensions.Configuration.KeyPerFile +{ + public partial class KeyPerFileConfigurationProvider : Microsoft.Extensions.Configuration.ConfigurationProvider + { + public KeyPerFileConfigurationProvider(Microsoft.Extensions.Configuration.KeyPerFile.KeyPerFileConfigurationSource source) { } + public override void Load() { } + public override string ToString() { throw null; } + } + public partial class KeyPerFileConfigurationSource : Microsoft.Extensions.Configuration.IConfigurationSource + { + public KeyPerFileConfigurationSource() { } + public Microsoft.Extensions.FileProviders.IFileProvider FileProvider { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public System.Func IgnoreCondition { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public string IgnorePrefix { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public bool Optional { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public Microsoft.Extensions.Configuration.IConfigurationProvider Build(Microsoft.Extensions.Configuration.IConfigurationBuilder builder) { throw null; } + } +} diff --git a/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj b/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj index 8c76c174ad14..10f41fa9f49d 100644 --- a/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj +++ b/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj @@ -2,7 +2,8 @@ Configuration provider that uses files in a directory for Microsoft.Extensions.Configuration. - netstandard2.0 + netstandard2.0;netcoreapp3.0 + netcoreapp3.0 true true diff --git a/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.csproj b/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.csproj index b8f2f33387f4..e9407f9164b0 100644 --- a/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.csproj +++ b/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.csproj @@ -1,10 +1,14 @@ - netstandard2.0 + netstandard2.0;netcoreapp3.0 + + + + diff --git a/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.netcoreapp3.0.cs b/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.netcoreapp3.0.cs new file mode 100644 index 000000000000..1596f191fdd4 --- /dev/null +++ b/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.netcoreapp3.0.cs @@ -0,0 +1,39 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.Extensions.FileProviders +{ + public partial class EmbeddedFileProvider : Microsoft.Extensions.FileProviders.IFileProvider + { + public EmbeddedFileProvider(System.Reflection.Assembly assembly) { } + public EmbeddedFileProvider(System.Reflection.Assembly assembly, string baseNamespace) { } + public Microsoft.Extensions.FileProviders.IDirectoryContents GetDirectoryContents(string subpath) { throw null; } + public Microsoft.Extensions.FileProviders.IFileInfo GetFileInfo(string subpath) { throw null; } + public Microsoft.Extensions.Primitives.IChangeToken Watch(string pattern) { throw null; } + } + public partial class ManifestEmbeddedFileProvider : Microsoft.Extensions.FileProviders.IFileProvider + { + public ManifestEmbeddedFileProvider(System.Reflection.Assembly assembly) { } + public ManifestEmbeddedFileProvider(System.Reflection.Assembly assembly, string root) { } + public ManifestEmbeddedFileProvider(System.Reflection.Assembly assembly, string root, System.DateTimeOffset lastModified) { } + public ManifestEmbeddedFileProvider(System.Reflection.Assembly assembly, string root, string manifestName, System.DateTimeOffset lastModified) { } + public System.Reflection.Assembly Assembly { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public Microsoft.Extensions.FileProviders.IDirectoryContents GetDirectoryContents(string subpath) { throw null; } + public Microsoft.Extensions.FileProviders.IFileInfo GetFileInfo(string subpath) { throw null; } + public Microsoft.Extensions.Primitives.IChangeToken Watch(string filter) { throw null; } + } +} +namespace Microsoft.Extensions.FileProviders.Embedded +{ + public partial class EmbeddedResourceFileInfo : Microsoft.Extensions.FileProviders.IFileInfo + { + public EmbeddedResourceFileInfo(System.Reflection.Assembly assembly, string resourcePath, string name, System.DateTimeOffset lastModified) { } + public bool Exists { get { throw null; } } + public bool IsDirectory { get { throw null; } } + public System.DateTimeOffset LastModified { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public long Length { get { throw null; } } + public string Name { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string PhysicalPath { get { throw null; } } + public System.IO.Stream CreateReadStream() { throw null; } + } +} diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj index 8119c2633903..115d9affe14a 100644 --- a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj @@ -3,8 +3,10 @@ Microsoft.Extensions.FileProviders File provider for files in embedded resources for Microsoft.Extensions.FileProviders. - netstandard2.0 - $(MSBuildProjectName).nuspec + netstandard2.0;netcoreapp3.0 + $(MSBuildProjectName).multitarget.nuspec + netcoreapp3.0 + $(MSBuildProjectName).netcoreapp3.0.nuspec true true @@ -23,21 +25,12 @@ - - - <_OutputBinary>@(BuiltProjectOutputGroupOutput) - <_OutputSymbol>@(DebugSymbolsProjectOutputGroupOutput) - <_OutputDocumentation>@(DocumentationProjectOutputGroupOutput) - - - - - - - - - - - - + + + + + + + + diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.multitarget.nuspec b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.multitarget.nuspec new file mode 100644 index 000000000000..874c90c79dcc --- /dev/null +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.multitarget.nuspec @@ -0,0 +1,24 @@ + + + + $CommonMetadataElements$ + + + + + + + + + + + + + + + + + + + + diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.nuspec b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.netcoreapp3.0.nuspec similarity index 73% rename from src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.nuspec rename to src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.netcoreapp3.0.nuspec index 4a33eb6a952a..f98f0b4ad5aa 100644 --- a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.nuspec +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.netcoreapp3.0.nuspec @@ -3,16 +3,16 @@ $CommonMetadataElements$ - + - - - + + + diff --git a/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj index be2385895560..f7514489fa30 100644 --- a/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj +++ b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj @@ -1,10 +1,14 @@ - netstandard2.0 + netstandard2.0;netcoreapp3.0 + + + + diff --git a/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netcoreapp3.0.cs b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netcoreapp3.0.cs new file mode 100644 index 000000000000..8c53adc27598 --- /dev/null +++ b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netcoreapp3.0.cs @@ -0,0 +1,72 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.Extensions.Diagnostics.HealthChecks +{ + public sealed partial class HealthCheckContext + { + public HealthCheckContext() { } + public Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckRegistration Registration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + } + public sealed partial class HealthCheckRegistration + { + public HealthCheckRegistration(string name, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck instance, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable tags) { } + public HealthCheckRegistration(string name, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck instance, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable tags, System.TimeSpan? timeout) { } + public HealthCheckRegistration(string name, System.Func factory, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable tags) { } + public HealthCheckRegistration(string name, System.Func factory, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable tags, System.TimeSpan? timeout) { } + public System.Func Factory { get { throw null; } set { } } + public Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus FailureStatus { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public string Name { get { throw null; } set { } } + public System.Collections.Generic.ISet Tags { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.TimeSpan Timeout { get { throw null; } set { } } + } + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] + public partial struct HealthCheckResult + { + private object _dummy; + private int _dummyPrimitive; + public HealthCheckResult(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus status, string description = null, System.Exception exception = null, System.Collections.Generic.IReadOnlyDictionary data = null) { throw null; } + public System.Collections.Generic.IReadOnlyDictionary Data { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string Description { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Exception Exception { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus Status { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public static Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult Degraded(string description = null, System.Exception exception = null, System.Collections.Generic.IReadOnlyDictionary data = null) { throw null; } + public static Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult Healthy(string description = null, System.Collections.Generic.IReadOnlyDictionary data = null) { throw null; } + public static Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult Unhealthy(string description = null, System.Exception exception = null, System.Collections.Generic.IReadOnlyDictionary data = null) { throw null; } + } + public sealed partial class HealthReport + { + public HealthReport(System.Collections.Generic.IReadOnlyDictionary entries, System.TimeSpan totalDuration) { } + public System.Collections.Generic.IReadOnlyDictionary Entries { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus Status { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.TimeSpan TotalDuration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] + public partial struct HealthReportEntry + { + private object _dummy; + private int _dummyPrimitive; + public HealthReportEntry(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus status, string description, System.TimeSpan duration, System.Exception exception, System.Collections.Generic.IReadOnlyDictionary data) { throw null; } + public HealthReportEntry(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus status, string description, System.TimeSpan duration, System.Exception exception, System.Collections.Generic.IReadOnlyDictionary data, System.Collections.Generic.IEnumerable tags = null) { throw null; } + public System.Collections.Generic.IReadOnlyDictionary Data { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string Description { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.TimeSpan Duration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Exception Exception { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus Status { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Collections.Generic.IEnumerable Tags { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } + public enum HealthStatus + { + Unhealthy = 0, + Degraded = 1, + Healthy = 2, + } + public partial interface IHealthCheck + { + System.Threading.Tasks.Task CheckHealthAsync(Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckContext context, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + } + public partial interface IHealthCheckPublisher + { + System.Threading.Tasks.Task PublishAsync(Microsoft.Extensions.Diagnostics.HealthChecks.HealthReport report, System.Threading.CancellationToken cancellationToken); + } +} diff --git a/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj b/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj index 306a303421eb..f41b257c6322 100644 --- a/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj +++ b/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj @@ -7,7 +7,8 @@ Commonly Used Types Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck Microsoft.Extensions.Diagnostics.HealthChecks - netstandard2.0 + netstandard2.0;netcoreapp3.0 + netcoreapp3.0 $(NoWarn);CS1591 true diagnostics;healthchecks diff --git a/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj b/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj index 277e60910fbf..2286087bc786 100644 --- a/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj +++ b/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + netstandard2.0;netcoreapp3.0 @@ -9,4 +9,10 @@ + + + + + + diff --git a/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netcoreapp3.0.cs b/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netcoreapp3.0.cs new file mode 100644 index 000000000000..a23961efdd26 --- /dev/null +++ b/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netcoreapp3.0.cs @@ -0,0 +1,59 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.Extensions.DependencyInjection +{ + public static partial class HealthChecksBuilderAddCheckExtensions + { + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck instance, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable tags) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck instance, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus = default(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus?), System.Collections.Generic.IEnumerable tags = null, System.TimeSpan? timeout = default(System.TimeSpan?)) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable tags) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus = default(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus?), System.Collections.Generic.IEnumerable tags = null, System.TimeSpan? timeout = default(System.TimeSpan?)) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddTypeActivatedCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable tags, params object[] args) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddTypeActivatedCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable tags, System.TimeSpan timeout, params object[] args) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddTypeActivatedCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, params object[] args) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddTypeActivatedCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, params object[] args) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; } + } + public static partial class HealthChecksBuilderDelegateExtensions + { + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddAsyncCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func> check, System.Collections.Generic.IEnumerable tags) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddAsyncCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func> check, System.Collections.Generic.IEnumerable tags = null, System.TimeSpan? timeout = default(System.TimeSpan?)) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddAsyncCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func> check, System.Collections.Generic.IEnumerable tags) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddAsyncCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func> check, System.Collections.Generic.IEnumerable tags = null, System.TimeSpan? timeout = default(System.TimeSpan?)) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func check, System.Collections.Generic.IEnumerable tags) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func check, System.Collections.Generic.IEnumerable tags = null, System.TimeSpan? timeout = default(System.TimeSpan?)) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func check, System.Collections.Generic.IEnumerable tags) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func check, System.Collections.Generic.IEnumerable tags = null, System.TimeSpan? timeout = default(System.TimeSpan?)) { throw null; } + } + public static partial class HealthCheckServiceCollectionExtensions + { + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddHealthChecks(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) { throw null; } + } + public partial interface IHealthChecksBuilder + { + Microsoft.Extensions.DependencyInjection.IServiceCollection Services { get; } + Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder Add(Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckRegistration registration); + } +} +namespace Microsoft.Extensions.Diagnostics.HealthChecks +{ + public sealed partial class HealthCheckPublisherOptions + { + public HealthCheckPublisherOptions() { } + public System.TimeSpan Delay { get { throw null; } set { } } + public System.TimeSpan Period { get { throw null; } set { } } + public System.Func Predicate { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public System.TimeSpan Timeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + } + public abstract partial class HealthCheckService + { + protected HealthCheckService() { } + public abstract System.Threading.Tasks.Task CheckHealthAsync(System.Func predicate, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + public System.Threading.Tasks.Task CheckHealthAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + } + public sealed partial class HealthCheckServiceOptions + { + public HealthCheckServiceOptions() { } + public System.Collections.Generic.ICollection Registrations { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } +} diff --git a/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj b/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj index 9670e8bb7b84..40056850113e 100644 --- a/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj +++ b/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj @@ -6,7 +6,8 @@ Commonly Used Types: Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckService Microsoft.Extensions.Diagnostics.HealthChecks.IHealthChecksBuilder - netstandard2.0 + netstandard2.0;netcoreapp3.0 + netcoreapp3.0 $(NoWarn);CS1591 true diagnostics;healthchecks diff --git a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj index 5f83c5309186..61980aa92097 100644 --- a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj +++ b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj @@ -1,10 +1,14 @@ - netstandard2.0 + netstandard2.0;netcoreapp3.0 + + + + diff --git a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netcoreapp3.0.cs b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netcoreapp3.0.cs new file mode 100644 index 000000000000..654ae9d6173a --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netcoreapp3.0.cs @@ -0,0 +1,64 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.JSInterop +{ + public static partial class DotNetDispatcher + { + public static void BeginInvoke(string callId, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { } + public static void EndInvoke(string arguments) { } + public static string Invoke(string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { throw null; } + [Microsoft.JSInterop.JSInvokableAttribute("DotNetDispatcher.ReleaseDotNetObject")] + public static void ReleaseDotNetObject(long dotNetObjectId) { } + } + public static partial class DotNetObjectRef + { + public static Microsoft.JSInterop.DotNetObjectRef Create(TValue value) where TValue : class { throw null; } + } + public sealed partial class DotNetObjectRef : System.IDisposable where TValue : class + { + internal DotNetObjectRef() { } + public TValue Value { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public void Dispose() { } + } + public partial interface IJSInProcessRuntime : Microsoft.JSInterop.IJSRuntime + { + T Invoke(string identifier, params object[] args); + } + public partial interface IJSRuntime + { + System.Threading.Tasks.Task InvokeAsync(string identifier, System.Collections.Generic.IEnumerable args, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + System.Threading.Tasks.Task InvokeAsync(string identifier, params object[] args); + } + public partial class JSException : System.Exception + { + public JSException(string message) { } + public JSException(string message, System.Exception innerException) { } + } + public abstract partial class JSInProcessRuntimeBase : Microsoft.JSInterop.JSRuntimeBase, Microsoft.JSInterop.IJSInProcessRuntime, Microsoft.JSInterop.IJSRuntime + { + protected JSInProcessRuntimeBase() { } + protected abstract string InvokeJS(string identifier, string argsJson); + public TValue Invoke(string identifier, params object[] args) { throw null; } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Method, AllowMultiple=true)] + public partial class JSInvokableAttribute : System.Attribute + { + public JSInvokableAttribute() { } + public JSInvokableAttribute(string identifier) { } + public string Identifier { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } + public static partial class JSRuntime + { + public static void SetCurrentJSRuntime(Microsoft.JSInterop.IJSRuntime instance) { } + } + public abstract partial class JSRuntimeBase : Microsoft.JSInterop.IJSRuntime + { + protected JSRuntimeBase() { } + protected System.TimeSpan? DefaultAsyncTimeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + protected abstract void BeginInvokeJS(long taskId, string identifier, string argsJson); + protected internal abstract void EndInvokeDotNet(string callId, bool success, object resultOrError, string assemblyName, string methodIdentifier, long dotNetObjectId); + public System.Threading.Tasks.Task InvokeAsync(string identifier, System.Collections.Generic.IEnumerable args, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public System.Threading.Tasks.Task InvokeAsync(string identifier, params object[] args) { throw null; } + } +} diff --git a/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj b/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj index 8dfb1ef22edb..044c843b2815 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj +++ b/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj @@ -1,7 +1,8 @@  - netstandard2.0 + netstandard2.0;netcoreapp3.0 + netcoreapp3.0 Abstractions and features for interop between .NET and JavaScript code. javascript;interop true @@ -9,7 +10,7 @@ true - + diff --git a/src/JSInterop/Microsoft.JSInterop/test/Microsoft.JSInterop.Tests.csproj b/src/JSInterop/Microsoft.JSInterop/test/Microsoft.JSInterop.Tests.csproj index 34ac70b80120..bff8fb3f99d2 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/Microsoft.JSInterop.Tests.csproj +++ b/src/JSInterop/Microsoft.JSInterop/test/Microsoft.JSInterop.Tests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0 + netcoreapp3.0;net472 diff --git a/src/JSInterop/Mono.WebAssembly.Interop/src/Mono.WebAssembly.Interop.csproj b/src/JSInterop/Mono.WebAssembly.Interop/src/Mono.WebAssembly.Interop.csproj index fc927084563b..f85c89a6d363 100644 --- a/src/JSInterop/Mono.WebAssembly.Interop/src/Mono.WebAssembly.Interop.csproj +++ b/src/JSInterop/Mono.WebAssembly.Interop/src/Mono.WebAssembly.Interop.csproj @@ -7,6 +7,8 @@ true true true + + true diff --git a/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.csproj b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.csproj index 0608c06147cf..6404d5ae8ecb 100644 --- a/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.csproj +++ b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.csproj @@ -1,10 +1,14 @@ - netstandard2.0 + netstandard2.0;netcoreapp3.0 + + + + diff --git a/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netcoreapp3.0.cs b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netcoreapp3.0.cs new file mode 100644 index 000000000000..174cac28e5cb --- /dev/null +++ b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netcoreapp3.0.cs @@ -0,0 +1,49 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.Extensions.Localization +{ + public partial interface IStringLocalizer + { + Microsoft.Extensions.Localization.LocalizedString this[string name] { get; } + Microsoft.Extensions.Localization.LocalizedString this[string name, params object[] arguments] { get; } + System.Collections.Generic.IEnumerable GetAllStrings(bool includeParentCultures); + [System.ObsoleteAttribute("This method is obsolete. Use `CurrentCulture` and `CurrentUICulture` instead.")] + Microsoft.Extensions.Localization.IStringLocalizer WithCulture(System.Globalization.CultureInfo culture); + } + public partial interface IStringLocalizerFactory + { + Microsoft.Extensions.Localization.IStringLocalizer Create(string baseName, string location); + Microsoft.Extensions.Localization.IStringLocalizer Create(System.Type resourceSource); + } + public partial interface IStringLocalizer : Microsoft.Extensions.Localization.IStringLocalizer + { + } + public partial class LocalizedString + { + public LocalizedString(string name, string value) { } + public LocalizedString(string name, string value, bool resourceNotFound) { } + public LocalizedString(string name, string value, bool resourceNotFound, string searchedLocation) { } + public string Name { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public bool ResourceNotFound { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string SearchedLocation { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string Value { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public static implicit operator string (Microsoft.Extensions.Localization.LocalizedString localizedString) { throw null; } + public override string ToString() { throw null; } + } + public static partial class StringLocalizerExtensions + { + public static System.Collections.Generic.IEnumerable GetAllStrings(this Microsoft.Extensions.Localization.IStringLocalizer stringLocalizer) { throw null; } + public static Microsoft.Extensions.Localization.LocalizedString GetString(this Microsoft.Extensions.Localization.IStringLocalizer stringLocalizer, string name) { throw null; } + public static Microsoft.Extensions.Localization.LocalizedString GetString(this Microsoft.Extensions.Localization.IStringLocalizer stringLocalizer, string name, params object[] arguments) { throw null; } + } + public partial class StringLocalizer : Microsoft.Extensions.Localization.IStringLocalizer, Microsoft.Extensions.Localization.IStringLocalizer + { + public StringLocalizer(Microsoft.Extensions.Localization.IStringLocalizerFactory factory) { } + public virtual Microsoft.Extensions.Localization.LocalizedString this[string name] { get { throw null; } } + public virtual Microsoft.Extensions.Localization.LocalizedString this[string name, params object[] arguments] { get { throw null; } } + public System.Collections.Generic.IEnumerable GetAllStrings(bool includeParentCultures) { throw null; } + [System.ObsoleteAttribute("This method is obsolete. Use `CurrentCulture` and `CurrentUICulture` instead.")] + public virtual Microsoft.Extensions.Localization.IStringLocalizer WithCulture(System.Globalization.CultureInfo culture) { throw null; } + } +} diff --git a/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj b/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj index 86af6dfde9fe..09b8bf65b79e 100644 --- a/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj +++ b/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj @@ -6,7 +6,8 @@ Commonly used types: Microsoft.Extensions.Localization.IStringLocalizer Microsoft.Extensions.Localization.IStringLocalizer<T> - netstandard2.0 + netstandard2.0;netcoreapp3.0 + netcoreapp3.0 $(NoWarn);CS1591 true localization diff --git a/src/Localization/Localization/ref/Microsoft.Extensions.Localization.csproj b/src/Localization/Localization/ref/Microsoft.Extensions.Localization.csproj index d628c33a54db..735277e75418 100644 --- a/src/Localization/Localization/ref/Microsoft.Extensions.Localization.csproj +++ b/src/Localization/Localization/ref/Microsoft.Extensions.Localization.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + netstandard2.0;netcoreapp3.0 @@ -10,4 +10,11 @@ + + + + + + + diff --git a/src/Localization/Localization/ref/Microsoft.Extensions.Localization.netcoreapp3.0.cs b/src/Localization/Localization/ref/Microsoft.Extensions.Localization.netcoreapp3.0.cs new file mode 100644 index 000000000000..80175da71840 --- /dev/null +++ b/src/Localization/Localization/ref/Microsoft.Extensions.Localization.netcoreapp3.0.cs @@ -0,0 +1,93 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.Extensions.DependencyInjection +{ + public static partial class LocalizationServiceCollectionExtensions + { + public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddLocalization(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddLocalization(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action setupAction) { throw null; } + } +} +namespace Microsoft.Extensions.Localization +{ + public partial interface IResourceNamesCache + { + System.Collections.Generic.IList GetOrAdd(string name, System.Func> valueFactory); + } + public partial class LocalizationOptions + { + public LocalizationOptions() { } + public string ResourcesPath { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly, AllowMultiple=false, Inherited=false)] + public partial class ResourceLocationAttribute : System.Attribute + { + public ResourceLocationAttribute(string resourceLocation) { } + public string ResourceLocation { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } + public partial class ResourceManagerStringLocalizer : Microsoft.Extensions.Localization.IStringLocalizer + { + public ResourceManagerStringLocalizer(System.Resources.ResourceManager resourceManager, Microsoft.Extensions.Localization.Internal.AssemblyWrapper resourceAssemblyWrapper, string baseName, Microsoft.Extensions.Localization.IResourceNamesCache resourceNamesCache, Microsoft.Extensions.Logging.ILogger logger) { } + public ResourceManagerStringLocalizer(System.Resources.ResourceManager resourceManager, Microsoft.Extensions.Localization.Internal.IResourceStringProvider resourceStringProvider, string baseName, Microsoft.Extensions.Localization.IResourceNamesCache resourceNamesCache, Microsoft.Extensions.Logging.ILogger logger) { } + public ResourceManagerStringLocalizer(System.Resources.ResourceManager resourceManager, System.Reflection.Assembly resourceAssembly, string baseName, Microsoft.Extensions.Localization.IResourceNamesCache resourceNamesCache, Microsoft.Extensions.Logging.ILogger logger) { } + public virtual Microsoft.Extensions.Localization.LocalizedString this[string name] { get { throw null; } } + public virtual Microsoft.Extensions.Localization.LocalizedString this[string name, params object[] arguments] { get { throw null; } } + public virtual System.Collections.Generic.IEnumerable GetAllStrings(bool includeParentCultures) { throw null; } + protected System.Collections.Generic.IEnumerable GetAllStrings(bool includeParentCultures, System.Globalization.CultureInfo culture) { throw null; } + protected string GetStringSafely(string name, System.Globalization.CultureInfo culture) { throw null; } + [System.ObsoleteAttribute("This method is obsolete. Use `CurrentCulture` and `CurrentUICulture` instead.")] + public Microsoft.Extensions.Localization.IStringLocalizer WithCulture(System.Globalization.CultureInfo culture) { throw null; } + } + public partial class ResourceManagerStringLocalizerFactory : Microsoft.Extensions.Localization.IStringLocalizerFactory + { + public ResourceManagerStringLocalizerFactory(Microsoft.Extensions.Options.IOptions localizationOptions, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) { } + public Microsoft.Extensions.Localization.IStringLocalizer Create(string baseName, string location) { throw null; } + public Microsoft.Extensions.Localization.IStringLocalizer Create(System.Type resourceSource) { throw null; } + protected virtual Microsoft.Extensions.Localization.ResourceManagerStringLocalizer CreateResourceManagerStringLocalizer(System.Reflection.Assembly assembly, string baseName) { throw null; } + protected virtual Microsoft.Extensions.Localization.ResourceLocationAttribute GetResourceLocationAttribute(System.Reflection.Assembly assembly) { throw null; } + protected virtual string GetResourcePrefix(System.Reflection.TypeInfo typeInfo) { throw null; } + protected virtual string GetResourcePrefix(System.Reflection.TypeInfo typeInfo, string baseNamespace, string resourcesRelativePath) { throw null; } + protected virtual string GetResourcePrefix(string baseResourceName, string baseNamespace) { throw null; } + protected virtual string GetResourcePrefix(string location, string baseName, string resourceLocation) { throw null; } + protected virtual Microsoft.Extensions.Localization.RootNamespaceAttribute GetRootNamespaceAttribute(System.Reflection.Assembly assembly) { throw null; } + } + [System.ObsoleteAttribute("This method is obsolete. Use `CurrentCulture` and `CurrentUICulture` instead.")] + public partial class ResourceManagerWithCultureStringLocalizer : Microsoft.Extensions.Localization.ResourceManagerStringLocalizer + { + public ResourceManagerWithCultureStringLocalizer(System.Resources.ResourceManager resourceManager, System.Reflection.Assembly resourceAssembly, string baseName, Microsoft.Extensions.Localization.IResourceNamesCache resourceNamesCache, System.Globalization.CultureInfo culture, Microsoft.Extensions.Logging.ILogger logger) : base (default(System.Resources.ResourceManager), default(System.Reflection.Assembly), default(string), default(Microsoft.Extensions.Localization.IResourceNamesCache), default(Microsoft.Extensions.Logging.ILogger)) { } + public override Microsoft.Extensions.Localization.LocalizedString this[string name] { get { throw null; } } + public override Microsoft.Extensions.Localization.LocalizedString this[string name, params object[] arguments] { get { throw null; } } + public override System.Collections.Generic.IEnumerable GetAllStrings(bool includeParentCultures) { throw null; } + } + public partial class ResourceNamesCache : Microsoft.Extensions.Localization.IResourceNamesCache + { + public ResourceNamesCache() { } + public System.Collections.Generic.IList GetOrAdd(string name, System.Func> valueFactory) { throw null; } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly, AllowMultiple=false, Inherited=false)] + public partial class RootNamespaceAttribute : System.Attribute + { + public RootNamespaceAttribute(string rootNamespace) { } + public string RootNamespace { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } +} +namespace Microsoft.Extensions.Localization.Internal +{ + public partial class AssemblyWrapper + { + public AssemblyWrapper(System.Reflection.Assembly assembly) { } + public System.Reflection.Assembly Assembly { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public virtual string FullName { get { throw null; } } + public virtual System.IO.Stream GetManifestResourceStream(string name) { throw null; } + } + public partial interface IResourceStringProvider + { + System.Collections.Generic.IList GetAllResourceStrings(System.Globalization.CultureInfo culture, bool throwOnMissing); + } + public partial class ResourceManagerStringProvider : Microsoft.Extensions.Localization.Internal.IResourceStringProvider + { + public ResourceManagerStringProvider(Microsoft.Extensions.Localization.IResourceNamesCache resourceCache, System.Resources.ResourceManager resourceManager, System.Reflection.Assembly assembly, string baseName) { } + public System.Collections.Generic.IList GetAllResourceStrings(System.Globalization.CultureInfo culture, bool throwOnMissing) { throw null; } + } +} diff --git a/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj b/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj index 5949cc5842ec..86ebaf1970fc 100644 --- a/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj +++ b/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj @@ -3,7 +3,8 @@ Microsoft .NET Extensions Application localization services and default implementation based on ResourceManager to load localized assembly resources. - netstandard2.0 + netstandard2.0;netcoreapp3.0 + netcoreapp3.0 $(NoWarn);CS1591 true localization diff --git a/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj b/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj index 9dea660cb042..628691a220f9 100644 --- a/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj +++ b/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + netstandard2.0;netcoreapp3.0 @@ -9,4 +9,9 @@ + + + + + diff --git a/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.netcoreapp3.0.cs b/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.netcoreapp3.0.cs new file mode 100644 index 000000000000..18cdcbdfa352 --- /dev/null +++ b/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.netcoreapp3.0.cs @@ -0,0 +1,55 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.Extensions.DependencyInjection +{ + public static partial class EncoderServiceCollectionExtensions + { + public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddWebEncoders(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddWebEncoders(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action setupAction) { throw null; } + } +} +namespace Microsoft.Extensions.WebEncoders +{ + public sealed partial class WebEncoderOptions + { + public WebEncoderOptions() { } + public System.Text.Encodings.Web.TextEncoderSettings TextEncoderSettings { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + } +} +namespace Microsoft.Extensions.WebEncoders.Testing +{ + public sealed partial class HtmlTestEncoder : System.Text.Encodings.Web.HtmlEncoder + { + public HtmlTestEncoder() { } + public override int MaxOutputCharactersPerInputCharacter { get { throw null; } } + public override void Encode(System.IO.TextWriter output, char[] value, int startIndex, int characterCount) { } + public override void Encode(System.IO.TextWriter output, string value, int startIndex, int characterCount) { } + public override string Encode(string value) { throw null; } + public unsafe override int FindFirstCharacterToEncode(char* text, int textLength) { throw null; } + public unsafe override bool TryEncodeUnicodeScalar(int unicodeScalar, char* buffer, int bufferLength, out int numberOfCharactersWritten) { throw null; } + public override bool WillEncode(int unicodeScalar) { throw null; } + } + public partial class JavaScriptTestEncoder : System.Text.Encodings.Web.JavaScriptEncoder + { + public JavaScriptTestEncoder() { } + public override int MaxOutputCharactersPerInputCharacter { get { throw null; } } + public override void Encode(System.IO.TextWriter output, char[] value, int startIndex, int characterCount) { } + public override void Encode(System.IO.TextWriter output, string value, int startIndex, int characterCount) { } + public override string Encode(string value) { throw null; } + public unsafe override int FindFirstCharacterToEncode(char* text, int textLength) { throw null; } + public unsafe override bool TryEncodeUnicodeScalar(int unicodeScalar, char* buffer, int bufferLength, out int numberOfCharactersWritten) { throw null; } + public override bool WillEncode(int unicodeScalar) { throw null; } + } + public partial class UrlTestEncoder : System.Text.Encodings.Web.UrlEncoder + { + public UrlTestEncoder() { } + public override int MaxOutputCharactersPerInputCharacter { get { throw null; } } + public override void Encode(System.IO.TextWriter output, char[] value, int startIndex, int characterCount) { } + public override void Encode(System.IO.TextWriter output, string value, int startIndex, int characterCount) { } + public override string Encode(string value) { throw null; } + public unsafe override int FindFirstCharacterToEncode(char* text, int textLength) { throw null; } + public unsafe override bool TryEncodeUnicodeScalar(int unicodeScalar, char* buffer, int bufferLength, out int numberOfCharactersWritten) { throw null; } + public override bool WillEncode(int unicodeScalar) { throw null; } + } +} diff --git a/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj b/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj index cec4a31d8678..2ed725eac7ea 100644 --- a/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj +++ b/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj @@ -2,7 +2,8 @@ Contains registration and configuration APIs to add the core framework encoders to a dependency injection container. - netstandard2.0 + netstandard2.0;netcoreapp3.0 + netcoreapp3.0 $(NoWarn);CS1591 true aspnetcore @@ -13,6 +14,9 @@ + + + From 42eec2cbfa9d8a86c110b7384a9cfbba8a5b0641 Mon Sep 17 00:00:00 2001 From: John Luo Date: Wed, 7 Aug 2019 15:44:03 -0700 Subject: [PATCH 120/183] Fix build break by using API that's available across net472 and netcoreapp3.0 (dotnet/extensions#2157) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/6e96af8eba3bba8f4a8e02e38dd3ac9549f85d34 --- .../Microsoft.JSInterop/test/DotNetDispatcherTest.cs | 8 ++++---- .../Microsoft.JSInterop/test/JSRuntimeBaseTest.cs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs b/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs index 8e9d0dc3b988..282aa0f364f2 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs @@ -250,7 +250,7 @@ public Task EndInvoke_WithSuccessValue() => WithJSRuntime(jsRuntime => DotNetDispatcher.EndInvoke(argsJson); // Assert - Assert.True(task.IsCompletedSuccessfully); + Assert.True(task.IsCompleted && task.Status == TaskStatus.RanToCompletion); var result = task.Result; Assert.Equal(testDTO.StringVal, result.StringVal); Assert.Equal(testDTO.IntVal, result.IntVal); @@ -597,7 +597,7 @@ public void ParseEndInvokeArguments_Works() DotNetDispatcher.ParseEndInvokeArguments(jsRuntime, $"[{jsRuntime.LastInvocationAsyncHandle}, true, {{\"intVal\": 7}}]"); - Assert.True(task.IsCompletedSuccessfully); + Assert.True(task.IsCompleted && task.Status == TaskStatus.RanToCompletion); Assert.Equal(7, task.Result.IntVal); } @@ -609,7 +609,7 @@ public void ParseEndInvokeArguments_WithArrayValue() DotNetDispatcher.ParseEndInvokeArguments(jsRuntime, $"[{jsRuntime.LastInvocationAsyncHandle}, true, [1, 2, 3]]"); - Assert.True(task.IsCompletedSuccessfully); + Assert.True(task.IsCompleted && task.Status == TaskStatus.RanToCompletion); Assert.Equal(new[] { 1, 2, 3 }, task.Result); } @@ -621,7 +621,7 @@ public void ParseEndInvokeArguments_WithNullValue() DotNetDispatcher.ParseEndInvokeArguments(jsRuntime, $"[{jsRuntime.LastInvocationAsyncHandle}, true, null]"); - Assert.True(task.IsCompletedSuccessfully); + Assert.True(task.IsCompleted && task.Status == TaskStatus.RanToCompletion); Assert.Null(task.Result); } diff --git a/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs index 468f8efba36d..2714886f9a28 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs @@ -66,7 +66,7 @@ public void InvokeAsync_CompletesSuccessfullyBeforeTimeout() runtime.EndInvokeJS(2, succeeded: true, ref reader); - Assert.True(task.IsCompletedSuccessfully); + Assert.True(task.IsCompleted && task.Status == TaskStatus.RanToCompletion); } [Fact] From 7dfc2fe46bd3939161247df59da0fb7435449a2f Mon Sep 17 00:00:00 2001 From: Hisham Bin Ateya Date: Fri, 9 Aug 2019 00:32:48 +0300 Subject: [PATCH 121/183] Fix GetResourcePrefix() that causes an issue with using Rootnamespace (dotnet/extensions#2081) * Fix GetResourcePrefix() that causes an issue with using Rootnamespace Update GetResourcePrefix to use baseNamespace to calculate resource prefix rather than assembly name. Addresses issues aspnet/AspNetCoredotnet/extensions#10639\n\nCommit migrated from https://github.com/dotnet/extensions/commit/2303aa23ddf5a2ffff866911a55f4f88782a215b --- .../ResourceManagerStringLocalizerFactory.cs | 7 +- .../AssemblyInfo.cs | 5 + .../Controllers/ValuesController.cs | 9 ++ ...ns.Localization.RootNamespace.Tests.csproj | 26 ++++ .../Controllers/ValuesController.resx | 123 ++++++++++++++++++ .../StringLocalizerOfTRootNamespaceTest.cs | 26 ++++ ...lizationServiceCollectionExtensionsTest.cs | 0 ...osoft.Extensions.Localization.Tests.csproj | 0 .../Model.cs | 8 ++ ...sourceManagerStringLocalizerFactoryTest.cs | 15 ++- .../ResourceManagerStringLocalizerTest.cs | 0 .../StringLocalizerOfTTest.cs | 0 12 files changed, 209 insertions(+), 10 deletions(-) create mode 100644 src/Localization/Localization/test/Microsoft.Extensions.Localization.RootNamespace.Tests/AssemblyInfo.cs create mode 100644 src/Localization/Localization/test/Microsoft.Extensions.Localization.RootNamespace.Tests/Controllers/ValuesController.cs create mode 100644 src/Localization/Localization/test/Microsoft.Extensions.Localization.RootNamespace.Tests/Microsoft.Extensions.Localization.RootNamespace.Tests.csproj create mode 100644 src/Localization/Localization/test/Microsoft.Extensions.Localization.RootNamespace.Tests/Resources/Controllers/ValuesController.resx create mode 100644 src/Localization/Localization/test/Microsoft.Extensions.Localization.RootNamespace.Tests/StringLocalizerOfTRootNamespaceTest.cs rename src/Localization/Localization/test/{ => Microsoft.Extensions.Localization.Tests}/LocalizationServiceCollectionExtensionsTest.cs (100%) rename src/Localization/Localization/test/{ => Microsoft.Extensions.Localization.Tests}/Microsoft.Extensions.Localization.Tests.csproj (100%) create mode 100644 src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests/Model.cs rename src/Localization/Localization/test/{ => Microsoft.Extensions.Localization.Tests}/ResourceManagerStringLocalizerFactoryTest.cs (96%) rename src/Localization/Localization/test/{ => Microsoft.Extensions.Localization.Tests}/ResourceManagerStringLocalizerTest.cs (100%) rename src/Localization/Localization/test/{ => Microsoft.Extensions.Localization.Tests}/StringLocalizerOfTTest.cs (100%) diff --git a/src/Localization/Localization/src/ResourceManagerStringLocalizerFactory.cs b/src/Localization/Localization/src/ResourceManagerStringLocalizerFactory.cs index 2eb737eaa74c..a525f723685d 100644 --- a/src/Localization/Localization/src/ResourceManagerStringLocalizerFactory.cs +++ b/src/Localization/Localization/src/ResourceManagerStringLocalizerFactory.cs @@ -101,9 +101,8 @@ protected virtual string GetResourcePrefix(TypeInfo typeInfo, string baseNamespa else { // This expectation is defined by dotnet's automatic resource storage. - // We have to conform to "{RootNamespace}.{ResourceLocation}.{FullTypeName - AssemblyName}". - var assemblyName = new AssemblyName(typeInfo.Assembly.FullName).Name; - return baseNamespace + "." + resourcesRelativePath + TrimPrefix(typeInfo.FullName, assemblyName + "."); + // We have to conform to "{RootNamespace}.{ResourceLocation}.{FullTypeName - RootNamespace}". + return baseNamespace + "." + resourcesRelativePath + TrimPrefix(typeInfo.FullName, baseNamespace + "."); } } @@ -267,4 +266,4 @@ private static string TrimPrefix(string name, string prefix) return name; } } -} \ No newline at end of file +} diff --git a/src/Localization/Localization/test/Microsoft.Extensions.Localization.RootNamespace.Tests/AssemblyInfo.cs b/src/Localization/Localization/test/Microsoft.Extensions.Localization.RootNamespace.Tests/AssemblyInfo.cs new file mode 100644 index 000000000000..bc7d005571eb --- /dev/null +++ b/src/Localization/Localization/test/Microsoft.Extensions.Localization.RootNamespace.Tests/AssemblyInfo.cs @@ -0,0 +1,5 @@ +using System.Reflection; +using Microsoft.Extensions.Localization; + +[assembly: ResourceLocation("Resources")] +[assembly: RootNamespace("LocalizationTest.Abc")] diff --git a/src/Localization/Localization/test/Microsoft.Extensions.Localization.RootNamespace.Tests/Controllers/ValuesController.cs b/src/Localization/Localization/test/Microsoft.Extensions.Localization.RootNamespace.Tests/Controllers/ValuesController.cs new file mode 100644 index 000000000000..93dad2460a2d --- /dev/null +++ b/src/Localization/Localization/test/Microsoft.Extensions.Localization.RootNamespace.Tests/Controllers/ValuesController.cs @@ -0,0 +1,9 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace LocalizationTest.Abc.Controllers +{ + public class ValuesController + { + } +} diff --git a/src/Localization/Localization/test/Microsoft.Extensions.Localization.RootNamespace.Tests/Microsoft.Extensions.Localization.RootNamespace.Tests.csproj b/src/Localization/Localization/test/Microsoft.Extensions.Localization.RootNamespace.Tests/Microsoft.Extensions.Localization.RootNamespace.Tests.csproj new file mode 100644 index 000000000000..d7a45a84ab2a --- /dev/null +++ b/src/Localization/Localization/test/Microsoft.Extensions.Localization.RootNamespace.Tests/Microsoft.Extensions.Localization.RootNamespace.Tests.csproj @@ -0,0 +1,26 @@ + + + netcoreapp3.0;net472 + LocalizationTest.Abc + + + + + + + + + + + + + + + + + + ResXFileCodeGenerator + ValuesController.Designer.cs + + + diff --git a/src/Localization/Localization/test/Microsoft.Extensions.Localization.RootNamespace.Tests/Resources/Controllers/ValuesController.resx b/src/Localization/Localization/test/Microsoft.Extensions.Localization.RootNamespace.Tests/Resources/Controllers/ValuesController.resx new file mode 100644 index 000000000000..9966ea419e49 --- /dev/null +++ b/src/Localization/Localization/test/Microsoft.Extensions.Localization.RootNamespace.Tests/Resources/Controllers/ValuesController.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ValFromResource + + \ No newline at end of file diff --git a/src/Localization/Localization/test/Microsoft.Extensions.Localization.RootNamespace.Tests/StringLocalizerOfTRootNamespaceTest.cs b/src/Localization/Localization/test/Microsoft.Extensions.Localization.RootNamespace.Tests/StringLocalizerOfTRootNamespaceTest.cs new file mode 100644 index 000000000000..7c892e65dd39 --- /dev/null +++ b/src/Localization/Localization/test/Microsoft.Extensions.Localization.RootNamespace.Tests/StringLocalizerOfTRootNamespaceTest.cs @@ -0,0 +1,26 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using LocalizationTest.Abc.Controllers; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; +using Moq; +using Xunit; + +namespace Microsoft.Extensions.Localization.RootNamespace.Tests +{ + public class StringLocalizerOfTRootNamespaceTest + { + [Fact] + public void RootNamespace() + { + var locOptions = new LocalizationOptions(); + var options = new Mock>(); + options.Setup(o => o.Value).Returns(locOptions); + var factory = new ResourceManagerStringLocalizerFactory(options.Object, NullLoggerFactory.Instance); + + var valuesLoc = factory.Create(typeof(ValuesController)); + Assert.Equal("ValFromResource", valuesLoc["String1"]); + } + } +} diff --git a/src/Localization/Localization/test/LocalizationServiceCollectionExtensionsTest.cs b/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests/LocalizationServiceCollectionExtensionsTest.cs similarity index 100% rename from src/Localization/Localization/test/LocalizationServiceCollectionExtensionsTest.cs rename to src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests/LocalizationServiceCollectionExtensionsTest.cs diff --git a/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests.csproj b/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests/Microsoft.Extensions.Localization.Tests.csproj similarity index 100% rename from src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests.csproj rename to src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests/Microsoft.Extensions.Localization.Tests.csproj diff --git a/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests/Model.cs b/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests/Model.cs new file mode 100644 index 000000000000..9d95c370fdbe --- /dev/null +++ b/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests/Model.cs @@ -0,0 +1,8 @@ +// This namespace for test resources with alternative RootNamespace +namespace MyNamespace +{ + public class Model + { + + } +} \ No newline at end of file diff --git a/src/Localization/Localization/test/ResourceManagerStringLocalizerFactoryTest.cs b/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests/ResourceManagerStringLocalizerFactoryTest.cs similarity index 96% rename from src/Localization/Localization/test/ResourceManagerStringLocalizerFactoryTest.cs rename to src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests/ResourceManagerStringLocalizerFactoryTest.cs index 7a18c0e4bdab..86f6e15ccd4c 100644 --- a/src/Localization/Localization/test/ResourceManagerStringLocalizerFactoryTest.cs +++ b/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests/ResourceManagerStringLocalizerFactoryTest.cs @@ -7,6 +7,7 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; +using MyNamespace; using Moq; using Xunit; @@ -132,7 +133,7 @@ public void Create_ResourceLocationAttribute_RootNamespaceIgnoredWhenNoLocation( var loggerFactory = NullLoggerFactory.Instance; var resourcePath = Path.Combine("My", "Resources"); - var rootNamespace = "MyNamespace"; + var rootNamespace = nameof(MyNamespace); var rootNamespaceAttribute = new RootNamespaceAttribute(rootNamespace); var typeFactory = new TestResourceManagerStringLocalizerFactory( @@ -141,12 +142,13 @@ public void Create_ResourceLocationAttribute_RootNamespaceIgnoredWhenNoLocation( rootNamespaceAttribute: rootNamespaceAttribute, loggerFactory: loggerFactory); - var type = typeof(ResourceManagerStringLocalizerFactoryTest); + var type = typeof(Model); + // Act typeFactory.Create(type); // Assert - Assert.Equal($"Microsoft.Extensions.Localization.Tests.ResourceManagerStringLocalizerFactoryTest", typeFactory.BaseName); + Assert.Equal($"{rootNamespace}.{nameof(Model)}", typeFactory.BaseName); } [Fact] @@ -159,7 +161,7 @@ public void Create_ResourceLocationAttribute_UsesRootNamespace() var loggerFactory = NullLoggerFactory.Instance; var resourcePath = Path.Combine("My", "Resources"); - var rootNamespace = "MyNamespace"; + var rootNamespace = nameof(MyNamespace); var resourceLocationAttribute = new ResourceLocationAttribute(resourcePath); var rootNamespaceAttribute = new RootNamespaceAttribute(rootNamespace); @@ -169,12 +171,13 @@ public void Create_ResourceLocationAttribute_UsesRootNamespace() rootNamespaceAttribute, loggerFactory); - var type = typeof(ResourceManagerStringLocalizerFactoryTest); + var type = typeof(Model); + // Act typeFactory.Create(type); // Assert - Assert.Equal($"MyNamespace.My.Resources.ResourceManagerStringLocalizerFactoryTest", typeFactory.BaseName); + Assert.Equal($"{rootNamespace}.My.Resources.{nameof(Model)}", typeFactory.BaseName); } [Fact] diff --git a/src/Localization/Localization/test/ResourceManagerStringLocalizerTest.cs b/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests/ResourceManagerStringLocalizerTest.cs similarity index 100% rename from src/Localization/Localization/test/ResourceManagerStringLocalizerTest.cs rename to src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests/ResourceManagerStringLocalizerTest.cs diff --git a/src/Localization/Localization/test/StringLocalizerOfTTest.cs b/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests/StringLocalizerOfTTest.cs similarity index 100% rename from src/Localization/Localization/test/StringLocalizerOfTTest.cs rename to src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests/StringLocalizerOfTTest.cs From 6c66664ae688f4a7b5c3124cfc4a0e8c6646b8ae Mon Sep 17 00:00:00 2001 From: John Luo Date: Fri, 9 Aug 2019 14:57:35 -0700 Subject: [PATCH 122/183] Update tfm in master to netcoreap5.0 \n\nCommit migrated from https://github.com/dotnet/extensions/commit/3fa337f230eda6dc8b94a65e65343d777b294011 --- .../src/Microsoft.Extensions.Configuration.KeyPerFile.csproj | 4 ++-- ...ft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj | 4 ++-- .../src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj | 4 ++-- .../Microsoft.JSInterop/src/Microsoft.JSInterop.csproj | 4 ++-- .../src/Microsoft.Extensions.Localization.Abstractions.csproj | 4 ++-- .../Localization/src/Microsoft.Extensions.Localization.csproj | 4 ++-- src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj | 4 ++-- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj b/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj index 10f41fa9f49d..77664416f93a 100644 --- a/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj +++ b/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj @@ -2,8 +2,8 @@ Configuration provider that uses files in a directory for Microsoft.Extensions.Configuration. - netstandard2.0;netcoreapp3.0 - netcoreapp3.0 + netstandard2.0;netcoreapp5.0 + netcoreapp5.0 true true diff --git a/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj b/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj index f41b257c6322..e5cf8c251a90 100644 --- a/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj +++ b/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj @@ -7,8 +7,8 @@ Commonly Used Types Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck Microsoft.Extensions.Diagnostics.HealthChecks - netstandard2.0;netcoreapp3.0 - netcoreapp3.0 + netstandard2.0;netcoreapp5.0 + netcoreapp5.0 $(NoWarn);CS1591 true diagnostics;healthchecks diff --git a/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj b/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj index 40056850113e..d3b3f82b0d0b 100644 --- a/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj +++ b/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj @@ -6,8 +6,8 @@ Commonly Used Types: Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckService Microsoft.Extensions.Diagnostics.HealthChecks.IHealthChecksBuilder - netstandard2.0;netcoreapp3.0 - netcoreapp3.0 + netstandard2.0;netcoreapp5.0 + netcoreapp5.0 $(NoWarn);CS1591 true diagnostics;healthchecks diff --git a/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj b/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj index 044c843b2815..3021d18cc3bf 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj +++ b/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj @@ -1,8 +1,8 @@  - netstandard2.0;netcoreapp3.0 - netcoreapp3.0 + netstandard2.0;netcoreapp5.0 + netcoreapp5.0 Abstractions and features for interop between .NET and JavaScript code. javascript;interop true diff --git a/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj b/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj index 09b8bf65b79e..919d4f3d2803 100644 --- a/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj +++ b/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj @@ -6,8 +6,8 @@ Commonly used types: Microsoft.Extensions.Localization.IStringLocalizer Microsoft.Extensions.Localization.IStringLocalizer<T> - netstandard2.0;netcoreapp3.0 - netcoreapp3.0 + netstandard2.0;netcoreapp5.0 + netcoreapp5.0 $(NoWarn);CS1591 true localization diff --git a/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj b/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj index 86ebaf1970fc..1cfdc34dfb29 100644 --- a/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj +++ b/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj @@ -3,8 +3,8 @@ Microsoft .NET Extensions Application localization services and default implementation based on ResourceManager to load localized assembly resources. - netstandard2.0;netcoreapp3.0 - netcoreapp3.0 + netstandard2.0;netcoreapp5.0 + netcoreapp5.0 $(NoWarn);CS1591 true localization diff --git a/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj b/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj index 2ed725eac7ea..f4c42944130f 100644 --- a/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj +++ b/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj @@ -2,8 +2,8 @@ Contains registration and configuration APIs to add the core framework encoders to a dependency injection container. - netstandard2.0;netcoreapp3.0 - netcoreapp3.0 + netstandard2.0;netcoreapp5.0 + netcoreapp5.0 $(NoWarn);CS1591 true aspnetcore From 4e01ec63483e3cd35e35eeda1b4da43ef183e596 Mon Sep 17 00:00:00 2001 From: John Luo Date: Fri, 9 Aug 2019 15:17:50 -0700 Subject: [PATCH 123/183] Update ref assemblies and missed updates \n\nCommit migrated from https://github.com/dotnet/extensions/commit/6fc17ec76f56d377047e386ea253445fab3581c1 --- ...Extensions.Configuration.KeyPerFile.csproj | 6 +- ....Configuration.KeyPerFile.netcoreapp5.0.cs | 29 ++++++ ...t.Extensions.FileProviders.Embedded.csproj | 6 +- ...ns.FileProviders.Embedded.netcoreapp5.0.cs | 39 ++++++++ ...t.Extensions.FileProviders.Embedded.csproj | 6 +- ....FileProviders.Embedded.multitarget.nuspec | 2 +- ...leProviders.Embedded.netcoreapp5.0.nuspec} | 2 +- ...agnostics.HealthChecks.Abstractions.csproj | 6 +- ...HealthChecks.Abstractions.netcoreapp5.0.cs | 72 ++++++++++++++ ...Extensions.Diagnostics.HealthChecks.csproj | 6 +- ....Diagnostics.HealthChecks.netcoreapp5.0.cs | 59 ++++++++++++ .../ref/Microsoft.JSInterop.csproj | 6 +- .../ref/Microsoft.JSInterop.netcoreapp5.0.cs | 64 +++++++++++++ ...xtensions.Localization.Abstractions.csproj | 6 +- ...Localization.Abstractions.netcoreapp5.0.cs | 49 ++++++++++ .../Microsoft.Extensions.Localization.csproj | 6 +- ...t.Extensions.Localization.netcoreapp5.0.cs | 93 +++++++++++++++++++ .../Microsoft.Extensions.WebEncoders.csproj | 6 +- ...ft.Extensions.WebEncoders.netcoreapp5.0.cs | 55 +++++++++++ 19 files changed, 489 insertions(+), 29 deletions(-) create mode 100644 src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.netcoreapp5.0.cs create mode 100644 src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.netcoreapp5.0.cs rename src/FileProviders/Embedded/src/{Microsoft.Extensions.FileProviders.Embedded.netcoreapp3.0.nuspec => Microsoft.Extensions.FileProviders.Embedded.netcoreapp5.0.nuspec} (94%) create mode 100644 src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netcoreapp5.0.cs create mode 100644 src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netcoreapp5.0.cs create mode 100644 src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netcoreapp5.0.cs create mode 100644 src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netcoreapp5.0.cs create mode 100644 src/Localization/Localization/ref/Microsoft.Extensions.Localization.netcoreapp5.0.cs create mode 100644 src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.netcoreapp5.0.cs diff --git a/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj b/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj index 4e074ab493c9..2ba44d34fa1b 100644 --- a/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj +++ b/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj @@ -1,15 +1,15 @@ - netstandard2.0;netcoreapp3.0 + netstandard2.0;netcoreapp5.0 - - + + diff --git a/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.netcoreapp5.0.cs b/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.netcoreapp5.0.cs new file mode 100644 index 000000000000..e26ca1909d18 --- /dev/null +++ b/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.netcoreapp5.0.cs @@ -0,0 +1,29 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.Extensions.Configuration +{ + public static partial class KeyPerFileConfigurationBuilderExtensions + { + public static Microsoft.Extensions.Configuration.IConfigurationBuilder AddKeyPerFile(this Microsoft.Extensions.Configuration.IConfigurationBuilder builder, System.Action configureSource) { throw null; } + public static Microsoft.Extensions.Configuration.IConfigurationBuilder AddKeyPerFile(this Microsoft.Extensions.Configuration.IConfigurationBuilder builder, string directoryPath, bool optional) { throw null; } + } +} +namespace Microsoft.Extensions.Configuration.KeyPerFile +{ + public partial class KeyPerFileConfigurationProvider : Microsoft.Extensions.Configuration.ConfigurationProvider + { + public KeyPerFileConfigurationProvider(Microsoft.Extensions.Configuration.KeyPerFile.KeyPerFileConfigurationSource source) { } + public override void Load() { } + public override string ToString() { throw null; } + } + public partial class KeyPerFileConfigurationSource : Microsoft.Extensions.Configuration.IConfigurationSource + { + public KeyPerFileConfigurationSource() { } + public Microsoft.Extensions.FileProviders.IFileProvider FileProvider { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public System.Func IgnoreCondition { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public string IgnorePrefix { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public bool Optional { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public Microsoft.Extensions.Configuration.IConfigurationProvider Build(Microsoft.Extensions.Configuration.IConfigurationBuilder builder) { throw null; } + } +} diff --git a/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.csproj b/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.csproj index e9407f9164b0..98d071201c4b 100644 --- a/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.csproj +++ b/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.csproj @@ -1,14 +1,14 @@ - netstandard2.0;netcoreapp3.0 + netstandard2.0;netcoreapp5.0 - - + + diff --git a/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.netcoreapp5.0.cs b/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.netcoreapp5.0.cs new file mode 100644 index 000000000000..1596f191fdd4 --- /dev/null +++ b/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.netcoreapp5.0.cs @@ -0,0 +1,39 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.Extensions.FileProviders +{ + public partial class EmbeddedFileProvider : Microsoft.Extensions.FileProviders.IFileProvider + { + public EmbeddedFileProvider(System.Reflection.Assembly assembly) { } + public EmbeddedFileProvider(System.Reflection.Assembly assembly, string baseNamespace) { } + public Microsoft.Extensions.FileProviders.IDirectoryContents GetDirectoryContents(string subpath) { throw null; } + public Microsoft.Extensions.FileProviders.IFileInfo GetFileInfo(string subpath) { throw null; } + public Microsoft.Extensions.Primitives.IChangeToken Watch(string pattern) { throw null; } + } + public partial class ManifestEmbeddedFileProvider : Microsoft.Extensions.FileProviders.IFileProvider + { + public ManifestEmbeddedFileProvider(System.Reflection.Assembly assembly) { } + public ManifestEmbeddedFileProvider(System.Reflection.Assembly assembly, string root) { } + public ManifestEmbeddedFileProvider(System.Reflection.Assembly assembly, string root, System.DateTimeOffset lastModified) { } + public ManifestEmbeddedFileProvider(System.Reflection.Assembly assembly, string root, string manifestName, System.DateTimeOffset lastModified) { } + public System.Reflection.Assembly Assembly { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public Microsoft.Extensions.FileProviders.IDirectoryContents GetDirectoryContents(string subpath) { throw null; } + public Microsoft.Extensions.FileProviders.IFileInfo GetFileInfo(string subpath) { throw null; } + public Microsoft.Extensions.Primitives.IChangeToken Watch(string filter) { throw null; } + } +} +namespace Microsoft.Extensions.FileProviders.Embedded +{ + public partial class EmbeddedResourceFileInfo : Microsoft.Extensions.FileProviders.IFileInfo + { + public EmbeddedResourceFileInfo(System.Reflection.Assembly assembly, string resourcePath, string name, System.DateTimeOffset lastModified) { } + public bool Exists { get { throw null; } } + public bool IsDirectory { get { throw null; } } + public System.DateTimeOffset LastModified { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public long Length { get { throw null; } } + public string Name { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string PhysicalPath { get { throw null; } } + public System.IO.Stream CreateReadStream() { throw null; } + } +} diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj index 115d9affe14a..3c995639493a 100644 --- a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj @@ -3,10 +3,10 @@ Microsoft.Extensions.FileProviders File provider for files in embedded resources for Microsoft.Extensions.FileProviders. - netstandard2.0;netcoreapp3.0 + netstandard2.0;netcoreapp5.0 $(MSBuildProjectName).multitarget.nuspec - netcoreapp3.0 - $(MSBuildProjectName).netcoreapp3.0.nuspec + netcoreapp5.0 + $(MSBuildProjectName).netcoreapp5.0.nuspec true true diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.multitarget.nuspec b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.multitarget.nuspec index 874c90c79dcc..3d693569e4e5 100644 --- a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.multitarget.nuspec +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.multitarget.nuspec @@ -3,7 +3,7 @@ $CommonMetadataElements$ - + diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.netcoreapp3.0.nuspec b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.netcoreapp5.0.nuspec similarity index 94% rename from src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.netcoreapp3.0.nuspec rename to src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.netcoreapp5.0.nuspec index f98f0b4ad5aa..4bc62a0e6bbb 100644 --- a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.netcoreapp3.0.nuspec +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.netcoreapp5.0.nuspec @@ -3,7 +3,7 @@ $CommonMetadataElements$ - + diff --git a/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj index f7514489fa30..d202fe2d713b 100644 --- a/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj +++ b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj @@ -1,14 +1,14 @@ - netstandard2.0;netcoreapp3.0 + netstandard2.0;netcoreapp5.0 - - + + diff --git a/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netcoreapp5.0.cs b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netcoreapp5.0.cs new file mode 100644 index 000000000000..8c53adc27598 --- /dev/null +++ b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netcoreapp5.0.cs @@ -0,0 +1,72 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.Extensions.Diagnostics.HealthChecks +{ + public sealed partial class HealthCheckContext + { + public HealthCheckContext() { } + public Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckRegistration Registration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + } + public sealed partial class HealthCheckRegistration + { + public HealthCheckRegistration(string name, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck instance, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable tags) { } + public HealthCheckRegistration(string name, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck instance, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable tags, System.TimeSpan? timeout) { } + public HealthCheckRegistration(string name, System.Func factory, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable tags) { } + public HealthCheckRegistration(string name, System.Func factory, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable tags, System.TimeSpan? timeout) { } + public System.Func Factory { get { throw null; } set { } } + public Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus FailureStatus { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public string Name { get { throw null; } set { } } + public System.Collections.Generic.ISet Tags { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.TimeSpan Timeout { get { throw null; } set { } } + } + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] + public partial struct HealthCheckResult + { + private object _dummy; + private int _dummyPrimitive; + public HealthCheckResult(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus status, string description = null, System.Exception exception = null, System.Collections.Generic.IReadOnlyDictionary data = null) { throw null; } + public System.Collections.Generic.IReadOnlyDictionary Data { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string Description { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Exception Exception { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus Status { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public static Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult Degraded(string description = null, System.Exception exception = null, System.Collections.Generic.IReadOnlyDictionary data = null) { throw null; } + public static Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult Healthy(string description = null, System.Collections.Generic.IReadOnlyDictionary data = null) { throw null; } + public static Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult Unhealthy(string description = null, System.Exception exception = null, System.Collections.Generic.IReadOnlyDictionary data = null) { throw null; } + } + public sealed partial class HealthReport + { + public HealthReport(System.Collections.Generic.IReadOnlyDictionary entries, System.TimeSpan totalDuration) { } + public System.Collections.Generic.IReadOnlyDictionary Entries { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus Status { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.TimeSpan TotalDuration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] + public partial struct HealthReportEntry + { + private object _dummy; + private int _dummyPrimitive; + public HealthReportEntry(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus status, string description, System.TimeSpan duration, System.Exception exception, System.Collections.Generic.IReadOnlyDictionary data) { throw null; } + public HealthReportEntry(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus status, string description, System.TimeSpan duration, System.Exception exception, System.Collections.Generic.IReadOnlyDictionary data, System.Collections.Generic.IEnumerable tags = null) { throw null; } + public System.Collections.Generic.IReadOnlyDictionary Data { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string Description { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.TimeSpan Duration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Exception Exception { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus Status { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Collections.Generic.IEnumerable Tags { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } + public enum HealthStatus + { + Unhealthy = 0, + Degraded = 1, + Healthy = 2, + } + public partial interface IHealthCheck + { + System.Threading.Tasks.Task CheckHealthAsync(Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckContext context, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + } + public partial interface IHealthCheckPublisher + { + System.Threading.Tasks.Task PublishAsync(Microsoft.Extensions.Diagnostics.HealthChecks.HealthReport report, System.Threading.CancellationToken cancellationToken); + } +} diff --git a/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj b/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj index 2286087bc786..07f63049eb7a 100644 --- a/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj +++ b/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj @@ -1,7 +1,7 @@ - netstandard2.0;netcoreapp3.0 + netstandard2.0;netcoreapp5.0 @@ -9,8 +9,8 @@ - - + + diff --git a/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netcoreapp5.0.cs b/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netcoreapp5.0.cs new file mode 100644 index 000000000000..a23961efdd26 --- /dev/null +++ b/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netcoreapp5.0.cs @@ -0,0 +1,59 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.Extensions.DependencyInjection +{ + public static partial class HealthChecksBuilderAddCheckExtensions + { + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck instance, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable tags) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck instance, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus = default(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus?), System.Collections.Generic.IEnumerable tags = null, System.TimeSpan? timeout = default(System.TimeSpan?)) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable tags) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus = default(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus?), System.Collections.Generic.IEnumerable tags = null, System.TimeSpan? timeout = default(System.TimeSpan?)) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddTypeActivatedCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable tags, params object[] args) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddTypeActivatedCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable tags, System.TimeSpan timeout, params object[] args) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddTypeActivatedCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, params object[] args) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddTypeActivatedCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, params object[] args) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; } + } + public static partial class HealthChecksBuilderDelegateExtensions + { + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddAsyncCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func> check, System.Collections.Generic.IEnumerable tags) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddAsyncCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func> check, System.Collections.Generic.IEnumerable tags = null, System.TimeSpan? timeout = default(System.TimeSpan?)) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddAsyncCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func> check, System.Collections.Generic.IEnumerable tags) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddAsyncCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func> check, System.Collections.Generic.IEnumerable tags = null, System.TimeSpan? timeout = default(System.TimeSpan?)) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func check, System.Collections.Generic.IEnumerable tags) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func check, System.Collections.Generic.IEnumerable tags = null, System.TimeSpan? timeout = default(System.TimeSpan?)) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func check, System.Collections.Generic.IEnumerable tags) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func check, System.Collections.Generic.IEnumerable tags = null, System.TimeSpan? timeout = default(System.TimeSpan?)) { throw null; } + } + public static partial class HealthCheckServiceCollectionExtensions + { + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddHealthChecks(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) { throw null; } + } + public partial interface IHealthChecksBuilder + { + Microsoft.Extensions.DependencyInjection.IServiceCollection Services { get; } + Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder Add(Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckRegistration registration); + } +} +namespace Microsoft.Extensions.Diagnostics.HealthChecks +{ + public sealed partial class HealthCheckPublisherOptions + { + public HealthCheckPublisherOptions() { } + public System.TimeSpan Delay { get { throw null; } set { } } + public System.TimeSpan Period { get { throw null; } set { } } + public System.Func Predicate { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public System.TimeSpan Timeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + } + public abstract partial class HealthCheckService + { + protected HealthCheckService() { } + public abstract System.Threading.Tasks.Task CheckHealthAsync(System.Func predicate, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + public System.Threading.Tasks.Task CheckHealthAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + } + public sealed partial class HealthCheckServiceOptions + { + public HealthCheckServiceOptions() { } + public System.Collections.Generic.ICollection Registrations { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } +} diff --git a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj index 61980aa92097..ae19552c6d53 100644 --- a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj +++ b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj @@ -1,14 +1,14 @@ - netstandard2.0;netcoreapp3.0 + netstandard2.0;netcoreapp5.0 - - + + diff --git a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netcoreapp5.0.cs b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netcoreapp5.0.cs new file mode 100644 index 000000000000..654ae9d6173a --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netcoreapp5.0.cs @@ -0,0 +1,64 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.JSInterop +{ + public static partial class DotNetDispatcher + { + public static void BeginInvoke(string callId, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { } + public static void EndInvoke(string arguments) { } + public static string Invoke(string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { throw null; } + [Microsoft.JSInterop.JSInvokableAttribute("DotNetDispatcher.ReleaseDotNetObject")] + public static void ReleaseDotNetObject(long dotNetObjectId) { } + } + public static partial class DotNetObjectRef + { + public static Microsoft.JSInterop.DotNetObjectRef Create(TValue value) where TValue : class { throw null; } + } + public sealed partial class DotNetObjectRef : System.IDisposable where TValue : class + { + internal DotNetObjectRef() { } + public TValue Value { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public void Dispose() { } + } + public partial interface IJSInProcessRuntime : Microsoft.JSInterop.IJSRuntime + { + T Invoke(string identifier, params object[] args); + } + public partial interface IJSRuntime + { + System.Threading.Tasks.Task InvokeAsync(string identifier, System.Collections.Generic.IEnumerable args, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + System.Threading.Tasks.Task InvokeAsync(string identifier, params object[] args); + } + public partial class JSException : System.Exception + { + public JSException(string message) { } + public JSException(string message, System.Exception innerException) { } + } + public abstract partial class JSInProcessRuntimeBase : Microsoft.JSInterop.JSRuntimeBase, Microsoft.JSInterop.IJSInProcessRuntime, Microsoft.JSInterop.IJSRuntime + { + protected JSInProcessRuntimeBase() { } + protected abstract string InvokeJS(string identifier, string argsJson); + public TValue Invoke(string identifier, params object[] args) { throw null; } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Method, AllowMultiple=true)] + public partial class JSInvokableAttribute : System.Attribute + { + public JSInvokableAttribute() { } + public JSInvokableAttribute(string identifier) { } + public string Identifier { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } + public static partial class JSRuntime + { + public static void SetCurrentJSRuntime(Microsoft.JSInterop.IJSRuntime instance) { } + } + public abstract partial class JSRuntimeBase : Microsoft.JSInterop.IJSRuntime + { + protected JSRuntimeBase() { } + protected System.TimeSpan? DefaultAsyncTimeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + protected abstract void BeginInvokeJS(long taskId, string identifier, string argsJson); + protected internal abstract void EndInvokeDotNet(string callId, bool success, object resultOrError, string assemblyName, string methodIdentifier, long dotNetObjectId); + public System.Threading.Tasks.Task InvokeAsync(string identifier, System.Collections.Generic.IEnumerable args, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public System.Threading.Tasks.Task InvokeAsync(string identifier, params object[] args) { throw null; } + } +} diff --git a/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.csproj b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.csproj index 6404d5ae8ecb..66e71c05545e 100644 --- a/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.csproj +++ b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.csproj @@ -1,14 +1,14 @@ - netstandard2.0;netcoreapp3.0 + netstandard2.0;netcoreapp5.0 - - + + diff --git a/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netcoreapp5.0.cs b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netcoreapp5.0.cs new file mode 100644 index 000000000000..174cac28e5cb --- /dev/null +++ b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netcoreapp5.0.cs @@ -0,0 +1,49 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.Extensions.Localization +{ + public partial interface IStringLocalizer + { + Microsoft.Extensions.Localization.LocalizedString this[string name] { get; } + Microsoft.Extensions.Localization.LocalizedString this[string name, params object[] arguments] { get; } + System.Collections.Generic.IEnumerable GetAllStrings(bool includeParentCultures); + [System.ObsoleteAttribute("This method is obsolete. Use `CurrentCulture` and `CurrentUICulture` instead.")] + Microsoft.Extensions.Localization.IStringLocalizer WithCulture(System.Globalization.CultureInfo culture); + } + public partial interface IStringLocalizerFactory + { + Microsoft.Extensions.Localization.IStringLocalizer Create(string baseName, string location); + Microsoft.Extensions.Localization.IStringLocalizer Create(System.Type resourceSource); + } + public partial interface IStringLocalizer : Microsoft.Extensions.Localization.IStringLocalizer + { + } + public partial class LocalizedString + { + public LocalizedString(string name, string value) { } + public LocalizedString(string name, string value, bool resourceNotFound) { } + public LocalizedString(string name, string value, bool resourceNotFound, string searchedLocation) { } + public string Name { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public bool ResourceNotFound { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string SearchedLocation { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string Value { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public static implicit operator string (Microsoft.Extensions.Localization.LocalizedString localizedString) { throw null; } + public override string ToString() { throw null; } + } + public static partial class StringLocalizerExtensions + { + public static System.Collections.Generic.IEnumerable GetAllStrings(this Microsoft.Extensions.Localization.IStringLocalizer stringLocalizer) { throw null; } + public static Microsoft.Extensions.Localization.LocalizedString GetString(this Microsoft.Extensions.Localization.IStringLocalizer stringLocalizer, string name) { throw null; } + public static Microsoft.Extensions.Localization.LocalizedString GetString(this Microsoft.Extensions.Localization.IStringLocalizer stringLocalizer, string name, params object[] arguments) { throw null; } + } + public partial class StringLocalizer : Microsoft.Extensions.Localization.IStringLocalizer, Microsoft.Extensions.Localization.IStringLocalizer + { + public StringLocalizer(Microsoft.Extensions.Localization.IStringLocalizerFactory factory) { } + public virtual Microsoft.Extensions.Localization.LocalizedString this[string name] { get { throw null; } } + public virtual Microsoft.Extensions.Localization.LocalizedString this[string name, params object[] arguments] { get { throw null; } } + public System.Collections.Generic.IEnumerable GetAllStrings(bool includeParentCultures) { throw null; } + [System.ObsoleteAttribute("This method is obsolete. Use `CurrentCulture` and `CurrentUICulture` instead.")] + public virtual Microsoft.Extensions.Localization.IStringLocalizer WithCulture(System.Globalization.CultureInfo culture) { throw null; } + } +} diff --git a/src/Localization/Localization/ref/Microsoft.Extensions.Localization.csproj b/src/Localization/Localization/ref/Microsoft.Extensions.Localization.csproj index 735277e75418..2a84077b0671 100644 --- a/src/Localization/Localization/ref/Microsoft.Extensions.Localization.csproj +++ b/src/Localization/Localization/ref/Microsoft.Extensions.Localization.csproj @@ -1,7 +1,7 @@ - netstandard2.0;netcoreapp3.0 + netstandard2.0;netcoreapp5.0 @@ -10,8 +10,8 @@ - - + + diff --git a/src/Localization/Localization/ref/Microsoft.Extensions.Localization.netcoreapp5.0.cs b/src/Localization/Localization/ref/Microsoft.Extensions.Localization.netcoreapp5.0.cs new file mode 100644 index 000000000000..80175da71840 --- /dev/null +++ b/src/Localization/Localization/ref/Microsoft.Extensions.Localization.netcoreapp5.0.cs @@ -0,0 +1,93 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.Extensions.DependencyInjection +{ + public static partial class LocalizationServiceCollectionExtensions + { + public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddLocalization(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddLocalization(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action setupAction) { throw null; } + } +} +namespace Microsoft.Extensions.Localization +{ + public partial interface IResourceNamesCache + { + System.Collections.Generic.IList GetOrAdd(string name, System.Func> valueFactory); + } + public partial class LocalizationOptions + { + public LocalizationOptions() { } + public string ResourcesPath { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly, AllowMultiple=false, Inherited=false)] + public partial class ResourceLocationAttribute : System.Attribute + { + public ResourceLocationAttribute(string resourceLocation) { } + public string ResourceLocation { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } + public partial class ResourceManagerStringLocalizer : Microsoft.Extensions.Localization.IStringLocalizer + { + public ResourceManagerStringLocalizer(System.Resources.ResourceManager resourceManager, Microsoft.Extensions.Localization.Internal.AssemblyWrapper resourceAssemblyWrapper, string baseName, Microsoft.Extensions.Localization.IResourceNamesCache resourceNamesCache, Microsoft.Extensions.Logging.ILogger logger) { } + public ResourceManagerStringLocalizer(System.Resources.ResourceManager resourceManager, Microsoft.Extensions.Localization.Internal.IResourceStringProvider resourceStringProvider, string baseName, Microsoft.Extensions.Localization.IResourceNamesCache resourceNamesCache, Microsoft.Extensions.Logging.ILogger logger) { } + public ResourceManagerStringLocalizer(System.Resources.ResourceManager resourceManager, System.Reflection.Assembly resourceAssembly, string baseName, Microsoft.Extensions.Localization.IResourceNamesCache resourceNamesCache, Microsoft.Extensions.Logging.ILogger logger) { } + public virtual Microsoft.Extensions.Localization.LocalizedString this[string name] { get { throw null; } } + public virtual Microsoft.Extensions.Localization.LocalizedString this[string name, params object[] arguments] { get { throw null; } } + public virtual System.Collections.Generic.IEnumerable GetAllStrings(bool includeParentCultures) { throw null; } + protected System.Collections.Generic.IEnumerable GetAllStrings(bool includeParentCultures, System.Globalization.CultureInfo culture) { throw null; } + protected string GetStringSafely(string name, System.Globalization.CultureInfo culture) { throw null; } + [System.ObsoleteAttribute("This method is obsolete. Use `CurrentCulture` and `CurrentUICulture` instead.")] + public Microsoft.Extensions.Localization.IStringLocalizer WithCulture(System.Globalization.CultureInfo culture) { throw null; } + } + public partial class ResourceManagerStringLocalizerFactory : Microsoft.Extensions.Localization.IStringLocalizerFactory + { + public ResourceManagerStringLocalizerFactory(Microsoft.Extensions.Options.IOptions localizationOptions, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) { } + public Microsoft.Extensions.Localization.IStringLocalizer Create(string baseName, string location) { throw null; } + public Microsoft.Extensions.Localization.IStringLocalizer Create(System.Type resourceSource) { throw null; } + protected virtual Microsoft.Extensions.Localization.ResourceManagerStringLocalizer CreateResourceManagerStringLocalizer(System.Reflection.Assembly assembly, string baseName) { throw null; } + protected virtual Microsoft.Extensions.Localization.ResourceLocationAttribute GetResourceLocationAttribute(System.Reflection.Assembly assembly) { throw null; } + protected virtual string GetResourcePrefix(System.Reflection.TypeInfo typeInfo) { throw null; } + protected virtual string GetResourcePrefix(System.Reflection.TypeInfo typeInfo, string baseNamespace, string resourcesRelativePath) { throw null; } + protected virtual string GetResourcePrefix(string baseResourceName, string baseNamespace) { throw null; } + protected virtual string GetResourcePrefix(string location, string baseName, string resourceLocation) { throw null; } + protected virtual Microsoft.Extensions.Localization.RootNamespaceAttribute GetRootNamespaceAttribute(System.Reflection.Assembly assembly) { throw null; } + } + [System.ObsoleteAttribute("This method is obsolete. Use `CurrentCulture` and `CurrentUICulture` instead.")] + public partial class ResourceManagerWithCultureStringLocalizer : Microsoft.Extensions.Localization.ResourceManagerStringLocalizer + { + public ResourceManagerWithCultureStringLocalizer(System.Resources.ResourceManager resourceManager, System.Reflection.Assembly resourceAssembly, string baseName, Microsoft.Extensions.Localization.IResourceNamesCache resourceNamesCache, System.Globalization.CultureInfo culture, Microsoft.Extensions.Logging.ILogger logger) : base (default(System.Resources.ResourceManager), default(System.Reflection.Assembly), default(string), default(Microsoft.Extensions.Localization.IResourceNamesCache), default(Microsoft.Extensions.Logging.ILogger)) { } + public override Microsoft.Extensions.Localization.LocalizedString this[string name] { get { throw null; } } + public override Microsoft.Extensions.Localization.LocalizedString this[string name, params object[] arguments] { get { throw null; } } + public override System.Collections.Generic.IEnumerable GetAllStrings(bool includeParentCultures) { throw null; } + } + public partial class ResourceNamesCache : Microsoft.Extensions.Localization.IResourceNamesCache + { + public ResourceNamesCache() { } + public System.Collections.Generic.IList GetOrAdd(string name, System.Func> valueFactory) { throw null; } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly, AllowMultiple=false, Inherited=false)] + public partial class RootNamespaceAttribute : System.Attribute + { + public RootNamespaceAttribute(string rootNamespace) { } + public string RootNamespace { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } +} +namespace Microsoft.Extensions.Localization.Internal +{ + public partial class AssemblyWrapper + { + public AssemblyWrapper(System.Reflection.Assembly assembly) { } + public System.Reflection.Assembly Assembly { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public virtual string FullName { get { throw null; } } + public virtual System.IO.Stream GetManifestResourceStream(string name) { throw null; } + } + public partial interface IResourceStringProvider + { + System.Collections.Generic.IList GetAllResourceStrings(System.Globalization.CultureInfo culture, bool throwOnMissing); + } + public partial class ResourceManagerStringProvider : Microsoft.Extensions.Localization.Internal.IResourceStringProvider + { + public ResourceManagerStringProvider(Microsoft.Extensions.Localization.IResourceNamesCache resourceCache, System.Resources.ResourceManager resourceManager, System.Reflection.Assembly assembly, string baseName) { } + public System.Collections.Generic.IList GetAllResourceStrings(System.Globalization.CultureInfo culture, bool throwOnMissing) { throw null; } + } +} diff --git a/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj b/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj index 628691a220f9..3fa06a72801d 100644 --- a/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj +++ b/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj @@ -1,7 +1,7 @@ - netstandard2.0;netcoreapp3.0 + netstandard2.0;netcoreapp5.0 @@ -9,8 +9,8 @@ - - + + diff --git a/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.netcoreapp5.0.cs b/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.netcoreapp5.0.cs new file mode 100644 index 000000000000..18cdcbdfa352 --- /dev/null +++ b/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.netcoreapp5.0.cs @@ -0,0 +1,55 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.Extensions.DependencyInjection +{ + public static partial class EncoderServiceCollectionExtensions + { + public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddWebEncoders(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddWebEncoders(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action setupAction) { throw null; } + } +} +namespace Microsoft.Extensions.WebEncoders +{ + public sealed partial class WebEncoderOptions + { + public WebEncoderOptions() { } + public System.Text.Encodings.Web.TextEncoderSettings TextEncoderSettings { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + } +} +namespace Microsoft.Extensions.WebEncoders.Testing +{ + public sealed partial class HtmlTestEncoder : System.Text.Encodings.Web.HtmlEncoder + { + public HtmlTestEncoder() { } + public override int MaxOutputCharactersPerInputCharacter { get { throw null; } } + public override void Encode(System.IO.TextWriter output, char[] value, int startIndex, int characterCount) { } + public override void Encode(System.IO.TextWriter output, string value, int startIndex, int characterCount) { } + public override string Encode(string value) { throw null; } + public unsafe override int FindFirstCharacterToEncode(char* text, int textLength) { throw null; } + public unsafe override bool TryEncodeUnicodeScalar(int unicodeScalar, char* buffer, int bufferLength, out int numberOfCharactersWritten) { throw null; } + public override bool WillEncode(int unicodeScalar) { throw null; } + } + public partial class JavaScriptTestEncoder : System.Text.Encodings.Web.JavaScriptEncoder + { + public JavaScriptTestEncoder() { } + public override int MaxOutputCharactersPerInputCharacter { get { throw null; } } + public override void Encode(System.IO.TextWriter output, char[] value, int startIndex, int characterCount) { } + public override void Encode(System.IO.TextWriter output, string value, int startIndex, int characterCount) { } + public override string Encode(string value) { throw null; } + public unsafe override int FindFirstCharacterToEncode(char* text, int textLength) { throw null; } + public unsafe override bool TryEncodeUnicodeScalar(int unicodeScalar, char* buffer, int bufferLength, out int numberOfCharactersWritten) { throw null; } + public override bool WillEncode(int unicodeScalar) { throw null; } + } + public partial class UrlTestEncoder : System.Text.Encodings.Web.UrlEncoder + { + public UrlTestEncoder() { } + public override int MaxOutputCharactersPerInputCharacter { get { throw null; } } + public override void Encode(System.IO.TextWriter output, char[] value, int startIndex, int characterCount) { } + public override void Encode(System.IO.TextWriter output, string value, int startIndex, int characterCount) { } + public override string Encode(string value) { throw null; } + public unsafe override int FindFirstCharacterToEncode(char* text, int textLength) { throw null; } + public unsafe override bool TryEncodeUnicodeScalar(int unicodeScalar, char* buffer, int bufferLength, out int numberOfCharactersWritten) { throw null; } + public override bool WillEncode(int unicodeScalar) { throw null; } + } +} From 60778e8dc7d11cd24e2fd368784db47cd2b18ce5 Mon Sep 17 00:00:00 2001 From: Doug Bunting <6431421+dougbu@users.noreply.github.com> Date: Sun, 11 Aug 2019 11:26:50 -0700 Subject: [PATCH 124/183] Fix unintended breaking change - blocking aspnet/AspNetCoredotnet/extensions#1293 dependency update PR - commit 3ec8c35e450c lost `AssemblyName` substitution - tasks assembly in package incorrectly named ".Manifest.Task.dll" \n\nCommit migrated from https://github.com/dotnet/extensions/commit/77403f35be5559b01c7d10074f3670e636c49811 --- .../src/Microsoft.Extensions.FileProviders.Embedded.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj index 115d9affe14a..d36323e3b151 100644 --- a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj @@ -26,7 +26,7 @@ - + From edd5f54bc37af4f6ad444976dd7d6f5fe0ee0733 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Wed, 14 Aug 2019 09:44:33 -0700 Subject: [PATCH 125/183] Special case Disposing DotNetObjectReferences (dotnet/extensions#2176) * Special case Disposing DotNetObjectReferences This removes a public JSInvokable method required for disposing DotNetObjectReferences \n\nCommit migrated from https://github.com/dotnet/extensions/commit/d6bfc28e2104066dc363bf79bed39d6580977b26 --- .../src/src/Microsoft.JSInterop.ts | 11 ++-- .../ref/Microsoft.JSInterop.netcoreapp3.0.cs | 4 +- .../ref/Microsoft.JSInterop.netstandard2.0.cs | 4 +- .../src/DotNetDispatcher.cs | 45 +++++++-------- .../src/DotNetObjectRef.cs | 3 +- .../src/DotNetObjectRefManager.cs | 6 +- .../src/DotNetObjectRefOfT.cs | 56 ++++++++++++++++--- .../src/DotNetObjectReferenceJsonConverter.cs | 4 +- .../src/IDotNetObjectRef.cs | 1 + .../test/DotNetDispatcherTest.cs | 20 ++++++- .../test/DotNetObjectRefTest.cs | 3 +- .../test/JSInProcessRuntimeBaseTest.cs | 6 +- .../test/JSRuntimeBaseTest.cs | 8 +-- 13 files changed, 109 insertions(+), 62 deletions(-) diff --git a/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts b/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts index 60f6f800a6b5..30a91bde4de5 100644 --- a/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts +++ b/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts @@ -66,7 +66,11 @@ module DotNet { } } - function invokePossibleInstanceMethodAsync(assemblyName: string | null, methodIdentifier: string, dotNetObjectId: number | null, args: any[]): Promise { + function invokePossibleInstanceMethodAsync(assemblyName: string | null, methodIdentifier: string, dotNetObjectId: number | null, ...args: any[]): Promise { + if (assemblyName && dotNetObjectId) { + throw new Error(`For instance method calls, assemblyName should be null. Received '${assemblyName}'.`) ; + } + const asyncCallId = nextAsyncCallId++; const resultPromise = new Promise((resolve, reject) => { pendingAsyncCalls[asyncCallId] = { resolve, reject }; @@ -269,10 +273,7 @@ module DotNet { } public dispose() { - const promise = invokeMethodAsync( - 'Microsoft.JSInterop', - 'DotNetDispatcher.ReleaseDotNetObject', - this._id); + const promise = invokePossibleInstanceMethodAsync(null, '__Dispose', this._id); promise.catch(error => console.error(error)); } diff --git a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netcoreapp3.0.cs b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netcoreapp3.0.cs index 654ae9d6173a..e73fa1be698e 100644 --- a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netcoreapp3.0.cs +++ b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netcoreapp3.0.cs @@ -8,8 +8,6 @@ public static partial class DotNetDispatcher public static void BeginInvoke(string callId, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { } public static void EndInvoke(string arguments) { } public static string Invoke(string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { throw null; } - [Microsoft.JSInterop.JSInvokableAttribute("DotNetDispatcher.ReleaseDotNetObject")] - public static void ReleaseDotNetObject(long dotNetObjectId) { } } public static partial class DotNetObjectRef { @@ -18,7 +16,7 @@ public static partial class DotNetObjectRef public sealed partial class DotNetObjectRef : System.IDisposable where TValue : class { internal DotNetObjectRef() { } - public TValue Value { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public TValue Value { get { throw null; } } public void Dispose() { } } public partial interface IJSInProcessRuntime : Microsoft.JSInterop.IJSRuntime diff --git a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs index 654ae9d6173a..e73fa1be698e 100644 --- a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs +++ b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs @@ -8,8 +8,6 @@ public static partial class DotNetDispatcher public static void BeginInvoke(string callId, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { } public static void EndInvoke(string arguments) { } public static string Invoke(string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { throw null; } - [Microsoft.JSInterop.JSInvokableAttribute("DotNetDispatcher.ReleaseDotNetObject")] - public static void ReleaseDotNetObject(long dotNetObjectId) { } } public static partial class DotNetObjectRef { @@ -18,7 +16,7 @@ public static partial class DotNetObjectRef public sealed partial class DotNetObjectRef : System.IDisposable where TValue : class { internal DotNetObjectRef() { } - public TValue Value { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public TValue Value { get { throw null; } } public void Dispose() { } } public partial interface IJSInProcessRuntime : Microsoft.JSInterop.IJSRuntime diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs index 0dfac228a684..e639a33ff211 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs @@ -18,6 +18,7 @@ namespace Microsoft.JSInterop /// public static class DotNetDispatcher { + private const string DisposeDotNetObjectReferenceMethodName = "__Dispose"; internal static readonly JsonEncodedText DotNetObjectRefKey = JsonEncodedText.Encode("__dotNetObject"); private static readonly ConcurrentDictionary> _cachedMethodsByAssembly @@ -38,7 +39,7 @@ public static string Invoke(string assemblyName, string methodIdentifier, long d // the targeted method has [JSInvokable]. It is not itself subject to that restriction, // because there would be nobody to police that. This method *is* the police. - var targetInstance = (object)null; + IDotNetObjectRef targetInstance = default; if (dotNetObjectId != default) { targetInstance = DotNetObjectRefManager.Current.FindDotNetObject(dotNetObjectId); @@ -78,7 +79,7 @@ public static void BeginInvoke(string callId, string assemblyName, string method // original stack traces. object syncResult = null; ExceptionDispatchInfo syncException = null; - object targetInstance = null; + IDotNetObjectRef targetInstance = null; try { @@ -127,21 +128,28 @@ public static void BeginInvoke(string callId, string assemblyName, string method } } - private static object InvokeSynchronously(string assemblyName, string methodIdentifier, object targetInstance, string argsJson) + private static object InvokeSynchronously(string assemblyName, string methodIdentifier, IDotNetObjectRef objectReference, string argsJson) { AssemblyKey assemblyKey; - if (targetInstance != null) + if (objectReference is null) + { + assemblyKey = new AssemblyKey(assemblyName); + } + else { if (assemblyName != null) { throw new ArgumentException($"For instance method calls, '{nameof(assemblyName)}' should be null. Value received: '{assemblyName}'."); } - assemblyKey = new AssemblyKey(targetInstance.GetType().Assembly); - } - else - { - assemblyKey = new AssemblyKey(assemblyName); + if (string.Equals(DisposeDotNetObjectReferenceMethodName, methodIdentifier, StringComparison.Ordinal)) + { + // The client executed dotNetObjectReference.dispose(). Dispose the reference and exit. + objectReference.Dispose(); + return default; + } + + assemblyKey = new AssemblyKey(objectReference.Value.GetType().Assembly); } var (methodInfo, parameterTypes) = GetCachedMethodInfo(assemblyKey, methodIdentifier); @@ -150,7 +158,8 @@ private static object InvokeSynchronously(string assemblyName, string methodIden try { - return methodInfo.Invoke(targetInstance, suppliedArgs); + // objectReference will be null if this call invokes a static JSInvokable method. + return methodInfo.Invoke(objectReference?.Value, suppliedArgs); } catch (TargetInvocationException tie) // Avoid using exception filters for AOT runtime support { @@ -280,22 +289,6 @@ internal static void ParseEndInvokeArguments(JSRuntimeBase jsRuntimeBase, string } } - /// - /// Releases the reference to the specified .NET object. This allows the .NET runtime - /// to garbage collect that object if there are no other references to it. - /// - /// To avoid leaking memory, the JavaScript side code must call this for every .NET - /// object it obtains a reference to. The exception is if that object is used for - /// the entire lifetime of a given user's session, in which case it is released - /// automatically when the JavaScript runtime is disposed. - /// - /// The identifier previously passed to JavaScript code. - [JSInvokable(nameof(DotNetDispatcher) + "." + nameof(ReleaseDotNetObject))] - public static void ReleaseDotNetObject(long dotNetObjectId) - { - DotNetObjectRefManager.Current.ReleaseDotNetObject(dotNetObjectId); - } - private static (MethodInfo, Type[]) GetCachedMethodInfo(AssemblyKey assemblyKey, string methodIdentifier) { if (string.IsNullOrWhiteSpace(assemblyKey.AssemblyName)) diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRef.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRef.cs index af790281e9e6..f604bab272c8 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRef.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRef.cs @@ -15,8 +15,7 @@ public static class DotNetObjectRef /// An instance of . public static DotNetObjectRef Create(TValue value) where TValue : class { - var objectId = DotNetObjectRefManager.Current.TrackObject(value); - return new DotNetObjectRef(objectId, value); + return new DotNetObjectRef(DotNetObjectRefManager.Current, value); } } } diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRefManager.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRefManager.cs index ad1469e38f27..a6be6aeb4ffe 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRefManager.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRefManager.cs @@ -10,7 +10,7 @@ namespace Microsoft.JSInterop internal class DotNetObjectRefManager { private long _nextId = 0; // 0 signals no object, but we increment prior to assignment. The first tracked object should have id 1 - private readonly ConcurrentDictionary _trackedRefsById = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _trackedRefsById = new ConcurrentDictionary(); public static DotNetObjectRefManager Current { @@ -25,7 +25,7 @@ public static DotNetObjectRefManager Current } } - public long TrackObject(object dotNetObjectRef) + public long TrackObject(IDotNetObjectRef dotNetObjectRef) { var dotNetObjectId = Interlocked.Increment(ref _nextId); _trackedRefsById[dotNetObjectId] = dotNetObjectRef; @@ -33,7 +33,7 @@ public long TrackObject(object dotNetObjectRef) return dotNetObjectId; } - public object FindDotNetObject(long dotNetObjectId) + public IDotNetObjectRef FindDotNetObject(long dotNetObjectId) { return _trackedRefsById.TryGetValue(dotNetObjectId, out var dotNetObjectRef) ? dotNetObjectRef diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRefOfT.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRefOfT.cs index be6bf9166365..d83d0e89bb5f 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRefOfT.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRefOfT.cs @@ -16,23 +16,53 @@ namespace Microsoft.JSInterop [JsonConverter(typeof(DotNetObjectReferenceJsonConverterFactory))] public sealed class DotNetObjectRef : IDotNetObjectRef, IDisposable where TValue : class { + private readonly DotNetObjectRefManager _referenceManager; + private readonly TValue _value; + private readonly long _objectId; + /// /// Initializes a new instance of . /// - /// The object Id. + /// /// The value to pass by reference. - internal DotNetObjectRef(long objectId, TValue value) + internal DotNetObjectRef(DotNetObjectRefManager referenceManager, TValue value) + { + _referenceManager = referenceManager; + _objectId = _referenceManager.TrackObject(this); + _value = value; + } + + internal DotNetObjectRef(DotNetObjectRefManager referenceManager, long objectId, TValue value) { - ObjectId = objectId; - Value = value; + _referenceManager = referenceManager; + _objectId = objectId; + _value = value; } /// /// Gets the object instance represented by this wrapper. /// - public TValue Value { get; } + public TValue Value + { + get + { + ThrowIfDisposed(); + return _value; + } + } - internal long ObjectId { get; } + internal long ObjectId + { + get + { + ThrowIfDisposed(); + return _objectId; + } + } + + object IDotNetObjectRef.Value => Value; + + internal bool Disposed { get; private set; } /// /// Stops tracking this object reference, allowing it to be garbage collected @@ -41,7 +71,19 @@ internal DotNetObjectRef(long objectId, TValue value) /// public void Dispose() { - DotNetObjectRefManager.Current.ReleaseDotNetObject(ObjectId); + if (!Disposed) + { + Disposed = true; + _referenceManager.ReleaseDotNetObject(_objectId); + } + } + + private void ThrowIfDisposed() + { + if (Disposed) + { + throw new ObjectDisposedException(GetType().Name); + } } } } diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceJsonConverter.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceJsonConverter.cs index eaabdbf9e6e7..71bfa28ad5e4 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceJsonConverter.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceJsonConverter.cs @@ -40,8 +40,8 @@ public override DotNetObjectRef Read(ref Utf8JsonReader reader, Type typ throw new JsonException($"Required property {DotNetObjectRefKey} not found."); } - var value = (TValue)DotNetObjectRefManager.Current.FindDotNetObject(dotNetObjectId); - return new DotNetObjectRef(dotNetObjectId, value); + var referenceManager = DotNetObjectRefManager.Current; + return (DotNetObjectRef)referenceManager.FindDotNetObject(dotNetObjectId); } public override void Write(Utf8JsonWriter writer, DotNetObjectRef value, JsonSerializerOptions options) diff --git a/src/JSInterop/Microsoft.JSInterop/src/IDotNetObjectRef.cs b/src/JSInterop/Microsoft.JSInterop/src/IDotNetObjectRef.cs index b082d0ce105a..da16fa60a094 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/IDotNetObjectRef.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/IDotNetObjectRef.cs @@ -7,5 +7,6 @@ namespace Microsoft.JSInterop { internal interface IDotNetObjectRef : IDisposable { + object Value { get; } } } diff --git a/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs b/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs index 282aa0f364f2..13e01c230451 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs @@ -142,7 +142,7 @@ public Task CanInvokeStaticWithParams() => WithJSRuntime(jsRuntime => Assert.False(resultDto2Ref.TryGetProperty(nameof(TestDTO.IntVal), out _)); Assert.True(resultDto2Ref.TryGetProperty(DotNetDispatcher.DotNetObjectRefKey.EncodedUtf8Bytes, out var property)); - var resultDto2 = Assert.IsType(DotNetObjectRefManager.Current.FindDotNetObject(property.GetInt64())); + var resultDto2 = Assert.IsType>(DotNetObjectRefManager.Current.FindDotNetObject(property.GetInt64())).Value; Assert.Equal("MY STRING", resultDto2.StringVal); Assert.Equal(1299, resultDto2.IntVal); }); @@ -202,6 +202,20 @@ public Task CanInvokeBaseInstanceVoidMethod() => WithJSRuntime(jsRuntime => Assert.True(targetInstance.DidInvokeMyBaseClassInvocableInstanceVoid); }); + [Fact] + public Task DotNetObjectReferencesCanBeDisposed() => WithJSRuntime(jsRuntime => + { + // Arrange + var targetInstance = new SomePublicType(); + var objectRef = DotNetObjectRef.Create(targetInstance); + + // Act + DotNetDispatcher.BeginInvoke(null, null, "__Dispose", objectRef.ObjectId, null); + + // Assert + Assert.True(objectRef.Disposed); + }); + [Fact] public Task CannotUseDotNetObjectRefAfterDisposal() => WithJSRuntime(jsRuntime => { @@ -230,7 +244,7 @@ public Task CannotUseDotNetObjectRefAfterReleaseDotNetObject() => WithJSRuntime( var targetInstance = new SomePublicType(); var objectRef = DotNetObjectRef.Create(targetInstance); jsRuntime.Invoke("unimportant", objectRef); - DotNetDispatcher.ReleaseDotNetObject(1); + objectRef.Dispose(); // Act/Assert var ex = Assert.Throws( @@ -320,7 +334,7 @@ public Task CanInvokeInstanceMethodWithParams() => WithJSRuntime(jsRuntime => // Assert Assert.Equal("[\"You passed myvalue\",{\"__dotNetObject\":3}]", resultJson); - var resultDto = (TestDTO)jsRuntime.ObjectRefManager.FindDotNetObject(3); + var resultDto = ((DotNetObjectRef)jsRuntime.ObjectRefManager.FindDotNetObject(3)).Value; Assert.Equal(1235, resultDto.IntVal); Assert.Equal("MY STRING", resultDto.StringVal); }); diff --git a/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectRefTest.cs b/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectRefTest.cs index 112363869eb7..22cb471f28c4 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectRefTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectRefTest.cs @@ -24,10 +24,11 @@ public Task NotifiesAssociatedJsRuntimeOfDisposal() => WithJSRuntime(jsRuntime = var objRef = DotNetObjectRef.Create(new object()); // Act + Assert.Equal(1, objRef.ObjectId); objRef.Dispose(); // Assert - var ex = Assert.Throws(() => jsRuntime.ObjectRefManager.FindDotNetObject(objRef.ObjectId)); + var ex = Assert.Throws(() => jsRuntime.ObjectRefManager.FindDotNetObject(1)); Assert.StartsWith("There is no tracked object with id '1'.", ex.Message); }); } diff --git a/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeBaseTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeBaseTest.cs index a8d551e94e09..d71969d450fe 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeBaseTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeBaseTest.cs @@ -60,9 +60,9 @@ public void SerializesDotNetObjectWrappersInKnownFormat() Assert.Equal("[{\"__dotNetObject\":1},{\"obj2\":{\"__dotNetObject\":2},\"obj3\":{\"__dotNetObject\":3}}]", call.ArgsJson); // Assert: Objects were tracked - Assert.Same(obj1, runtime.ObjectRefManager.FindDotNetObject(1)); - Assert.Same(obj2, runtime.ObjectRefManager.FindDotNetObject(2)); - Assert.Same(obj3, runtime.ObjectRefManager.FindDotNetObject(3)); + Assert.Same(obj1, runtime.ObjectRefManager.FindDotNetObject(1).Value); + Assert.Same(obj2, runtime.ObjectRefManager.FindDotNetObject(2).Value); + Assert.Same(obj3, runtime.ObjectRefManager.FindDotNetObject(3).Value); } [Fact] diff --git a/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs index 2714886f9a28..c3bf4f9eeffa 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs @@ -282,10 +282,10 @@ public void SerializesDotNetObjectWrappersInKnownFormat() Assert.Equal("[{\"__dotNetObject\":1},{\"obj2\":{\"__dotNetObject\":3},\"obj3\":{\"__dotNetObject\":4},\"obj1SameRef\":{\"__dotNetObject\":1},\"obj1DifferentRef\":{\"__dotNetObject\":2}}]", call.ArgsJson); // Assert: Objects were tracked - Assert.Same(obj1, runtime.ObjectRefManager.FindDotNetObject(1)); - Assert.Same(obj1, runtime.ObjectRefManager.FindDotNetObject(2)); - Assert.Same(obj2, runtime.ObjectRefManager.FindDotNetObject(3)); - Assert.Same(obj3, runtime.ObjectRefManager.FindDotNetObject(4)); + Assert.Same(obj1, runtime.ObjectRefManager.FindDotNetObject(1).Value); + Assert.Same(obj1, runtime.ObjectRefManager.FindDotNetObject(2).Value); + Assert.Same(obj2, runtime.ObjectRefManager.FindDotNetObject(3).Value); + Assert.Same(obj3, runtime.ObjectRefManager.FindDotNetObject(4).Value); } [Fact] From 9372816b7c436e06da6d6426e0863b3677aac83c Mon Sep 17 00:00:00 2001 From: Pranav K Date: Wed, 14 Aug 2019 12:24:35 -0700 Subject: [PATCH 126/183] API Review: JSRuntime (dotnet/extensions#2166) * API Review: JSRuntime * Rename JSRuntimeBase -> JSRuntime * Rename JSInProcessRuntimeBase -> JSInProcessRuntime * Rename DotNetObjectRef -> DotNetObjectReference * Update JSRuntime to return ValueTask * Make InvokeAsync APIs that explicitly cancels and API that default cancels more crisp * Introduce void invoking APIs * Fixup method names on DotNetDispatcher \n\nCommit migrated from https://github.com/dotnet/extensions/commit/93d3ae448551cac29af8cf882b31047f5da0dadc --- .../ref/Microsoft.JSInterop.netcoreapp3.0.cs | 63 +-- .../ref/Microsoft.JSInterop.netstandard2.0.cs | 63 +-- ...tObjectRef.cs => DotNetObjectReference.cs} | 14 +- ...tRefOfT.cs => DotNetObjectReferenceOfT.cs} | 18 +- .../Microsoft.JSInterop/src/IJSRuntime.cs | 14 +- .../{ => Infrastructure}/DotNetDispatcher.cs | 28 +- .../DotNetObjectReferenceJsonConverter.cs | 12 +- ...NetObjectReferenceJsonConverterFactory.cs} | 4 +- .../DotNetObjectReferenceManager.cs} | 20 +- .../IDotNetObjectReference.cs} | 4 +- .../{ => Infrastructure}/TaskGenericsUtil.cs | 2 +- ...ssRuntimeBase.cs => JSInProcessRuntime.cs} | 2 +- .../src/JSInProcessRuntimeExtensions.cs | 29 ++ .../src/JSInvokableAttribute.cs | 2 +- .../Microsoft.JSInterop/src/JSRuntime.cs | 169 +++++++- .../Microsoft.JSInterop/src/JSRuntimeBase.cs | 174 -------- .../src/JSRuntimeExtensions.cs | 140 +++++++ ...efTest.cs => DotNetObjectReferenceTest.cs} | 6 +- .../DotNetDispatcherTest.cs | 78 ++-- .../DotNetObjectReferenceJsonConverterTest.cs | 34 +- .../test/JSInProcessRuntimeExtensionsTest.cs | 27 ++ ...eBaseTest.cs => JSInProcessRuntimeTest.cs} | 18 +- .../test/JSRuntimeBaseTest.cs | 386 ------------------ .../test/JSRuntimeExtensionsTest.cs | 181 ++++++++ .../Microsoft.JSInterop/test/JSRuntimeTest.cs | 380 ++++++++++++++++- .../Microsoft.JSInterop/test/TestJSRuntime.cs | 4 +- ...Mono.WebAssembly.Interop.netstandard2.0.cs | 2 +- .../src/MonoWebAssemblyJSRuntime.cs | 7 +- 28 files changed, 1133 insertions(+), 748 deletions(-) rename src/JSInterop/Microsoft.JSInterop/src/{DotNetObjectRef.cs => DotNetObjectReference.cs} (54%) rename src/JSInterop/Microsoft.JSInterop/src/{DotNetObjectRefOfT.cs => DotNetObjectReferenceOfT.cs} (80%) rename src/JSInterop/Microsoft.JSInterop/src/{ => Infrastructure}/DotNetDispatcher.cs (94%) rename src/JSInterop/Microsoft.JSInterop/src/{ => Infrastructure}/DotNetObjectReferenceJsonConverter.cs (77%) rename src/JSInterop/Microsoft.JSInterop/src/{DotNetObjectRefJsonConverterFactory.cs => Infrastructure/DotNetObjectReferenceJsonConverterFactory.cs} (90%) rename src/JSInterop/Microsoft.JSInterop/src/{DotNetObjectRefManager.cs => Infrastructure/DotNetObjectReferenceManager.cs} (74%) rename src/JSInterop/Microsoft.JSInterop/src/{IDotNetObjectRef.cs => Infrastructure/IDotNetObjectReference.cs} (68%) rename src/JSInterop/Microsoft.JSInterop/src/{ => Infrastructure}/TaskGenericsUtil.cs (98%) rename src/JSInterop/Microsoft.JSInterop/src/{JSInProcessRuntimeBase.cs => JSInProcessRuntime.cs} (95%) create mode 100644 src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntimeExtensions.cs delete mode 100644 src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs create mode 100644 src/JSInterop/Microsoft.JSInterop/src/JSRuntimeExtensions.cs rename src/JSInterop/Microsoft.JSInterop/test/{DotNetObjectRefTest.cs => DotNetObjectReferenceTest.cs} (83%) rename src/JSInterop/Microsoft.JSInterop/test/{ => Infrastructure}/DotNetDispatcherTest.cs (91%) rename src/JSInterop/Microsoft.JSInterop/test/{ => Infrastructure}/DotNetObjectReferenceJsonConverterTest.cs (80%) create mode 100644 src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeExtensionsTest.cs rename src/JSInterop/Microsoft.JSInterop/test/{JSInProcessRuntimeBaseTest.cs => JSInProcessRuntimeTest.cs} (88%) delete mode 100644 src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs create mode 100644 src/JSInterop/Microsoft.JSInterop/test/JSRuntimeExtensionsTest.cs diff --git a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netcoreapp3.0.cs b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netcoreapp3.0.cs index e73fa1be698e..a5fbbc768a1e 100644 --- a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netcoreapp3.0.cs +++ b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netcoreapp3.0.cs @@ -3,19 +3,13 @@ namespace Microsoft.JSInterop { - public static partial class DotNetDispatcher + public static partial class DotNetObjectReference { - public static void BeginInvoke(string callId, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { } - public static void EndInvoke(string arguments) { } - public static string Invoke(string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { throw null; } + public static Microsoft.JSInterop.DotNetObjectReference Create(TValue value) where TValue : class { throw null; } } - public static partial class DotNetObjectRef + public sealed partial class DotNetObjectReference : System.IDisposable where TValue : class { - public static Microsoft.JSInterop.DotNetObjectRef Create(TValue value) where TValue : class { throw null; } - } - public sealed partial class DotNetObjectRef : System.IDisposable where TValue : class - { - internal DotNetObjectRef() { } + internal DotNetObjectReference() { } public TValue Value { get { throw null; } } public void Dispose() { } } @@ -25,38 +19,61 @@ public partial interface IJSInProcessRuntime : Microsoft.JSInterop.IJSRuntime } public partial interface IJSRuntime { - System.Threading.Tasks.Task InvokeAsync(string identifier, System.Collections.Generic.IEnumerable args, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); - System.Threading.Tasks.Task InvokeAsync(string identifier, params object[] args); + System.Threading.Tasks.ValueTask InvokeAsync(string identifier, object[] args); + System.Threading.Tasks.ValueTask InvokeAsync(string identifier, System.Threading.CancellationToken cancellationToken, object[] args); } public partial class JSException : System.Exception { public JSException(string message) { } public JSException(string message, System.Exception innerException) { } } - public abstract partial class JSInProcessRuntimeBase : Microsoft.JSInterop.JSRuntimeBase, Microsoft.JSInterop.IJSInProcessRuntime, Microsoft.JSInterop.IJSRuntime + public abstract partial class JSInProcessRuntime : Microsoft.JSInterop.JSRuntime, Microsoft.JSInterop.IJSInProcessRuntime, Microsoft.JSInterop.IJSRuntime { - protected JSInProcessRuntimeBase() { } + protected JSInProcessRuntime() { } protected abstract string InvokeJS(string identifier, string argsJson); public TValue Invoke(string identifier, params object[] args) { throw null; } } + public static partial class JSInProcessRuntimeExtensions + { + public static void InvokeVoid(this Microsoft.JSInterop.IJSInProcessRuntime jsRuntime, string identifier, params object[] args) { } + } [System.AttributeUsageAttribute(System.AttributeTargets.Method, AllowMultiple=true)] - public partial class JSInvokableAttribute : System.Attribute + public sealed partial class JSInvokableAttribute : System.Attribute { public JSInvokableAttribute() { } public JSInvokableAttribute(string identifier) { } public string Identifier { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } } - public static partial class JSRuntime - { - public static void SetCurrentJSRuntime(Microsoft.JSInterop.IJSRuntime instance) { } - } - public abstract partial class JSRuntimeBase : Microsoft.JSInterop.IJSRuntime + public abstract partial class JSRuntime : Microsoft.JSInterop.IJSRuntime { - protected JSRuntimeBase() { } + protected JSRuntime() { } protected System.TimeSpan? DefaultAsyncTimeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } protected abstract void BeginInvokeJS(long taskId, string identifier, string argsJson); protected internal abstract void EndInvokeDotNet(string callId, bool success, object resultOrError, string assemblyName, string methodIdentifier, long dotNetObjectId); - public System.Threading.Tasks.Task InvokeAsync(string identifier, System.Collections.Generic.IEnumerable args, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - public System.Threading.Tasks.Task InvokeAsync(string identifier, params object[] args) { throw null; } + public System.Threading.Tasks.ValueTask InvokeAsync(string identifier, object[] args) { throw null; } + public System.Threading.Tasks.ValueTask InvokeAsync(string identifier, System.Threading.CancellationToken cancellationToken, object[] args) { throw null; } + public static void SetCurrentJSRuntime(Microsoft.JSInterop.IJSRuntime instance) { } + } + public static partial class JSRuntimeExtensions + { + public static System.Threading.Tasks.ValueTask InvokeAsync(this Microsoft.JSInterop.IJSRuntime jsRuntime, string identifier, params object[] args) { throw null; } + public static System.Threading.Tasks.ValueTask InvokeAsync(this Microsoft.JSInterop.IJSRuntime jsRuntime, string identifier, System.Threading.CancellationToken cancellationToken, params object[] args) { throw null; } + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.ValueTask InvokeAsync(this Microsoft.JSInterop.IJSRuntime jsRuntime, string identifier, System.TimeSpan timeout, params object[] args) { throw null; } + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.ValueTask InvokeVoidAsync(this Microsoft.JSInterop.IJSRuntime jsRuntime, string identifier, params object[] args) { throw null; } + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.ValueTask InvokeVoidAsync(this Microsoft.JSInterop.IJSRuntime jsRuntime, string identifier, System.Threading.CancellationToken cancellationToken, params object[] args) { throw null; } + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.ValueTask InvokeVoidAsync(this Microsoft.JSInterop.IJSRuntime jsRuntime, string identifier, System.TimeSpan timeout, params object[] args) { throw null; } + } +} +namespace Microsoft.JSInterop.Infrastructure +{ + public static partial class DotNetDispatcher + { + public static void BeginInvokeDotNet(string callId, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { } + public static void EndInvokeJS(string arguments) { } + public static string Invoke(string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { throw null; } } } diff --git a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs index e73fa1be698e..a5fbbc768a1e 100644 --- a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs +++ b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs @@ -3,19 +3,13 @@ namespace Microsoft.JSInterop { - public static partial class DotNetDispatcher + public static partial class DotNetObjectReference { - public static void BeginInvoke(string callId, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { } - public static void EndInvoke(string arguments) { } - public static string Invoke(string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { throw null; } + public static Microsoft.JSInterop.DotNetObjectReference Create(TValue value) where TValue : class { throw null; } } - public static partial class DotNetObjectRef + public sealed partial class DotNetObjectReference : System.IDisposable where TValue : class { - public static Microsoft.JSInterop.DotNetObjectRef Create(TValue value) where TValue : class { throw null; } - } - public sealed partial class DotNetObjectRef : System.IDisposable where TValue : class - { - internal DotNetObjectRef() { } + internal DotNetObjectReference() { } public TValue Value { get { throw null; } } public void Dispose() { } } @@ -25,38 +19,61 @@ public partial interface IJSInProcessRuntime : Microsoft.JSInterop.IJSRuntime } public partial interface IJSRuntime { - System.Threading.Tasks.Task InvokeAsync(string identifier, System.Collections.Generic.IEnumerable args, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); - System.Threading.Tasks.Task InvokeAsync(string identifier, params object[] args); + System.Threading.Tasks.ValueTask InvokeAsync(string identifier, object[] args); + System.Threading.Tasks.ValueTask InvokeAsync(string identifier, System.Threading.CancellationToken cancellationToken, object[] args); } public partial class JSException : System.Exception { public JSException(string message) { } public JSException(string message, System.Exception innerException) { } } - public abstract partial class JSInProcessRuntimeBase : Microsoft.JSInterop.JSRuntimeBase, Microsoft.JSInterop.IJSInProcessRuntime, Microsoft.JSInterop.IJSRuntime + public abstract partial class JSInProcessRuntime : Microsoft.JSInterop.JSRuntime, Microsoft.JSInterop.IJSInProcessRuntime, Microsoft.JSInterop.IJSRuntime { - protected JSInProcessRuntimeBase() { } + protected JSInProcessRuntime() { } protected abstract string InvokeJS(string identifier, string argsJson); public TValue Invoke(string identifier, params object[] args) { throw null; } } + public static partial class JSInProcessRuntimeExtensions + { + public static void InvokeVoid(this Microsoft.JSInterop.IJSInProcessRuntime jsRuntime, string identifier, params object[] args) { } + } [System.AttributeUsageAttribute(System.AttributeTargets.Method, AllowMultiple=true)] - public partial class JSInvokableAttribute : System.Attribute + public sealed partial class JSInvokableAttribute : System.Attribute { public JSInvokableAttribute() { } public JSInvokableAttribute(string identifier) { } public string Identifier { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } } - public static partial class JSRuntime - { - public static void SetCurrentJSRuntime(Microsoft.JSInterop.IJSRuntime instance) { } - } - public abstract partial class JSRuntimeBase : Microsoft.JSInterop.IJSRuntime + public abstract partial class JSRuntime : Microsoft.JSInterop.IJSRuntime { - protected JSRuntimeBase() { } + protected JSRuntime() { } protected System.TimeSpan? DefaultAsyncTimeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } protected abstract void BeginInvokeJS(long taskId, string identifier, string argsJson); protected internal abstract void EndInvokeDotNet(string callId, bool success, object resultOrError, string assemblyName, string methodIdentifier, long dotNetObjectId); - public System.Threading.Tasks.Task InvokeAsync(string identifier, System.Collections.Generic.IEnumerable args, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - public System.Threading.Tasks.Task InvokeAsync(string identifier, params object[] args) { throw null; } + public System.Threading.Tasks.ValueTask InvokeAsync(string identifier, object[] args) { throw null; } + public System.Threading.Tasks.ValueTask InvokeAsync(string identifier, System.Threading.CancellationToken cancellationToken, object[] args) { throw null; } + public static void SetCurrentJSRuntime(Microsoft.JSInterop.IJSRuntime instance) { } + } + public static partial class JSRuntimeExtensions + { + public static System.Threading.Tasks.ValueTask InvokeAsync(this Microsoft.JSInterop.IJSRuntime jsRuntime, string identifier, params object[] args) { throw null; } + public static System.Threading.Tasks.ValueTask InvokeAsync(this Microsoft.JSInterop.IJSRuntime jsRuntime, string identifier, System.Threading.CancellationToken cancellationToken, params object[] args) { throw null; } + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.ValueTask InvokeAsync(this Microsoft.JSInterop.IJSRuntime jsRuntime, string identifier, System.TimeSpan timeout, params object[] args) { throw null; } + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.ValueTask InvokeVoidAsync(this Microsoft.JSInterop.IJSRuntime jsRuntime, string identifier, params object[] args) { throw null; } + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.ValueTask InvokeVoidAsync(this Microsoft.JSInterop.IJSRuntime jsRuntime, string identifier, System.Threading.CancellationToken cancellationToken, params object[] args) { throw null; } + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.ValueTask InvokeVoidAsync(this Microsoft.JSInterop.IJSRuntime jsRuntime, string identifier, System.TimeSpan timeout, params object[] args) { throw null; } + } +} +namespace Microsoft.JSInterop.Infrastructure +{ + public static partial class DotNetDispatcher + { + public static void BeginInvokeDotNet(string callId, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { } + public static void EndInvokeJS(string arguments) { } + public static string Invoke(string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { throw null; } } } diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRef.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReference.cs similarity index 54% rename from src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRef.cs rename to src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReference.cs index f604bab272c8..24b13f0c859b 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRef.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReference.cs @@ -1,21 +1,23 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using Microsoft.JSInterop.Infrastructure; + namespace Microsoft.JSInterop { /// - /// Provides convenience methods to produce a . + /// Provides convenience methods to produce a . /// - public static class DotNetObjectRef + public static class DotNetObjectReference { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The reference type to track. - /// An instance of . - public static DotNetObjectRef Create(TValue value) where TValue : class + /// An instance of . + public static DotNetObjectReference Create(TValue value) where TValue : class { - return new DotNetObjectRef(DotNetObjectRefManager.Current, value); + return new DotNetObjectReference(DotNetObjectReferenceManager.Current, value); } } } diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRefOfT.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceOfT.cs similarity index 80% rename from src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRefOfT.cs rename to src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceOfT.cs index d83d0e89bb5f..eb1ac6a23457 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRefOfT.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceOfT.cs @@ -3,6 +3,7 @@ using System; using System.Text.Json.Serialization; +using Microsoft.JSInterop.Infrastructure; namespace Microsoft.JSInterop { @@ -14,31 +15,24 @@ namespace Microsoft.JSInterop /// /// The type of the value to wrap. [JsonConverter(typeof(DotNetObjectReferenceJsonConverterFactory))] - public sealed class DotNetObjectRef : IDotNetObjectRef, IDisposable where TValue : class + public sealed class DotNetObjectReference : IDotNetObjectReference, IDisposable where TValue : class { - private readonly DotNetObjectRefManager _referenceManager; + private readonly DotNetObjectReferenceManager _referenceManager; private readonly TValue _value; private readonly long _objectId; /// - /// Initializes a new instance of . + /// Initializes a new instance of . /// /// /// The value to pass by reference. - internal DotNetObjectRef(DotNetObjectRefManager referenceManager, TValue value) + internal DotNetObjectReference(DotNetObjectReferenceManager referenceManager, TValue value) { _referenceManager = referenceManager; _objectId = _referenceManager.TrackObject(this); _value = value; } - internal DotNetObjectRef(DotNetObjectRefManager referenceManager, long objectId, TValue value) - { - _referenceManager = referenceManager; - _objectId = objectId; - _value = value; - } - /// /// Gets the object instance represented by this wrapper. /// @@ -60,7 +54,7 @@ internal long ObjectId } } - object IDotNetObjectRef.Value => Value; + object IDotNetObjectReference.Value => Value; internal bool Disposed { get; private set; } diff --git a/src/JSInterop/Microsoft.JSInterop/src/IJSRuntime.cs b/src/JSInterop/Microsoft.JSInterop/src/IJSRuntime.cs index d7ee372a732f..6edc725177b4 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/IJSRuntime.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/IJSRuntime.cs @@ -1,7 +1,6 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; @@ -14,21 +13,28 @@ public interface IJSRuntime { /// /// Invokes the specified JavaScript function asynchronously. + /// + /// will apply timeouts to this operation based on the value configured in . To dispatch a call with a different timeout, or no timeout, + /// consider using . + /// /// /// The JSON-serializable return type. /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. /// JSON-serializable arguments. /// An instance of obtained by JSON-deserializing the return value. - Task InvokeAsync(string identifier, params object[] args); + ValueTask InvokeAsync(string identifier, object[] args); /// /// Invokes the specified JavaScript function asynchronously. /// /// The JSON-serializable return type. /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. + /// + /// A cancellation token to signal the cancellation of the operation. Specifying this parameter will override any default cancellations such as due to timeouts + /// () from being applied. + /// /// JSON-serializable arguments. - /// A cancellation token to signal the cancellation of the operation. /// An instance of obtained by JSON-deserializing the return value. - Task InvokeAsync(string identifier, IEnumerable args, CancellationToken cancellationToken = default); + ValueTask InvokeAsync(string identifier, CancellationToken cancellationToken, object[] args); } } diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs similarity index 94% rename from src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs rename to src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs index e639a33ff211..d4a4de14ddac 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs @@ -11,7 +11,7 @@ using System.Text.Json; using System.Threading.Tasks; -namespace Microsoft.JSInterop +namespace Microsoft.JSInterop.Infrastructure { /// /// Provides methods that receive incoming calls from JS to .NET. @@ -39,10 +39,10 @@ public static string Invoke(string assemblyName, string methodIdentifier, long d // the targeted method has [JSInvokable]. It is not itself subject to that restriction, // because there would be nobody to police that. This method *is* the police. - IDotNetObjectRef targetInstance = default; + IDotNetObjectReference targetInstance = default; if (dotNetObjectId != default) { - targetInstance = DotNetObjectRefManager.Current.FindDotNetObject(dotNetObjectId); + targetInstance = DotNetObjectReferenceManager.Current.FindDotNetObject(dotNetObjectId); } var syncResult = InvokeSynchronously(assemblyName, methodIdentifier, targetInstance, argsJson); @@ -63,7 +63,7 @@ public static string Invoke(string assemblyName, string methodIdentifier, long d /// For instance method calls, identifies the target object. /// A JSON representation of the parameters. /// A JSON representation of the return value, or null. - public static void BeginInvoke(string callId, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) + public static void BeginInvokeDotNet(string callId, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { // This method doesn't need [JSInvokable] because the platform is responsible for having // some way to dispatch calls here. The logic inside here is the thing that checks whether @@ -73,19 +73,19 @@ public static void BeginInvoke(string callId, string assemblyName, string method // DotNetDispatcher only works with JSRuntimeBase instances. // If the developer wants to use a totally custom IJSRuntime, then their JS-side // code has to implement its own way of returning async results. - var jsRuntimeBaseInstance = (JSRuntimeBase)JSRuntime.Current; + var jsRuntimeBaseInstance = (JSRuntime)JSRuntime.Current; // Using ExceptionDispatchInfo here throughout because we want to always preserve // original stack traces. object syncResult = null; ExceptionDispatchInfo syncException = null; - IDotNetObjectRef targetInstance = null; + IDotNetObjectReference targetInstance = null; try { if (dotNetObjectId != default) { - targetInstance = DotNetObjectRefManager.Current.FindDotNetObject(dotNetObjectId); + targetInstance = DotNetObjectReferenceManager.Current.FindDotNetObject(dotNetObjectId); } syncResult = InvokeSynchronously(assemblyName, methodIdentifier, targetInstance, argsJson); @@ -128,7 +128,7 @@ public static void BeginInvoke(string callId, string assemblyName, string method } } - private static object InvokeSynchronously(string assemblyName, string methodIdentifier, IDotNetObjectRef objectReference, string argsJson) + private static object InvokeSynchronously(string assemblyName, string methodIdentifier, IDotNetObjectReference objectReference, string argsJson) { AssemblyKey assemblyKey; if (objectReference is null) @@ -227,7 +227,7 @@ static bool IsIncorrectDotNetObjectRefUse(Type parameterType, Utf8JsonReader jso jsonReader.ValueTextEquals(DotNetObjectRefKey.EncodedUtf8Bytes)) { // The JSON payload has the shape we expect from a DotNetObjectRef instance. - return !parameterType.IsGenericType || parameterType.GetGenericTypeDefinition() != typeof(DotNetObjectRef<>); + return !parameterType.IsGenericType || parameterType.GetGenericTypeDefinition() != typeof(DotNetObjectReference<>); } return false; @@ -239,9 +239,9 @@ static bool IsIncorrectDotNetObjectRefUse(Type parameterType, Utf8JsonReader jso /// associated as completed. /// /// - /// All exceptions from are caught + /// All exceptions from are caught /// are delivered via JS interop to the JavaScript side when it requests confirmation, as - /// the mechanism to call relies on + /// the mechanism to call relies on /// using JS->.NET interop. This overload is meant for directly triggering completion callbacks /// for .NET -> JS operations without going through JS interop, so the callsite for this /// method is responsible for handling any possible exception generated from the arguments @@ -252,13 +252,13 @@ static bool IsIncorrectDotNetObjectRefUse(Type parameterType, Utf8JsonReader jso /// This method can throw any exception either from the argument received or as a result /// of executing any callback synchronously upon completion. /// - public static void EndInvoke(string arguments) + public static void EndInvokeJS(string arguments) { - var jsRuntimeBase = (JSRuntimeBase)JSRuntime.Current; + var jsRuntimeBase = (JSRuntime)JSRuntime.Current; ParseEndInvokeArguments(jsRuntimeBase, arguments); } - internal static void ParseEndInvokeArguments(JSRuntimeBase jsRuntimeBase, string arguments) + internal static void ParseEndInvokeArguments(JSRuntime jsRuntimeBase, string arguments) { var utf8JsonBytes = Encoding.UTF8.GetBytes(arguments); diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceJsonConverter.cs b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetObjectReferenceJsonConverter.cs similarity index 77% rename from src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceJsonConverter.cs rename to src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetObjectReferenceJsonConverter.cs index 71bfa28ad5e4..c077ac0b1709 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceJsonConverter.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetObjectReferenceJsonConverter.cs @@ -5,13 +5,13 @@ using System.Text.Json; using System.Text.Json.Serialization; -namespace Microsoft.JSInterop +namespace Microsoft.JSInterop.Infrastructure { - internal sealed class DotNetObjectReferenceJsonConverter : JsonConverter> where TValue : class + internal sealed class DotNetObjectReferenceJsonConverter : JsonConverter> where TValue : class { private static JsonEncodedText DotNetObjectRefKey => DotNetDispatcher.DotNetObjectRefKey; - public override DotNetObjectRef Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override DotNetObjectReference Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { long dotNetObjectId = 0; @@ -40,11 +40,11 @@ public override DotNetObjectRef Read(ref Utf8JsonReader reader, Type typ throw new JsonException($"Required property {DotNetObjectRefKey} not found."); } - var referenceManager = DotNetObjectRefManager.Current; - return (DotNetObjectRef)referenceManager.FindDotNetObject(dotNetObjectId); + var referenceManager = DotNetObjectReferenceManager.Current; + return (DotNetObjectReference)referenceManager.FindDotNetObject(dotNetObjectId); } - public override void Write(Utf8JsonWriter writer, DotNetObjectRef value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, DotNetObjectReference value, JsonSerializerOptions options) { writer.WriteStartObject(); writer.WriteNumber(DotNetObjectRefKey, value.ObjectId); diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRefJsonConverterFactory.cs b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetObjectReferenceJsonConverterFactory.cs similarity index 90% rename from src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRefJsonConverterFactory.cs rename to src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetObjectReferenceJsonConverterFactory.cs index 5cfec5be9dce..350530b62409 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRefJsonConverterFactory.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetObjectReferenceJsonConverterFactory.cs @@ -5,13 +5,13 @@ using System.Text.Json; using System.Text.Json.Serialization; -namespace Microsoft.JSInterop +namespace Microsoft.JSInterop.Infrastructure { internal sealed class DotNetObjectReferenceJsonConverterFactory : JsonConverterFactory { public override bool CanConvert(Type typeToConvert) { - return typeToConvert.IsGenericType && typeToConvert.GetGenericTypeDefinition() == typeof(DotNetObjectRef<>); + return typeToConvert.IsGenericType && typeToConvert.GetGenericTypeDefinition() == typeof(DotNetObjectReference<>); } public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions jsonSerializerOptions) diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRefManager.cs b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetObjectReferenceManager.cs similarity index 74% rename from src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRefManager.cs rename to src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetObjectReferenceManager.cs index a6be6aeb4ffe..709dd963fad3 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRefManager.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetObjectReferenceManager.cs @@ -5,27 +5,27 @@ using System.Collections.Concurrent; using System.Threading; -namespace Microsoft.JSInterop +namespace Microsoft.JSInterop.Infrastructure { - internal class DotNetObjectRefManager + internal class DotNetObjectReferenceManager { private long _nextId = 0; // 0 signals no object, but we increment prior to assignment. The first tracked object should have id 1 - private readonly ConcurrentDictionary _trackedRefsById = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _trackedRefsById = new ConcurrentDictionary(); - public static DotNetObjectRefManager Current + public static DotNetObjectReferenceManager Current { get { - if (!(JSRuntime.Current is JSRuntimeBase jsRuntimeBase)) + if (!(JSRuntime.Current is JSRuntime jsRuntime)) { - throw new InvalidOperationException("JSRuntime must be set up correctly and must be an instance of JSRuntimeBase to use DotNetObjectRef."); + throw new InvalidOperationException("JSRuntime must be set up correctly and must be an instance of JSRuntimeBase to use DotNetObjectReference."); } - return jsRuntimeBase.ObjectRefManager; + return jsRuntime.ObjectRefManager; } } - public long TrackObject(IDotNetObjectRef dotNetObjectRef) + public long TrackObject(IDotNetObjectReference dotNetObjectRef) { var dotNetObjectId = Interlocked.Increment(ref _nextId); _trackedRefsById[dotNetObjectId] = dotNetObjectRef; @@ -33,7 +33,7 @@ public long TrackObject(IDotNetObjectRef dotNetObjectRef) return dotNetObjectId; } - public IDotNetObjectRef FindDotNetObject(long dotNetObjectId) + public IDotNetObjectReference FindDotNetObject(long dotNetObjectId) { return _trackedRefsById.TryGetValue(dotNetObjectId, out var dotNetObjectRef) ? dotNetObjectRef @@ -45,7 +45,7 @@ public IDotNetObjectRef FindDotNetObject(long dotNetObjectId) /// Stops tracking the specified .NET object reference. /// This may be invoked either by disposing a DotNetObjectRef in .NET code, or via JS interop by calling "dispose" on the corresponding instance in JavaScript code /// - /// The ID of the . + /// The ID of the . public void ReleaseDotNetObject(long dotNetObjectId) => _trackedRefsById.TryRemove(dotNetObjectId, out _); } } diff --git a/src/JSInterop/Microsoft.JSInterop/src/IDotNetObjectRef.cs b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/IDotNetObjectReference.cs similarity index 68% rename from src/JSInterop/Microsoft.JSInterop/src/IDotNetObjectRef.cs rename to src/JSInterop/Microsoft.JSInterop/src/Infrastructure/IDotNetObjectReference.cs index da16fa60a094..4b84f2bd0c78 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/IDotNetObjectRef.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/IDotNetObjectReference.cs @@ -3,9 +3,9 @@ using System; -namespace Microsoft.JSInterop +namespace Microsoft.JSInterop.Infrastructure { - internal interface IDotNetObjectRef : IDisposable + internal interface IDotNetObjectReference : IDisposable { object Value { get; } } diff --git a/src/JSInterop/Microsoft.JSInterop/src/TaskGenericsUtil.cs b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/TaskGenericsUtil.cs similarity index 98% rename from src/JSInterop/Microsoft.JSInterop/src/TaskGenericsUtil.cs rename to src/JSInterop/Microsoft.JSInterop/src/Infrastructure/TaskGenericsUtil.cs index 734e9863b84a..4e14d5078338 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/TaskGenericsUtil.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/TaskGenericsUtil.cs @@ -6,7 +6,7 @@ using System.Linq; using System.Threading.Tasks; -namespace Microsoft.JSInterop +namespace Microsoft.JSInterop.Infrastructure { internal static class TaskGenericsUtil { diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntimeBase.cs b/src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntime.cs similarity index 95% rename from src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntimeBase.cs rename to src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntime.cs index 7606f1865fc4..cf8cc7030be9 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntimeBase.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntime.cs @@ -8,7 +8,7 @@ namespace Microsoft.JSInterop /// /// Abstract base class for an in-process JavaScript runtime. /// - public abstract class JSInProcessRuntimeBase : JSRuntimeBase, IJSInProcessRuntime + public abstract class JSInProcessRuntime : JSRuntime, IJSInProcessRuntime { /// /// Invokes the specified JavaScript function synchronously. diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntimeExtensions.cs b/src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntimeExtensions.cs new file mode 100644 index 000000000000..73bc2478480b --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntimeExtensions.cs @@ -0,0 +1,29 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.JSInterop +{ + /// + /// Extensions for . + /// + public static class JSInProcessRuntimeExtensions + { + /// + /// Invokes the specified JavaScript function synchronously. + /// + /// The . + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. + /// JSON-serializable arguments. + public static void InvokeVoid(this IJSInProcessRuntime jsRuntime, string identifier, params object[] args) + { + if (jsRuntime == null) + { + throw new ArgumentNullException(nameof(jsRuntime)); + } + + jsRuntime.Invoke(identifier, args); + } + } +} diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSInvokableAttribute.cs b/src/JSInterop/Microsoft.JSInterop/src/JSInvokableAttribute.cs index e037078cba46..b710d54b2cb0 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/JSInvokableAttribute.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/JSInvokableAttribute.cs @@ -11,7 +11,7 @@ namespace Microsoft.JSInterop /// from untrusted callers. All inputs should be validated carefully. /// [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] - public class JSInvokableAttribute : Attribute + public sealed class JSInvokableAttribute : Attribute { /// /// Gets the identifier for the method. The identifier must be unique within the scope diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs b/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs index ae097ca68e87..598b47c4d41e 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs @@ -2,19 +2,38 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Concurrent; +using System.Linq; +using System.Text.Json; using System.Threading; +using System.Threading.Tasks; +using Microsoft.JSInterop.Infrastructure; namespace Microsoft.JSInterop { /// - /// Provides mechanisms for accessing the current . + /// Abstract base class for a JavaScript runtime. /// - public static class JSRuntime + public abstract partial class JSRuntime : IJSRuntime { private static readonly AsyncLocal _currentJSRuntime = new AsyncLocal(); internal static IJSRuntime Current => _currentJSRuntime.Value; + private long _nextPendingTaskId = 1; // Start at 1 because zero signals "no response needed" + private readonly ConcurrentDictionary _pendingTasks + = new ConcurrentDictionary(); + + private readonly ConcurrentDictionary _cancellationRegistrations = + new ConcurrentDictionary(); + + internal DotNetObjectReferenceManager ObjectRefManager { get; } = new DotNetObjectReferenceManager(); + + /// + /// Gets or sets the default timeout for asynchronous JavaScript calls. + /// + protected TimeSpan? DefaultAsyncTimeout { get; set; } + /// /// Sets the current JS runtime to the supplied instance. /// @@ -26,5 +45,151 @@ public static void SetCurrentJSRuntime(IJSRuntime instance) _currentJSRuntime.Value = instance ?? throw new ArgumentNullException(nameof(instance)); } + + /// + /// Invokes the specified JavaScript function asynchronously. + /// + /// will apply timeouts to this operation based on the value configured in . To dispatch a call with a different, or no timeout, + /// consider using . + /// + /// + /// The JSON-serializable return type. + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. + /// JSON-serializable arguments. + /// An instance of obtained by JSON-deserializing the return value. + public ValueTask InvokeAsync(string identifier, object[] args) + { + if (DefaultAsyncTimeout.HasValue) + { + return InvokeWithDefaultCancellation(identifier, args); + } + + return InvokeAsync(identifier, CancellationToken.None, args); + } + + /// + /// Invokes the specified JavaScript function asynchronously. + /// + /// The JSON-serializable return type. + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. + /// + /// A cancellation token to signal the cancellation of the operation. Specifying this parameter will override any default cancellations such as due to timeouts + /// () from being applied. + /// + /// JSON-serializable arguments. + /// An instance of obtained by JSON-deserializing the return value. + public ValueTask InvokeAsync(string identifier, CancellationToken cancellationToken, object[] args) + { + var taskId = Interlocked.Increment(ref _nextPendingTaskId); + var tcs = new TaskCompletionSource(TaskContinuationOptions.RunContinuationsAsynchronously); + if (cancellationToken != default) + { + _cancellationRegistrations[taskId] = cancellationToken.Register(() => + { + tcs.TrySetCanceled(cancellationToken); + CleanupTasksAndRegistrations(taskId); + }); + } + _pendingTasks[taskId] = tcs; + + try + { + if (cancellationToken.IsCancellationRequested) + { + tcs.TrySetCanceled(cancellationToken); + CleanupTasksAndRegistrations(taskId); + + return new ValueTask(tcs.Task); + } + + var argsJson = args?.Any() == true ? + JsonSerializer.Serialize(args, JsonSerializerOptionsProvider.Options) : + null; + BeginInvokeJS(taskId, identifier, argsJson); + + return new ValueTask(tcs.Task); + } + catch + { + CleanupTasksAndRegistrations(taskId); + throw; + } + } + + private void CleanupTasksAndRegistrations(long taskId) + { + _pendingTasks.TryRemove(taskId, out _); + if (_cancellationRegistrations.TryRemove(taskId, out var registration)) + { + registration.Dispose(); + } + } + + private async ValueTask InvokeWithDefaultCancellation(string identifier, object[] args) + { + using (var cts = new CancellationTokenSource(DefaultAsyncTimeout.Value)) + { + // We need to await here due to the using + return await InvokeAsync(identifier, cts.Token, args); + } + } + + /// + /// Begins an asynchronous function invocation. + /// + /// The identifier for the function invocation, or zero if no async callback is required. + /// The identifier for the function to invoke. + /// A JSON representation of the arguments. + protected abstract void BeginInvokeJS(long taskId, string identifier, string argsJson); + + /// + /// Completes an async JS interop call from JavaScript to .NET + /// + /// The id of the JavaScript callback to execute on completion. + /// Whether the operation succeeded or not. + /// The result of the operation or an object containing error details. + /// The name of the method assembly if the invocation was for a static method. + /// The identifier for the method within the assembly. + /// The tracking id of the dotnet object if the invocation was for an instance method. + protected internal abstract void EndInvokeDotNet( + string callId, + bool success, + object resultOrError, + string assemblyName, + string methodIdentifier, + long dotNetObjectId); + + internal void EndInvokeJS(long taskId, bool succeeded, ref Utf8JsonReader jsonReader) + { + if (!_pendingTasks.TryRemove(taskId, out var tcs)) + { + // We should simply return if we can't find an id for the invocation. + // This likely means that the method that initiated the call defined a timeout and stopped waiting. + return; + } + + CleanupTasksAndRegistrations(taskId); + + try + { + if (succeeded) + { + var resultType = TaskGenericsUtil.GetTaskCompletionSourceResultType(tcs); + + var result = JsonSerializer.Deserialize(ref jsonReader, resultType, JsonSerializerOptionsProvider.Options); + TaskGenericsUtil.SetTaskCompletionSourceResult(tcs, result); + } + else + { + var exceptionText = jsonReader.GetString() ?? string.Empty; + TaskGenericsUtil.SetTaskCompletionSourceException(tcs, new JSException(exceptionText)); + } + } + catch (Exception exception) + { + var message = $"An exception occurred executing JS interop: {exception.Message}. See InnerException for more details."; + TaskGenericsUtil.SetTaskCompletionSourceException(tcs, new JSException(message, exception)); + } + } } } diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs b/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs deleted file mode 100644 index 2121df052343..000000000000 --- a/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Text.Json; -using System.Threading; -using System.Threading.Tasks; - -namespace Microsoft.JSInterop -{ - /// - /// Abstract base class for a JavaScript runtime. - /// - public abstract class JSRuntimeBase : IJSRuntime - { - private long _nextPendingTaskId = 1; // Start at 1 because zero signals "no response needed" - private readonly ConcurrentDictionary _pendingTasks - = new ConcurrentDictionary(); - - private readonly ConcurrentDictionary _cancellationRegistrations = - new ConcurrentDictionary(); - - internal DotNetObjectRefManager ObjectRefManager { get; } = new DotNetObjectRefManager(); - - /// - /// Gets or sets the default timeout for asynchronous JavaScript calls. - /// - protected TimeSpan? DefaultAsyncTimeout { get; set; } - - /// - /// Invokes the specified JavaScript function asynchronously. - /// - /// The JSON-serializable return type. - /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. - /// JSON-serializable arguments. - /// A cancellation token to signal the cancellation of the operation. - /// An instance of obtained by JSON-deserializing the return value. - public Task InvokeAsync(string identifier, IEnumerable args, CancellationToken cancellationToken = default) - { - var taskId = Interlocked.Increment(ref _nextPendingTaskId); - var tcs = new TaskCompletionSource(TaskContinuationOptions.RunContinuationsAsynchronously); - if (cancellationToken != default) - { - _cancellationRegistrations[taskId] = cancellationToken.Register(() => - { - tcs.TrySetCanceled(cancellationToken); - CleanupTasksAndRegistrations(taskId); - }); - } - _pendingTasks[taskId] = tcs; - - try - { - if (cancellationToken.IsCancellationRequested) - { - tcs.TrySetCanceled(cancellationToken); - CleanupTasksAndRegistrations(taskId); - - return tcs.Task; - } - - var argsJson = args?.Any() == true ? - JsonSerializer.Serialize(args, JsonSerializerOptionsProvider.Options) : - null; - BeginInvokeJS(taskId, identifier, argsJson); - - return tcs.Task; - } - catch - { - CleanupTasksAndRegistrations(taskId); - throw; - } - } - - private void CleanupTasksAndRegistrations(long taskId) - { - _pendingTasks.TryRemove(taskId, out _); - if (_cancellationRegistrations.TryRemove(taskId, out var registration)) - { - registration.Dispose(); - } - } - - /// - /// Invokes the specified JavaScript function asynchronously. - /// - /// The JSON-serializable return type. - /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. - /// JSON-serializable arguments. - /// An instance of obtained by JSON-deserializing the return value. - public Task InvokeAsync(string identifier, params object[] args) - { - if (!DefaultAsyncTimeout.HasValue) - { - return InvokeAsync(identifier, args, default); - } - else - { - return InvokeWithDefaultCancellation(identifier, args); - } - } - - private async Task InvokeWithDefaultCancellation(string identifier, IEnumerable args) - { - using (var cts = new CancellationTokenSource(DefaultAsyncTimeout.Value)) - { - // We need to await here due to the using - return await InvokeAsync(identifier, args, cts.Token); - } - } - - /// - /// Begins an asynchronous function invocation. - /// - /// The identifier for the function invocation, or zero if no async callback is required. - /// The identifier for the function to invoke. - /// A JSON representation of the arguments. - protected abstract void BeginInvokeJS(long taskId, string identifier, string argsJson); - - /// - /// Completes an async JS interop call from JavaScript to .NET - /// - /// The id of the JavaScript callback to execute on completion. - /// Whether the operation succeeded or not. - /// The result of the operation or an object containing error details. - /// The name of the method assembly if the invocation was for a static method. - /// The identifier for the method within the assembly. - /// The tracking id of the dotnet object if the invocation was for an instance method. - protected internal abstract void EndInvokeDotNet( - string callId, - bool success, - object resultOrError, - string assemblyName, - string methodIdentifier, - long dotNetObjectId); - - internal void EndInvokeJS(long taskId, bool succeeded, ref Utf8JsonReader jsonReader) - { - if (!_pendingTasks.TryRemove(taskId, out var tcs)) - { - // We should simply return if we can't find an id for the invocation. - // This likely means that the method that initiated the call defined a timeout and stopped waiting. - return; - } - - CleanupTasksAndRegistrations(taskId); - - try - { - if (succeeded) - { - var resultType = TaskGenericsUtil.GetTaskCompletionSourceResultType(tcs); - - var result = JsonSerializer.Deserialize(ref jsonReader, resultType, JsonSerializerOptionsProvider.Options); - TaskGenericsUtil.SetTaskCompletionSourceResult(tcs, result); - } - else - { - var exceptionText = jsonReader.GetString() ?? string.Empty; - TaskGenericsUtil.SetTaskCompletionSourceException(tcs, new JSException(exceptionText)); - } - } - catch (Exception exception) - { - var message = $"An exception occurred executing JS interop: {exception.Message}. See InnerException for more details."; - TaskGenericsUtil.SetTaskCompletionSourceException(tcs, new JSException(message, exception)); - } - } - } -} diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeExtensions.cs b/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeExtensions.cs new file mode 100644 index 000000000000..ff4d3fd1524d --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeExtensions.cs @@ -0,0 +1,140 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.JSInterop +{ + /// + /// Extensions for . + /// + public static class JSRuntimeExtensions + { + /// + /// Invokes the specified JavaScript function asynchronously. + /// + /// The . + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. + /// JSON-serializable arguments. + /// A that represents the asynchronous invocation operation. + public static async ValueTask InvokeVoidAsync(this IJSRuntime jsRuntime, string identifier, params object[] args) + { + if (jsRuntime is null) + { + throw new ArgumentNullException(nameof(jsRuntime)); + } + + await jsRuntime.InvokeAsync(identifier, args); + } + + /// + /// Invokes the specified JavaScript function asynchronously. + /// + /// will apply timeouts to this operation based on the value configured in . To dispatch a call with a different timeout, or no timeout, + /// consider using . + /// + /// + /// The . + /// The JSON-serializable return type. + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. + /// JSON-serializable arguments. + /// An instance of obtained by JSON-deserializing the return value. + public static ValueTask InvokeAsync(this IJSRuntime jsRuntime, string identifier, params object[] args) + { + if (jsRuntime is null) + { + throw new ArgumentNullException(nameof(jsRuntime)); + } + + return jsRuntime.InvokeAsync(identifier, args); + } + + /// + /// Invokes the specified JavaScript function asynchronously. + /// + /// The JSON-serializable return type. + /// The . + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. + /// + /// A cancellation token to signal the cancellation of the operation. Specifying this parameter will override any default cancellations such as due to timeouts + /// () from being applied. + /// + /// JSON-serializable arguments. + /// An instance of obtained by JSON-deserializing the return value. + public static ValueTask InvokeAsync(this IJSRuntime jsRuntime, string identifier, CancellationToken cancellationToken, params object[] args) + { + if (jsRuntime is null) + { + throw new ArgumentNullException(nameof(jsRuntime)); + } + + return jsRuntime.InvokeAsync(identifier, cancellationToken, args); + } + + /// + /// Invokes the specified JavaScript function asynchronously. + /// + /// The . + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. + /// + /// A cancellation token to signal the cancellation of the operation. Specifying this parameter will override any default cancellations such as due to timeouts + /// () from being applied. + /// + /// JSON-serializable arguments. + /// A that represents the asynchronous invocation operation. + public static async ValueTask InvokeVoidAsync(this IJSRuntime jsRuntime, string identifier, CancellationToken cancellationToken, params object[] args) + { + if (jsRuntime is null) + { + throw new ArgumentNullException(nameof(jsRuntime)); + } + + await jsRuntime.InvokeAsync(identifier, cancellationToken, args); + } + + /// + /// Invokes the specified JavaScript function asynchronously. + /// + /// The . + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. + /// The duration after which to cancel the async operation. Overrides default timeouts (). + /// JSON-serializable arguments. + /// A that represents the asynchronous invocation operation. + public static async ValueTask InvokeAsync(this IJSRuntime jsRuntime, string identifier, TimeSpan timeout, params object[] args) + { + if (jsRuntime is null) + { + throw new ArgumentNullException(nameof(jsRuntime)); + } + + + using var cancellationTokenSource = timeout == Timeout.InfiniteTimeSpan ? null : new CancellationTokenSource(timeout); + var cancellationToken = cancellationTokenSource?.Token ?? CancellationToken.None; + + return await jsRuntime.InvokeAsync(identifier, cancellationToken, args); + } + + /// + /// Invokes the specified JavaScript function asynchronously. + /// + /// The . + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. + /// The duration after which to cancel the async operation. Overrides default timeouts (). + /// JSON-serializable arguments. + /// A that represents the asynchronous invocation operation. + public static async ValueTask InvokeVoidAsync(this IJSRuntime jsRuntime, string identifier, TimeSpan timeout, params object[] args) + { + if (jsRuntime is null) + { + throw new ArgumentNullException(nameof(jsRuntime)); + } + + using var cancellationTokenSource = timeout == Timeout.InfiniteTimeSpan ? null : new CancellationTokenSource(timeout); + var cancellationToken = cancellationTokenSource?.Token ?? CancellationToken.None; + + await jsRuntime.InvokeAsync(identifier, cancellationToken, args); + } + } +} diff --git a/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectRefTest.cs b/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectReferenceTest.cs similarity index 83% rename from src/JSInterop/Microsoft.JSInterop/test/DotNetObjectRefTest.cs rename to src/JSInterop/Microsoft.JSInterop/test/DotNetObjectReferenceTest.cs index 22cb471f28c4..bcd5c9502865 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectRefTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectReferenceTest.cs @@ -8,20 +8,20 @@ namespace Microsoft.JSInterop { - public class DotNetObjectRefTest + public class DotNetObjectReferenceTest { [Fact] public Task CanAccessValue() => WithJSRuntime(_ => { var obj = new object(); - Assert.Same(obj, DotNetObjectRef.Create(obj).Value); + Assert.Same(obj, DotNetObjectReference.Create(obj).Value); }); [Fact] public Task NotifiesAssociatedJsRuntimeOfDisposal() => WithJSRuntime(jsRuntime => { // Arrange - var objRef = DotNetObjectRef.Create(new object()); + var objRef = DotNetObjectReference.Create(new object()); // Act Assert.Equal(1, objRef.ObjectId); diff --git a/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs b/src/JSInterop/Microsoft.JSInterop/test/Infrastructure/DotNetDispatcherTest.cs similarity index 91% rename from src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs rename to src/JSInterop/Microsoft.JSInterop/test/Infrastructure/DotNetDispatcherTest.cs index 13e01c230451..d9ddac2a89d1 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/Infrastructure/DotNetDispatcherTest.cs @@ -9,7 +9,7 @@ using System.Threading.Tasks; using Xunit; -namespace Microsoft.JSInterop +namespace Microsoft.JSInterop.Infrastructure { public class DotNetDispatcherTest { @@ -114,7 +114,7 @@ public Task CanInvokeStaticWithParams() => WithJSRuntime(jsRuntime => { // Arrange: Track a .NET object to use as an arg var arg3 = new TestDTO { IntVal = 999, StringVal = "My string" }; - var objectRef = DotNetObjectRef.Create(arg3); + var objectRef = DotNetObjectReference.Create(arg3); jsRuntime.Invoke("unimportant", objectRef); // Arrange: Remaining args @@ -142,7 +142,7 @@ public Task CanInvokeStaticWithParams() => WithJSRuntime(jsRuntime => Assert.False(resultDto2Ref.TryGetProperty(nameof(TestDTO.IntVal), out _)); Assert.True(resultDto2Ref.TryGetProperty(DotNetDispatcher.DotNetObjectRefKey.EncodedUtf8Bytes, out var property)); - var resultDto2 = Assert.IsType>(DotNetObjectRefManager.Current.FindDotNetObject(property.GetInt64())).Value; + var resultDto2 = Assert.IsType>(DotNetObjectReferenceManager.Current.FindDotNetObject(property.GetInt64())).Value; Assert.Equal("MY STRING", resultDto2.StringVal); Assert.Equal(1299, resultDto2.IntVal); }); @@ -153,7 +153,7 @@ public Task InvokingWithIncorrectUseOfDotNetObjectRefThrows() => WithJSRuntime(j // Arrange var method = nameof(SomePublicType.IncorrectDotNetObjectRefUsage); var arg3 = new TestDTO { IntVal = 999, StringVal = "My string" }; - var objectRef = DotNetObjectRef.Create(arg3); + var objectRef = DotNetObjectReference.Create(arg3); jsRuntime.Invoke("unimportant", objectRef); // Arrange: Remaining args @@ -175,7 +175,7 @@ public Task CanInvokeInstanceVoidMethod() => WithJSRuntime(jsRuntime => { // Arrange: Track some instance var targetInstance = new SomePublicType(); - var objectRef = DotNetObjectRef.Create(targetInstance); + var objectRef = DotNetObjectReference.Create(targetInstance); jsRuntime.Invoke("unimportant", objectRef); // Act @@ -191,7 +191,7 @@ public Task CanInvokeBaseInstanceVoidMethod() => WithJSRuntime(jsRuntime => { // Arrange: Track some instance var targetInstance = new DerivedClass(); - var objectRef = DotNetObjectRef.Create(targetInstance); + var objectRef = DotNetObjectReference.Create(targetInstance); jsRuntime.Invoke("unimportant", objectRef); // Act @@ -207,10 +207,10 @@ public Task DotNetObjectReferencesCanBeDisposed() => WithJSRuntime(jsRuntime => { // Arrange var targetInstance = new SomePublicType(); - var objectRef = DotNetObjectRef.Create(targetInstance); + var objectRef = DotNetObjectReference.Create(targetInstance); // Act - DotNetDispatcher.BeginInvoke(null, null, "__Dispose", objectRef.ObjectId, null); + DotNetDispatcher.BeginInvokeDotNet(null, null, "__Dispose", objectRef.ObjectId, null); // Assert Assert.True(objectRef.Disposed); @@ -224,7 +224,7 @@ public Task CannotUseDotNetObjectRefAfterDisposal() => WithJSRuntime(jsRuntime = // Arrange: Track some instance, then dispose it var targetInstance = new SomePublicType(); - var objectRef = DotNetObjectRef.Create(targetInstance); + var objectRef = DotNetObjectReference.Create(targetInstance); jsRuntime.Invoke("unimportant", objectRef); objectRef.Dispose(); @@ -242,7 +242,7 @@ public Task CannotUseDotNetObjectRefAfterReleaseDotNetObject() => WithJSRuntime( // Arrange: Track some instance, then dispose it var targetInstance = new SomePublicType(); - var objectRef = DotNetObjectRef.Create(targetInstance); + var objectRef = DotNetObjectReference.Create(targetInstance); jsRuntime.Invoke("unimportant", objectRef); objectRef.Dispose(); @@ -261,10 +261,10 @@ public Task EndInvoke_WithSuccessValue() => WithJSRuntime(jsRuntime => var argsJson = JsonSerializer.Serialize(new object[] { jsRuntime.LastInvocationAsyncHandle, true, testDTO }, JsonSerializerOptionsProvider.Options); // Act - DotNetDispatcher.EndInvoke(argsJson); + DotNetDispatcher.EndInvokeJS(argsJson); // Assert - Assert.True(task.IsCompleted && task.Status == TaskStatus.RanToCompletion); + Assert.True(task.IsCompletedSuccessfully); var result = task.Result; Assert.Equal(testDTO.StringVal, result.StringVal); Assert.Equal(testDTO.IntVal, result.IntVal); @@ -279,10 +279,10 @@ public Task EndInvoke_WithErrorString() => WithJSRuntime(async jsRuntime => var argsJson = JsonSerializer.Serialize(new object[] { jsRuntime.LastInvocationAsyncHandle, false, expected }, JsonSerializerOptionsProvider.Options); // Act - DotNetDispatcher.EndInvoke(argsJson); + DotNetDispatcher.EndInvokeJS(argsJson); // Assert - var ex = await Assert.ThrowsAsync(() => task); + var ex = await Assert.ThrowsAsync(async () => await task); Assert.Equal(expected, ex.Message); }); @@ -297,7 +297,7 @@ public Task EndInvoke_AfterCancel() => WithJSRuntime(jsRuntime => // Act cts.Cancel(); - DotNetDispatcher.EndInvoke(argsJson); + DotNetDispatcher.EndInvokeJS(argsJson); // Assert Assert.True(task.IsCanceled); @@ -311,10 +311,10 @@ public Task EndInvoke_WithNullError() => WithJSRuntime(async jsRuntime => var argsJson = JsonSerializer.Serialize(new object[] { jsRuntime.LastInvocationAsyncHandle, false, null }, JsonSerializerOptionsProvider.Options); // Act - DotNetDispatcher.EndInvoke(argsJson); + DotNetDispatcher.EndInvokeJS(argsJson); // Assert - var ex = await Assert.ThrowsAsync(() => task); + var ex = await Assert.ThrowsAsync(async () => await task); Assert.Empty(ex.Message); }); @@ -325,8 +325,8 @@ public Task CanInvokeInstanceMethodWithParams() => WithJSRuntime(jsRuntime => var targetInstance = new SomePublicType(); var arg2 = new TestDTO { IntVal = 1234, StringVal = "My string" }; jsRuntime.Invoke("unimportant", - DotNetObjectRef.Create(targetInstance), - DotNetObjectRef.Create(arg2)); + DotNetObjectReference.Create(targetInstance), + DotNetObjectReference.Create(arg2)); var argsJson = "[\"myvalue\",{\"__dotNetObject\":2}]"; // Act @@ -334,7 +334,7 @@ public Task CanInvokeInstanceMethodWithParams() => WithJSRuntime(jsRuntime => // Assert Assert.Equal("[\"You passed myvalue\",{\"__dotNetObject\":3}]", resultJson); - var resultDto = ((DotNetObjectRef)jsRuntime.ObjectRefManager.FindDotNetObject(3)).Value; + var resultDto = ((DotNetObjectReference)jsRuntime.ObjectRefManager.FindDotNetObject(3)).Value; Assert.Equal(1235, resultDto.IntVal); Assert.Equal("MY STRING", resultDto.StringVal); }); @@ -362,7 +362,7 @@ public Task CannotInvokeWithFewerNumberOfParameters() => WithJSRuntime(jsRuntime public Task CannotInvokeWithMoreParameters() => WithJSRuntime(jsRuntime => { // Arrange - var objectRef = DotNetObjectRef.Create(new TestDTO { IntVal = 4 }); + var objectRef = DotNetObjectReference.Create(new TestDTO { IntVal = 4 }); var argsJson = JsonSerializer.Serialize(new object[] { new TestDTO { StringVal = "Another string", IntVal = 456 }, @@ -386,8 +386,8 @@ public Task CanInvokeAsyncMethod() => WithJSRuntime(async jsRuntime => // Arrange: Track some instance plus another object we'll pass as a param var targetInstance = new SomePublicType(); var arg2 = new TestDTO { IntVal = 1234, StringVal = "My string" }; - var arg1Ref = DotNetObjectRef.Create(targetInstance); - var arg2Ref = DotNetObjectRef.Create(arg2); + var arg1Ref = DotNetObjectReference.Create(targetInstance); + var arg2Ref = DotNetObjectReference.Create(arg2); jsRuntime.Invoke("unimportant", arg1Ref, arg2Ref); // Arrange: all args @@ -400,7 +400,7 @@ public Task CanInvokeAsyncMethod() => WithJSRuntime(async jsRuntime => // Act var callId = "123"; var resultTask = jsRuntime.NextInvocationTask; - DotNetDispatcher.BeginInvoke(callId, null, "InvokableAsyncMethod", 1, argsJson); + DotNetDispatcher.BeginInvokeDotNet(callId, null, "InvokableAsyncMethod", 1, argsJson); await resultTask; // Assert: Correct completion information @@ -413,7 +413,7 @@ public Task CanInvokeAsyncMethod() => WithJSRuntime(async jsRuntime => Assert.Equal(2000, resultDto1.IntVal); // Assert: Second result value marshalled by ref - var resultDto2Ref = Assert.IsType>(result[1]); + var resultDto2Ref = Assert.IsType>(result[1]); var resultDto2 = resultDto2Ref.Value; Assert.Equal("MY STRING", resultDto2.StringVal); Assert.Equal(2468, resultDto2.IntVal); @@ -427,7 +427,7 @@ public Task CanInvokeSyncThrowingMethod() => WithJSRuntime(async jsRuntime => // Act var callId = "123"; var resultTask = jsRuntime.NextInvocationTask; - DotNetDispatcher.BeginInvoke(callId, thisAssemblyName, nameof(ThrowingClass.ThrowingMethod), default, default); + DotNetDispatcher.BeginInvokeDotNet(callId, thisAssemblyName, nameof(ThrowingClass.ThrowingMethod), default, default); await resultTask; // This won't throw, it sets properties on the jsRuntime. @@ -449,7 +449,7 @@ public Task CanInvokeAsyncThrowingMethod() => WithJSRuntime(async jsRuntime => // Act var callId = "123"; var resultTask = jsRuntime.NextInvocationTask; - DotNetDispatcher.BeginInvoke(callId, thisAssemblyName, nameof(ThrowingClass.AsyncThrowingMethod), default, default); + DotNetDispatcher.BeginInvokeDotNet(callId, thisAssemblyName, nameof(ThrowingClass.AsyncThrowingMethod), default, default); await resultTask; // This won't throw, it sets properties on the jsRuntime. @@ -469,7 +469,7 @@ public Task BeginInvoke_ThrowsWithInvalidArgsJson_WithCallId() => WithJSRuntime( // Arrange var callId = "123"; var resultTask = jsRuntime.NextInvocationTask; - DotNetDispatcher.BeginInvoke(callId, thisAssemblyName, "InvocableStaticWithParams", default, "not json"); + DotNetDispatcher.BeginInvokeDotNet(callId, thisAssemblyName, "InvocableStaticWithParams", default, "not json"); await resultTask; // This won't throw, it sets properties on the jsRuntime. @@ -486,7 +486,7 @@ public Task BeginInvoke_ThrowsWithInvalid_DotNetObjectRef() => WithJSRuntime(jsR // Arrange var callId = "123"; var resultTask = jsRuntime.NextInvocationTask; - DotNetDispatcher.BeginInvoke(callId, null, "InvokableInstanceVoid", 1, null); + DotNetDispatcher.BeginInvokeDotNet(callId, null, "InvokableInstanceVoid", 1, null); // Assert Assert.Equal(callId, jsRuntime.LastCompletionCallId); @@ -611,7 +611,7 @@ public void ParseEndInvokeArguments_Works() DotNetDispatcher.ParseEndInvokeArguments(jsRuntime, $"[{jsRuntime.LastInvocationAsyncHandle}, true, {{\"intVal\": 7}}]"); - Assert.True(task.IsCompleted && task.Status == TaskStatus.RanToCompletion); + Assert.True(task.IsCompletedSuccessfully); Assert.Equal(7, task.Result.IntVal); } @@ -623,7 +623,7 @@ public void ParseEndInvokeArguments_WithArrayValue() DotNetDispatcher.ParseEndInvokeArguments(jsRuntime, $"[{jsRuntime.LastInvocationAsyncHandle}, true, [1, 2, 3]]"); - Assert.True(task.IsCompleted && task.Status == TaskStatus.RanToCompletion); + Assert.True(task.IsCompletedSuccessfully); Assert.Equal(new[] { 1, 2, 3 }, task.Result); } @@ -635,7 +635,7 @@ public void ParseEndInvokeArguments_WithNullValue() DotNetDispatcher.ParseEndInvokeArguments(jsRuntime, $"[{jsRuntime.LastInvocationAsyncHandle}, true, null]"); - Assert.True(task.IsCompleted && task.Status == TaskStatus.RanToCompletion); + Assert.True(task.IsCompletedSuccessfully); Assert.Null(task.Result); } @@ -685,7 +685,7 @@ public static object MyInvocableNonVoid() => new TestDTO { StringVal = "Test", IntVal = 123 }; [JSInvokable("InvocableStaticWithParams")] - public static object[] MyInvocableWithParams(TestDTO dtoViaJson, int[] incrementAmounts, DotNetObjectRef dtoByRef) + public static object[] MyInvocableWithParams(TestDTO dtoViaJson, int[] incrementAmounts, DotNetObjectReference dtoByRef) => new object[] { new TestDTO // Return via JSON marshalling @@ -693,7 +693,7 @@ public static object[] MyInvocableWithParams(TestDTO dtoViaJson, int[] increment StringVal = dtoViaJson.StringVal.ToUpperInvariant(), IntVal = dtoViaJson.IntVal + incrementAmounts.Sum() }, - DotNetObjectRef.Create(new TestDTO // Return by ref + DotNetObjectReference.Create(new TestDTO // Return by ref { StringVal = dtoByRef.Value.StringVal.ToUpperInvariant(), IntVal = dtoByRef.Value.IntVal + incrementAmounts.Sum() @@ -715,7 +715,7 @@ public void InvokableInstanceVoid() } [JSInvokable] - public object[] InvokableInstanceMethod(string someString, DotNetObjectRef someDTORef) + public object[] InvokableInstanceMethod(string someString, DotNetObjectReference someDTORef) { var someDTO = someDTORef.Value; // Returning an array to make the point that object references @@ -723,7 +723,7 @@ public object[] InvokableInstanceMethod(string someString, DotNetObjectRef InvokableAsyncMethod(TestDTO dtoViaJson, DotNetObjectRef dtoByRefWrapper) + public async Task InvokableAsyncMethod(TestDTO dtoViaJson, DotNetObjectReference dtoByRefWrapper) { await Task.Delay(50); var dtoByRef = dtoByRefWrapper.Value; @@ -743,7 +743,7 @@ public async Task InvokableAsyncMethod(TestDTO dtoViaJson, DotNetObjec StringVal = dtoViaJson.StringVal.ToUpperInvariant(), IntVal = dtoViaJson.IntVal * 2, }, - DotNetObjectRef.Create(new TestDTO // Return by ref + DotNetObjectReference.Create(new TestDTO // Return by ref { StringVal = dtoByRef.StringVal.ToUpperInvariant(), IntVal = dtoByRef.IntVal * 2, @@ -789,7 +789,7 @@ public static async Task AsyncThrowingMethod() } } - public class TestJSRuntime : JSInProcessRuntimeBase + public class TestJSRuntime : JSInProcessRuntime { private TaskCompletionSource _nextInvocationTcs = new TaskCompletionSource(); public Task NextInvocationTask => _nextInvocationTcs.Task; diff --git a/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectReferenceJsonConverterTest.cs b/src/JSInterop/Microsoft.JSInterop/test/Infrastructure/DotNetObjectReferenceJsonConverterTest.cs similarity index 80% rename from src/JSInterop/Microsoft.JSInterop/test/DotNetObjectReferenceJsonConverterTest.cs rename to src/JSInterop/Microsoft.JSInterop/test/Infrastructure/DotNetObjectReferenceJsonConverterTest.cs index 18f3db55c107..541ad2b025d3 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectReferenceJsonConverterTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/Infrastructure/DotNetObjectReferenceJsonConverterTest.cs @@ -6,7 +6,7 @@ using Xunit; using static Microsoft.JSInterop.TestJSRuntime; -namespace Microsoft.JSInterop.Tests +namespace Microsoft.JSInterop.Infrastructure { public class DotNetObjectReferenceJsonConverterTest { @@ -14,12 +14,12 @@ public class DotNetObjectReferenceJsonConverterTest public Task Read_Throws_IfJsonIsMissingDotNetObjectProperty() => WithJSRuntime(_ => { // Arrange - var dotNetObjectRef = DotNetObjectRef.Create(new TestModel()); + var dotNetObjectRef = DotNetObjectReference.Create(new TestModel()); var json = "{}"; // Act & Assert - var ex = Assert.Throws(() => JsonSerializer.Deserialize>(json)); + var ex = Assert.Throws(() => JsonSerializer.Deserialize>(json)); Assert.Equal("Required property __dotNetObject not found.", ex.Message); }); @@ -27,12 +27,12 @@ public Task Read_Throws_IfJsonIsMissingDotNetObjectProperty() => WithJSRuntime(_ public Task Read_Throws_IfJsonContainsUnknownContent() => WithJSRuntime(_ => { // Arrange - var dotNetObjectRef = DotNetObjectRef.Create(new TestModel()); + var dotNetObjectRef = DotNetObjectReference.Create(new TestModel()); var json = "{\"foo\":2}"; // Act & Assert - var ex = Assert.Throws(() => JsonSerializer.Deserialize>(json)); + var ex = Assert.Throws(() => JsonSerializer.Deserialize>(json)); Assert.Equal("Unexcepted JSON property foo.", ex.Message); }); @@ -41,13 +41,13 @@ public Task Read_Throws_IfJsonIsIncomplete() => WithJSRuntime(_ => { // Arrange var input = new TestModel(); - var dotNetObjectRef = DotNetObjectRef.Create(input); + var dotNetObjectRef = DotNetObjectReference.Create(input); var objectId = dotNetObjectRef.ObjectId; var json = $"{{\"__dotNetObject\":{objectId}"; // Act & Assert - var ex = Record.Exception(() => JsonSerializer.Deserialize>(json)); + var ex = Record.Exception(() => JsonSerializer.Deserialize>(json)); Assert.IsAssignableFrom(ex); }); @@ -56,13 +56,13 @@ public Task Read_Throws_IfDotNetObjectIdAppearsMultipleTimes() => WithJSRuntime( { // Arrange var input = new TestModel(); - var dotNetObjectRef = DotNetObjectRef.Create(input); + var dotNetObjectRef = DotNetObjectReference.Create(input); var objectId = dotNetObjectRef.ObjectId; var json = $"{{\"__dotNetObject\":{objectId},\"__dotNetObject\":{objectId}}}"; // Act & Assert - var ex = Record.Exception(() => JsonSerializer.Deserialize>(json)); + var ex = Record.Exception(() => JsonSerializer.Deserialize>(json)); Assert.IsAssignableFrom(ex); }); @@ -71,13 +71,13 @@ public Task Read_ReadsJson() => WithJSRuntime(_ => { // Arrange var input = new TestModel(); - var dotNetObjectRef = DotNetObjectRef.Create(input); + var dotNetObjectRef = DotNetObjectReference.Create(input); var objectId = dotNetObjectRef.ObjectId; var json = $"{{\"__dotNetObject\":{objectId}}}"; // Act - var deserialized = JsonSerializer.Deserialize>(json); + var deserialized = JsonSerializer.Deserialize>(json); // Assert Assert.Same(input, deserialized.Value); @@ -92,13 +92,13 @@ public Task Read_ReturnsTheCorrectInstance() => WithJSRuntime(_ => // Track a few instances and verify that the deserialized value returns the correct value. var instance1 = new TestModel(); var instance2 = new TestModel(); - var ref1 = DotNetObjectRef.Create(instance1); - var ref2 = DotNetObjectRef.Create(instance2); + var ref1 = DotNetObjectReference.Create(instance1); + var ref2 = DotNetObjectReference.Create(instance2); var json = $"[{{\"__dotNetObject\":{ref2.ObjectId}}},{{\"__dotNetObject\":{ref1.ObjectId}}}]"; // Act - var deserialized = JsonSerializer.Deserialize[]>(json); + var deserialized = JsonSerializer.Deserialize[]>(json); // Assert Assert.Same(instance2, deserialized[0].Value); @@ -110,7 +110,7 @@ public Task Read_ReadsJson_WithFormatting() => WithJSRuntime(_ => { // Arrange var input = new TestModel(); - var dotNetObjectRef = DotNetObjectRef.Create(input); + var dotNetObjectRef = DotNetObjectReference.Create(input); var objectId = dotNetObjectRef.ObjectId; var json = @@ -119,7 +119,7 @@ public Task Read_ReadsJson_WithFormatting() => WithJSRuntime(_ => }}"; // Act - var deserialized = JsonSerializer.Deserialize>(json); + var deserialized = JsonSerializer.Deserialize>(json); // Assert Assert.Same(input, deserialized.Value); @@ -130,7 +130,7 @@ public Task Read_ReadsJson_WithFormatting() => WithJSRuntime(_ => public Task WriteJsonTwice_KeepsObjectId() => WithJSRuntime(_ => { // Arrange - var dotNetObjectRef = DotNetObjectRef.Create(new TestModel()); + var dotNetObjectRef = DotNetObjectReference.Create(new TestModel()); // Act var json1 = JsonSerializer.Serialize(dotNetObjectRef); diff --git a/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeExtensionsTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeExtensionsTest.cs new file mode 100644 index 000000000000..3a7f0a4d7905 --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeExtensionsTest.cs @@ -0,0 +1,27 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Threading.Tasks; +using Moq; +using Xunit; + +namespace Microsoft.JSInterop +{ + public class JSInProcessRuntimeExtensionsTest + { + [Fact] + public void InvokeVoid_Works() + { + // Arrange + var method = "someMethod"; + var args = new[] { "a", "b" }; + var jsRuntime = new Mock(MockBehavior.Strict); + jsRuntime.Setup(s => s.Invoke(method, args)).Returns(new ValueTask(new object())); + + // Act + jsRuntime.Object.InvokeVoid(method, args); + + jsRuntime.Verify(); + } + } +} diff --git a/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeBaseTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeTest.cs similarity index 88% rename from src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeBaseTest.cs rename to src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeTest.cs index d71969d450fe..40541012589f 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeBaseTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeTest.cs @@ -6,7 +6,7 @@ using System.Linq; using Xunit; -namespace Microsoft.JSInterop.Tests +namespace Microsoft.JSInterop { public class JSInProcessRuntimeBaseTest { @@ -43,12 +43,12 @@ public void SerializesDotNetObjectWrappersInKnownFormat() // Act // Showing we can pass the DotNetObject either as top-level args or nested - var syncResult = runtime.Invoke>("test identifier", - DotNetObjectRef.Create(obj1), + var syncResult = runtime.Invoke>("test identifier", + DotNetObjectReference.Create(obj1), new Dictionary { - { "obj2", DotNetObjectRef.Create(obj2) }, - { "obj3", DotNetObjectRef.Create(obj3) }, + { "obj2", DotNetObjectReference.Create(obj2) }, + { "obj3", DotNetObjectReference.Create(obj3) }, }); // Assert: Handles null result string @@ -78,11 +78,11 @@ public void SyncCallResultCanIncludeDotNetObjects() var obj2 = new object(); // Act - var syncResult = runtime.Invoke[]>( + var syncResult = runtime.Invoke[]>( "test identifier", - DotNetObjectRef.Create(obj1), + DotNetObjectReference.Create(obj1), "some other arg", - DotNetObjectRef.Create(obj2)); + DotNetObjectReference.Create(obj2)); var call = runtime.InvokeCalls.Single(); // Assert @@ -95,7 +95,7 @@ class TestDTO public string StringValue { get; set; } } - class TestJSInProcessRuntime : JSInProcessRuntimeBase + class TestJSInProcessRuntime : JSInProcessRuntime { public List InvokeCalls { get; set; } = new List(); diff --git a/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs deleted file mode 100644 index c3bf4f9eeffa..000000000000 --- a/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs +++ /dev/null @@ -1,386 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.Json; -using System.Threading; -using System.Threading.Tasks; -using Xunit; - -namespace Microsoft.JSInterop -{ - public class JSRuntimeBaseTest - { - [Fact] - public void DispatchesAsyncCallsWithDistinctAsyncHandles() - { - // Arrange - var runtime = new TestJSRuntime(); - - // Act - runtime.InvokeAsync("test identifier 1", "arg1", 123, true); - runtime.InvokeAsync("test identifier 2", "some other arg"); - - // Assert - Assert.Collection(runtime.BeginInvokeCalls, - call => - { - Assert.Equal("test identifier 1", call.Identifier); - Assert.Equal("[\"arg1\",123,true]", call.ArgsJson); - }, - call => - { - Assert.Equal("test identifier 2", call.Identifier); - Assert.Equal("[\"some other arg\"]", call.ArgsJson); - Assert.NotEqual(runtime.BeginInvokeCalls[0].AsyncHandle, call.AsyncHandle); - }); - } - - [Fact] - public async Task InvokeAsync_CancelsAsyncTask_AfterDefaultTimeout() - { - // Arrange - var runtime = new TestJSRuntime(); - runtime.DefaultTimeout = TimeSpan.FromSeconds(1); - - // Act - var task = runtime.InvokeAsync("test identifier 1", "arg1", 123, true); - - // Assert - await Assert.ThrowsAsync(async () => await task); - } - - [Fact] - public void InvokeAsync_CompletesSuccessfullyBeforeTimeout() - { - // Arrange - var runtime = new TestJSRuntime(); - runtime.DefaultTimeout = TimeSpan.FromSeconds(10); - var reader = new Utf8JsonReader(Encoding.UTF8.GetBytes("null")); - - // Act - var task = runtime.InvokeAsync("test identifier 1", "arg1", 123, true); - - runtime.EndInvokeJS(2, succeeded: true, ref reader); - - Assert.True(task.IsCompleted && task.Status == TaskStatus.RanToCompletion); - } - - [Fact] - public async Task InvokeAsync_CancelsAsyncTasksWhenCancellationTokenFires() - { - // Arrange - using var cts = new CancellationTokenSource(); - var runtime = new TestJSRuntime(); - - // Act - var task = runtime.InvokeAsync("test identifier 1", new object[] { "arg1", 123, true }, cts.Token); - - cts.Cancel(); - - // Assert - await Assert.ThrowsAsync(async () => await task); - } - - [Fact] - public async Task InvokeAsync_DoesNotStartWorkWhenCancellationHasBeenRequested() - { - // Arrange - using var cts = new CancellationTokenSource(); - cts.Cancel(); - var runtime = new TestJSRuntime(); - - // Act - var task = runtime.InvokeAsync("test identifier 1", new object[] { "arg1", 123, true }, cts.Token); - - cts.Cancel(); - - // Assert - await Assert.ThrowsAsync(async () => await task); - Assert.Empty(runtime.BeginInvokeCalls); - } - - [Fact] - public void CanCompleteAsyncCallsAsSuccess() - { - // Arrange - var runtime = new TestJSRuntime(); - - // Act/Assert: Tasks not initially completed - var unrelatedTask = runtime.InvokeAsync("unrelated call", Array.Empty()); - var task = runtime.InvokeAsync("test identifier", Array.Empty()); - Assert.False(unrelatedTask.IsCompleted); - Assert.False(task.IsCompleted); - var bytes = Encoding.UTF8.GetBytes("\"my result\""); - var reader = new Utf8JsonReader(bytes); - - // Act/Assert: Task can be completed - runtime.EndInvokeJS( - runtime.BeginInvokeCalls[1].AsyncHandle, - /* succeeded: */ true, - ref reader); - Assert.False(unrelatedTask.IsCompleted); - Assert.True(task.IsCompleted); - Assert.Equal("my result", task.Result); - } - - [Fact] - public void CanCompleteAsyncCallsWithComplexType() - { - // Arrange - var runtime = new TestJSRuntime(); - - var task = runtime.InvokeAsync("test identifier", Array.Empty()); - var bytes = Encoding.UTF8.GetBytes("{\"id\":10, \"name\": \"Test\"}"); - var reader = new Utf8JsonReader(bytes); - - // Act/Assert: Task can be completed - runtime.EndInvokeJS( - runtime.BeginInvokeCalls[0].AsyncHandle, - /* succeeded: */ true, - ref reader); - Assert.True(task.IsCompleted); - var poco = task.Result; - Assert.Equal(10, poco.Id); - Assert.Equal("Test", poco.Name); - } - - [Fact] - public void CanCompleteAsyncCallsWithComplexTypeUsingPropertyCasing() - { - // Arrange - var runtime = new TestJSRuntime(); - - var task = runtime.InvokeAsync("test identifier", Array.Empty()); - var bytes = Encoding.UTF8.GetBytes("{\"Id\":10, \"Name\": \"Test\"}"); - var reader = new Utf8JsonReader(bytes); - reader.Read(); - - // Act/Assert: Task can be completed - runtime.EndInvokeJS( - runtime.BeginInvokeCalls[0].AsyncHandle, - /* succeeded: */ true, - ref reader); - Assert.True(task.IsCompleted); - var poco = task.Result; - Assert.Equal(10, poco.Id); - Assert.Equal("Test", poco.Name); - } - - [Fact] - public void CanCompleteAsyncCallsAsFailure() - { - // Arrange - var runtime = new TestJSRuntime(); - - // Act/Assert: Tasks not initially completed - var unrelatedTask = runtime.InvokeAsync("unrelated call", Array.Empty()); - var task = runtime.InvokeAsync("test identifier", Array.Empty()); - Assert.False(unrelatedTask.IsCompleted); - Assert.False(task.IsCompleted); - var bytes = Encoding.UTF8.GetBytes("\"This is a test exception\""); - var reader = new Utf8JsonReader(bytes); - reader.Read(); - - // Act/Assert: Task can be failed - runtime.EndInvokeJS( - runtime.BeginInvokeCalls[1].AsyncHandle, - /* succeeded: */ false, - ref reader); - Assert.False(unrelatedTask.IsCompleted); - Assert.True(task.IsCompleted); - - Assert.IsType(task.Exception); - Assert.IsType(task.Exception.InnerException); - Assert.Equal("This is a test exception", ((JSException)task.Exception.InnerException).Message); - } - - [Fact] - public Task CanCompleteAsyncCallsWithErrorsDuringDeserialization() - { - // Arrange - var runtime = new TestJSRuntime(); - - // Act/Assert: Tasks not initially completed - var unrelatedTask = runtime.InvokeAsync("unrelated call", Array.Empty()); - var task = runtime.InvokeAsync("test identifier", Array.Empty()); - Assert.False(unrelatedTask.IsCompleted); - Assert.False(task.IsCompleted); - var bytes = Encoding.UTF8.GetBytes("Not a string"); - var reader = new Utf8JsonReader(bytes); - - // Act/Assert: Task can be failed - runtime.EndInvokeJS( - runtime.BeginInvokeCalls[1].AsyncHandle, - /* succeeded: */ true, - ref reader); - Assert.False(unrelatedTask.IsCompleted); - - return AssertTask(); - - async Task AssertTask() - { - var jsException = await Assert.ThrowsAsync(() => task); - Assert.IsAssignableFrom(jsException.InnerException); - } - } - - [Fact] - public Task CompletingSameAsyncCallMoreThanOnce_IgnoresSecondResultAsync() - { - // Arrange - var runtime = new TestJSRuntime(); - - // Act/Assert - var task = runtime.InvokeAsync("test identifier", Array.Empty()); - var asyncHandle = runtime.BeginInvokeCalls[0].AsyncHandle; - var firstReader = new Utf8JsonReader(Encoding.UTF8.GetBytes("\"Some data\"")); - var secondReader = new Utf8JsonReader(Encoding.UTF8.GetBytes("\"Exception\"")); - - runtime.EndInvokeJS(asyncHandle, true, ref firstReader); - runtime.EndInvokeJS(asyncHandle, false, ref secondReader); - - return AssertTask(); - - async Task AssertTask() - { - var result = await task; - Assert.Equal("Some data", result); - } - } - - [Fact] - public void SerializesDotNetObjectWrappersInKnownFormat() - { - // Arrange - var runtime = new TestJSRuntime(); - JSRuntime.SetCurrentJSRuntime(runtime); - var obj1 = new object(); - var obj2 = new object(); - var obj3 = new object(); - - // Act - // Showing we can pass the DotNetObject either as top-level args or nested - var obj1Ref = DotNetObjectRef.Create(obj1); - var obj1DifferentRef = DotNetObjectRef.Create(obj1); - runtime.InvokeAsync("test identifier", - obj1Ref, - new Dictionary - { - { "obj2", DotNetObjectRef.Create(obj2) }, - { "obj3", DotNetObjectRef.Create(obj3) }, - { "obj1SameRef", obj1Ref }, - { "obj1DifferentRef", obj1DifferentRef }, - }); - - // Assert: Serialized as expected - var call = runtime.BeginInvokeCalls.Single(); - Assert.Equal("test identifier", call.Identifier); - Assert.Equal("[{\"__dotNetObject\":1},{\"obj2\":{\"__dotNetObject\":3},\"obj3\":{\"__dotNetObject\":4},\"obj1SameRef\":{\"__dotNetObject\":1},\"obj1DifferentRef\":{\"__dotNetObject\":2}}]", call.ArgsJson); - - // Assert: Objects were tracked - Assert.Same(obj1, runtime.ObjectRefManager.FindDotNetObject(1).Value); - Assert.Same(obj1, runtime.ObjectRefManager.FindDotNetObject(2).Value); - Assert.Same(obj2, runtime.ObjectRefManager.FindDotNetObject(3).Value); - Assert.Same(obj3, runtime.ObjectRefManager.FindDotNetObject(4).Value); - } - - [Fact] - public void CanSanitizeDotNetInteropExceptions() - { - // Arrange - var expectedMessage = "An error ocurred while invoking '[Assembly]::Method'. Swapping to 'Development' environment will " + - "display more detailed information about the error that occurred."; - - string GetMessage(string assembly, string method) => $"An error ocurred while invoking '[{assembly}]::{method}'. Swapping to 'Development' environment will " + - "display more detailed information about the error that occurred."; - - var runtime = new TestJSRuntime() - { - OnDotNetException = (e, a, m) => new JSError { Message = GetMessage(a, m) } - }; - - var exception = new Exception("Some really sensitive data in here"); - - // Act - runtime.EndInvokeDotNet("0", false, exception, "Assembly", "Method", 0); - - // Assert - var call = runtime.EndInvokeDotNetCalls.Single(); - Assert.Equal("0", call.CallId); - Assert.False(call.Success); - var jsError = Assert.IsType(call.ResultOrError); - Assert.Equal(expectedMessage, jsError.Message); - } - - private class JSError - { - public string Message { get; set; } - } - - private class TestPoco - { - public int Id { get; set; } - - public string Name { get; set; } - } - - class TestJSRuntime : JSRuntimeBase - { - public List BeginInvokeCalls = new List(); - public List EndInvokeDotNetCalls = new List(); - - public TimeSpan? DefaultTimeout - { - set - { - base.DefaultAsyncTimeout = value; - } - } - - public class BeginInvokeAsyncArgs - { - public long AsyncHandle { get; set; } - public string Identifier { get; set; } - public string ArgsJson { get; set; } - } - - public class EndInvokeDotNetArgs - { - public string CallId { get; set; } - public bool Success { get; set; } - public object ResultOrError { get; set; } - } - - public Func OnDotNetException { get; set; } - - protected internal override void EndInvokeDotNet(string callId, bool success, object resultOrError, string assemblyName, string methodIdentifier, long dotNetObjectId) - { - if (OnDotNetException != null && !success) - { - resultOrError = OnDotNetException(resultOrError as Exception, assemblyName, methodIdentifier); - } - - EndInvokeDotNetCalls.Add(new EndInvokeDotNetArgs - { - CallId = callId, - Success = success, - ResultOrError = resultOrError - }); - } - - protected override void BeginInvokeJS(long asyncHandle, string identifier, string argsJson) - { - BeginInvokeCalls.Add(new BeginInvokeAsyncArgs - { - AsyncHandle = asyncHandle, - Identifier = identifier, - ArgsJson = argsJson, - }); - } - } - } -} diff --git a/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeExtensionsTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeExtensionsTest.cs new file mode 100644 index 000000000000..a5f69fbef20e --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeExtensionsTest.cs @@ -0,0 +1,181 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Threading; +using System.Threading.Tasks; +using Moq; +using Xunit; + +namespace Microsoft.JSInterop +{ + public class JSRuntimeExtensionsTest + { + [Fact] + public async Task InvokeAsync_WithParamsArgs() + { + // Arrange + var method = "someMethod"; + var expected = new[] { "a", "b" }; + var jsRuntime = new Mock(MockBehavior.Strict); + jsRuntime.Setup(s => s.InvokeAsync(method, It.IsAny())) + .Callback((method, args) => + { + Assert.Equal(expected, args); + }) + .Returns(new ValueTask("Hello")) + .Verifiable(); + + // Act + var result = await jsRuntime.Object.InvokeAsync(method, "a", "b"); + + // Assert + Assert.Equal("Hello", result); + jsRuntime.Verify(); + } + + [Fact] + public async Task InvokeAsync_WithParamsArgsAndCancellationToken() + { + // Arrange + var method = "someMethod"; + var expected = new[] { "a", "b" }; + var cancellationToken = new CancellationToken(); + var jsRuntime = new Mock(MockBehavior.Strict); + jsRuntime.Setup(s => s.InvokeAsync(method, cancellationToken, It.IsAny())) + .Callback((method, cts, args) => + { + Assert.Equal(expected, args); + }) + .Returns(new ValueTask("Hello")) + .Verifiable(); + + // Act + var result = await jsRuntime.Object.InvokeAsync(method, cancellationToken, "a", "b"); + + // Assert + Assert.Equal("Hello", result); + jsRuntime.Verify(); + } + + [Fact] + public async Task InvokeVoidAsync_WithoutCancellationToken() + { + // Arrange + var method = "someMethod"; + var args = new[] { "a", "b" }; + var jsRuntime = new Mock(MockBehavior.Strict); + jsRuntime.Setup(s => s.InvokeAsync(method, args)).Returns(new ValueTask(new object())); + + // Act + await jsRuntime.Object.InvokeVoidAsync(method, args); + + jsRuntime.Verify(); + } + + [Fact] + public async Task InvokeVoidAsync_WithCancellationToken() + { + // Arrange + var method = "someMethod"; + var args = new[] { "a", "b" }; + var jsRuntime = new Mock(MockBehavior.Strict); + jsRuntime.Setup(s => s.InvokeAsync(method, It.IsAny(), args)).Returns(new ValueTask(new object())); + + // Act + await jsRuntime.Object.InvokeVoidAsync(method, new CancellationToken(), args); + + jsRuntime.Verify(); + } + + [Fact] + public async Task InvokeAsync_WithTimeout() + { + // Arrange + var expected = "Hello"; + var method = "someMethod"; + var args = new[] { "a", "b" }; + var jsRuntime = new Mock(MockBehavior.Strict); + jsRuntime.Setup(s => s.InvokeAsync(method, It.IsAny(), args)) + .Callback((method, cts, args) => + { + // There isn't a very good way to test when the cts will cancel. We'll just verify that + // it'll get cancelled eventually. + Assert.True(cts.CanBeCanceled); + }) + .Returns(new ValueTask(expected)); + + // Act + var result = await jsRuntime.Object.InvokeAsync(method, TimeSpan.FromMinutes(5), args); + + Assert.Equal(expected, result); + jsRuntime.Verify(); + } + + [Fact] + public async Task InvokeAsync_WithInfiniteTimeout() + { + // Arrange + var expected = "Hello"; + var method = "someMethod"; + var args = new[] { "a", "b" }; + var jsRuntime = new Mock(MockBehavior.Strict); + jsRuntime.Setup(s => s.InvokeAsync(method, It.IsAny(), args)) + .Callback((method, cts, args) => + { + Assert.False(cts.CanBeCanceled); + Assert.True(cts == CancellationToken.None); + }) + .Returns(new ValueTask(expected)); + + // Act + var result = await jsRuntime.Object.InvokeAsync(method, Timeout.InfiniteTimeSpan, args); + + Assert.Equal(expected, result); + jsRuntime.Verify(); + } + + [Fact] + public async Task InvokeVoidAsync_WithTimeout() + { + // Arrange + var method = "someMethod"; + var args = new[] { "a", "b" }; + var jsRuntime = new Mock(MockBehavior.Strict); + jsRuntime.Setup(s => s.InvokeAsync(method, It.IsAny(), args)) + .Callback((method, cts, args) => + { + // There isn't a very good way to test when the cts will cancel. We'll just verify that + // it'll get cancelled eventually. + Assert.True(cts.CanBeCanceled); + }) + .Returns(new ValueTask(new object())); + + // Act + await jsRuntime.Object.InvokeVoidAsync(method, TimeSpan.FromMinutes(5), args); + + jsRuntime.Verify(); + } + + [Fact] + public async Task InvokeVoidAsync_WithInfiniteTimeout() + { + // Arrange + var method = "someMethod"; + var args = new[] { "a", "b" }; + var jsRuntime = new Mock(MockBehavior.Strict); + jsRuntime.Setup(s => s.InvokeAsync(method, It.IsAny(), args)) + .Callback((method, cts, args) => + { + Assert.False(cts.CanBeCanceled); + Assert.True(cts == CancellationToken.None); + }) + .Returns(new ValueTask(new object())); + + // Act + await jsRuntime.Object.InvokeVoidAsync(method, Timeout.InfiniteTimeSpan, args); + + jsRuntime.Verify(); + } + } +} diff --git a/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeTest.cs index f2fab5d74110..4e65ddeb0fe2 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeTest.cs @@ -4,20 +4,23 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Xunit; -namespace Microsoft.JSInterop.Tests +namespace Microsoft.JSInterop { public class JSRuntimeTest { + #region this will be removed eventually [Fact] public async Task CanHaveDistinctJSRuntimeInstancesInEachAsyncContext() { var tasks = Enumerable.Range(0, 20).Select(async _ => { - var jsRuntime = new FakeJSRuntime(); + var jsRuntime = new TestJSRuntime(); JSRuntime.SetCurrentJSRuntime(jsRuntime); await Task.Delay(50).ConfigureAwait(false); Assert.Same(jsRuntime, JSRuntime.Current); @@ -26,14 +29,377 @@ public async Task CanHaveDistinctJSRuntimeInstancesInEachAsyncContext() await Task.WhenAll(tasks); Assert.Null(JSRuntime.Current); } + #endregion - private class FakeJSRuntime : IJSRuntime + [Fact] + public void DispatchesAsyncCallsWithDistinctAsyncHandles() + { + // Arrange + var runtime = new TestJSRuntime(); + + // Act + runtime.InvokeAsync("test identifier 1", "arg1", 123, true); + runtime.InvokeAsync("test identifier 2", "some other arg"); + + // Assert + Assert.Collection(runtime.BeginInvokeCalls, + call => + { + Assert.Equal("test identifier 1", call.Identifier); + Assert.Equal("[\"arg1\",123,true]", call.ArgsJson); + }, + call => + { + Assert.Equal("test identifier 2", call.Identifier); + Assert.Equal("[\"some other arg\"]", call.ArgsJson); + Assert.NotEqual(runtime.BeginInvokeCalls[0].AsyncHandle, call.AsyncHandle); + }); + } + + [Fact] + public async Task InvokeAsync_CancelsAsyncTask_AfterDefaultTimeout() + { + // Arrange + var runtime = new TestJSRuntime(); + runtime.DefaultTimeout = TimeSpan.FromSeconds(1); + + // Act + var task = runtime.InvokeAsync("test identifier 1", "arg1", 123, true); + + // Assert + await Assert.ThrowsAsync(async () => await task); + } + + [Fact] + public void InvokeAsync_CompletesSuccessfullyBeforeTimeout() + { + // Arrange + var runtime = new TestJSRuntime(); + runtime.DefaultTimeout = TimeSpan.FromSeconds(10); + var reader = new Utf8JsonReader(Encoding.UTF8.GetBytes("null")); + + // Act + var task = runtime.InvokeAsync("test identifier 1", "arg1", 123, true); + + runtime.EndInvokeJS(2, succeeded: true, ref reader); + + Assert.True(task.IsCompletedSuccessfully); + } + + [Fact] + public async Task InvokeAsync_CancelsAsyncTasksWhenCancellationTokenFires() + { + // Arrange + using var cts = new CancellationTokenSource(); + var runtime = new TestJSRuntime(); + + // Act + var task = runtime.InvokeAsync("test identifier 1", cts.Token, new object[] { "arg1", 123, true }); + + cts.Cancel(); + + // Assert + await Assert.ThrowsAsync(async () => await task); + } + + [Fact] + public async Task InvokeAsync_DoesNotStartWorkWhenCancellationHasBeenRequested() + { + // Arrange + using var cts = new CancellationTokenSource(); + cts.Cancel(); + var runtime = new TestJSRuntime(); + + // Act + var task = runtime.InvokeAsync("test identifier 1", cts.Token, new object[] { "arg1", 123, true }); + + cts.Cancel(); + + // Assert + await Assert.ThrowsAsync(async () => await task); + Assert.Empty(runtime.BeginInvokeCalls); + } + + [Fact] + public void CanCompleteAsyncCallsAsSuccess() + { + // Arrange + var runtime = new TestJSRuntime(); + + // Act/Assert: Tasks not initially completed + var unrelatedTask = runtime.InvokeAsync("unrelated call", Array.Empty()); + var task = runtime.InvokeAsync("test identifier", Array.Empty()); + Assert.False(unrelatedTask.IsCompleted); + Assert.False(task.IsCompleted); + var bytes = Encoding.UTF8.GetBytes("\"my result\""); + var reader = new Utf8JsonReader(bytes); + + // Act/Assert: Task can be completed + runtime.EndInvokeJS( + runtime.BeginInvokeCalls[1].AsyncHandle, + /* succeeded: */ true, + ref reader); + Assert.False(unrelatedTask.IsCompleted); + Assert.True(task.IsCompleted); + Assert.Equal("my result", task.Result); + } + + [Fact] + public void CanCompleteAsyncCallsWithComplexType() + { + // Arrange + var runtime = new TestJSRuntime(); + + var task = runtime.InvokeAsync("test identifier", Array.Empty()); + var bytes = Encoding.UTF8.GetBytes("{\"id\":10, \"name\": \"Test\"}"); + var reader = new Utf8JsonReader(bytes); + + // Act/Assert: Task can be completed + runtime.EndInvokeJS( + runtime.BeginInvokeCalls[0].AsyncHandle, + /* succeeded: */ true, + ref reader); + Assert.True(task.IsCompleted); + var poco = task.Result; + Assert.Equal(10, poco.Id); + Assert.Equal("Test", poco.Name); + } + + [Fact] + public void CanCompleteAsyncCallsWithComplexTypeUsingPropertyCasing() + { + // Arrange + var runtime = new TestJSRuntime(); + + var task = runtime.InvokeAsync("test identifier", Array.Empty()); + var bytes = Encoding.UTF8.GetBytes("{\"Id\":10, \"Name\": \"Test\"}"); + var reader = new Utf8JsonReader(bytes); + reader.Read(); + + // Act/Assert: Task can be completed + runtime.EndInvokeJS( + runtime.BeginInvokeCalls[0].AsyncHandle, + /* succeeded: */ true, + ref reader); + Assert.True(task.IsCompleted); + var poco = task.Result; + Assert.Equal(10, poco.Id); + Assert.Equal("Test", poco.Name); + } + + [Fact] + public void CanCompleteAsyncCallsAsFailure() { - public Task InvokeAsync(string identifier, params object[] args) - => throw new NotImplementedException(); + // Arrange + var runtime = new TestJSRuntime(); + + // Act/Assert: Tasks not initially completed + var unrelatedTask = runtime.InvokeAsync("unrelated call", Array.Empty()); + var task = runtime.InvokeAsync("test identifier", Array.Empty()); + Assert.False(unrelatedTask.IsCompleted); + Assert.False(task.IsCompleted); + var bytes = Encoding.UTF8.GetBytes("\"This is a test exception\""); + var reader = new Utf8JsonReader(bytes); + reader.Read(); + + // Act/Assert: Task can be failed + runtime.EndInvokeJS( + runtime.BeginInvokeCalls[1].AsyncHandle, + /* succeeded: */ false, + ref reader); + Assert.False(unrelatedTask.IsCompleted); + Assert.True(task.IsCompleted); + + var exception = Assert.IsType(task.AsTask().Exception); + var jsException = Assert.IsType(exception.InnerException); + Assert.Equal("This is a test exception", jsException.Message); + } - public Task InvokeAsync(string identifier, IEnumerable args, CancellationToken cancellationToken = default) => - throw new NotImplementedException(); + [Fact] + public Task CanCompleteAsyncCallsWithErrorsDuringDeserialization() + { + // Arrange + var runtime = new TestJSRuntime(); + + // Act/Assert: Tasks not initially completed + var unrelatedTask = runtime.InvokeAsync("unrelated call", Array.Empty()); + var task = runtime.InvokeAsync("test identifier", Array.Empty()); + Assert.False(unrelatedTask.IsCompleted); + Assert.False(task.IsCompleted); + var bytes = Encoding.UTF8.GetBytes("Not a string"); + var reader = new Utf8JsonReader(bytes); + + // Act/Assert: Task can be failed + runtime.EndInvokeJS( + runtime.BeginInvokeCalls[1].AsyncHandle, + /* succeeded: */ true, + ref reader); + Assert.False(unrelatedTask.IsCompleted); + + return AssertTask(); + + async Task AssertTask() + { + var jsException = await Assert.ThrowsAsync(async () => await task); + Assert.IsAssignableFrom(jsException.InnerException); + } + } + + [Fact] + public Task CompletingSameAsyncCallMoreThanOnce_IgnoresSecondResultAsync() + { + // Arrange + var runtime = new TestJSRuntime(); + + // Act/Assert + var task = runtime.InvokeAsync("test identifier", Array.Empty()); + var asyncHandle = runtime.BeginInvokeCalls[0].AsyncHandle; + var firstReader = new Utf8JsonReader(Encoding.UTF8.GetBytes("\"Some data\"")); + var secondReader = new Utf8JsonReader(Encoding.UTF8.GetBytes("\"Exception\"")); + + runtime.EndInvokeJS(asyncHandle, true, ref firstReader); + runtime.EndInvokeJS(asyncHandle, false, ref secondReader); + + return AssertTask(); + + async Task AssertTask() + { + var result = await task; + Assert.Equal("Some data", result); + } + } + + [Fact] + public void SerializesDotNetObjectWrappersInKnownFormat() + { + // Arrange + var runtime = new TestJSRuntime(); + JSRuntime.SetCurrentJSRuntime(runtime); + var obj1 = new object(); + var obj2 = new object(); + var obj3 = new object(); + + // Act + // Showing we can pass the DotNetObject either as top-level args or nested + var obj1Ref = DotNetObjectReference.Create(obj1); + var obj1DifferentRef = DotNetObjectReference.Create(obj1); + runtime.InvokeAsync("test identifier", + obj1Ref, + new Dictionary + { + { "obj2", DotNetObjectReference.Create(obj2) }, + { "obj3", DotNetObjectReference.Create(obj3) }, + { "obj1SameRef", obj1Ref }, + { "obj1DifferentRef", obj1DifferentRef }, + }); + + // Assert: Serialized as expected + var call = runtime.BeginInvokeCalls.Single(); + Assert.Equal("test identifier", call.Identifier); + Assert.Equal("[{\"__dotNetObject\":1},{\"obj2\":{\"__dotNetObject\":3},\"obj3\":{\"__dotNetObject\":4},\"obj1SameRef\":{\"__dotNetObject\":1},\"obj1DifferentRef\":{\"__dotNetObject\":2}}]", call.ArgsJson); + + // Assert: Objects were tracked + Assert.Same(obj1Ref, runtime.ObjectRefManager.FindDotNetObject(1)); + Assert.Same(obj1, obj1Ref.Value); + Assert.NotSame(obj1Ref, runtime.ObjectRefManager.FindDotNetObject(2)); + Assert.Same(obj1, runtime.ObjectRefManager.FindDotNetObject(2).Value); + Assert.Same(obj2, runtime.ObjectRefManager.FindDotNetObject(3).Value); + Assert.Same(obj3, runtime.ObjectRefManager.FindDotNetObject(4).Value); + } + + [Fact] + public void CanSanitizeDotNetInteropExceptions() + { + // Arrange + var expectedMessage = "An error ocurred while invoking '[Assembly]::Method'. Swapping to 'Development' environment will " + + "display more detailed information about the error that occurred."; + + string GetMessage(string assembly, string method) => $"An error ocurred while invoking '[{assembly}]::{method}'. Swapping to 'Development' environment will " + + "display more detailed information about the error that occurred."; + + var runtime = new TestJSRuntime() + { + OnDotNetException = (e, a, m) => new JSError { Message = GetMessage(a, m) } + }; + + var exception = new Exception("Some really sensitive data in here"); + + // Act + runtime.EndInvokeDotNet("0", false, exception, "Assembly", "Method", 0); + + // Assert + var call = runtime.EndInvokeDotNetCalls.Single(); + Assert.Equal("0", call.CallId); + Assert.False(call.Success); + var jsError = Assert.IsType(call.ResultOrError); + Assert.Equal(expectedMessage, jsError.Message); + } + + private class JSError + { + public string Message { get; set; } + } + + private class TestPoco + { + public int Id { get; set; } + + public string Name { get; set; } + } + + class TestJSRuntime : JSRuntime + { + public List BeginInvokeCalls = new List(); + public List EndInvokeDotNetCalls = new List(); + + public TimeSpan? DefaultTimeout + { + set + { + base.DefaultAsyncTimeout = value; + } + } + + public class BeginInvokeAsyncArgs + { + public long AsyncHandle { get; set; } + public string Identifier { get; set; } + public string ArgsJson { get; set; } + } + + public class EndInvokeDotNetArgs + { + public string CallId { get; set; } + public bool Success { get; set; } + public object ResultOrError { get; set; } + } + + public Func OnDotNetException { get; set; } + + protected internal override void EndInvokeDotNet(string callId, bool success, object resultOrError, string assemblyName, string methodIdentifier, long dotNetObjectId) + { + if (OnDotNetException != null && !success) + { + resultOrError = OnDotNetException(resultOrError as Exception, assemblyName, methodIdentifier); + } + + EndInvokeDotNetCalls.Add(new EndInvokeDotNetArgs + { + CallId = callId, + Success = success, + ResultOrError = resultOrError + }); + } + + protected override void BeginInvokeJS(long asyncHandle, string identifier, string argsJson) + { + BeginInvokeCalls.Add(new BeginInvokeAsyncArgs + { + AsyncHandle = asyncHandle, + Identifier = identifier, + ArgsJson = argsJson, + }); + } } } } diff --git a/src/JSInterop/Microsoft.JSInterop/test/TestJSRuntime.cs b/src/JSInterop/Microsoft.JSInterop/test/TestJSRuntime.cs index c4e6b05c5ba1..48782fc4df8f 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/TestJSRuntime.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/TestJSRuntime.cs @@ -6,7 +6,7 @@ namespace Microsoft.JSInterop { - internal class TestJSRuntime : JSRuntimeBase + internal class TestJSRuntime : JSRuntime { protected override void BeginInvokeJS(long asyncHandle, string identifier, string argsJson) { @@ -18,7 +18,7 @@ protected internal override void EndInvokeDotNet(string callId, bool success, ob throw new NotImplementedException(); } - public static async Task WithJSRuntime(Action testCode) + public static async Task WithJSRuntime(Action testCode) { // Since the tests rely on the asynclocal JSRuntime.Current, ensure we // are on a distinct async context with a non-null JSRuntime.Current diff --git a/src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.netstandard2.0.cs b/src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.netstandard2.0.cs index 5299370576a3..2e4defd1b7be 100644 --- a/src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.netstandard2.0.cs +++ b/src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.netstandard2.0.cs @@ -3,7 +3,7 @@ namespace Mono.WebAssembly.Interop { - public partial class MonoWebAssemblyJSRuntime : Microsoft.JSInterop.JSInProcessRuntimeBase + public partial class MonoWebAssemblyJSRuntime : Microsoft.JSInterop.JSInProcessRuntime { public MonoWebAssemblyJSRuntime() { } protected override void BeginInvokeJS(long asyncHandle, string identifier, string argsJson) { } diff --git a/src/JSInterop/Mono.WebAssembly.Interop/src/MonoWebAssemblyJSRuntime.cs b/src/JSInterop/Mono.WebAssembly.Interop/src/MonoWebAssemblyJSRuntime.cs index e65df172f8b9..0e292a3e3ce4 100644 --- a/src/JSInterop/Mono.WebAssembly.Interop/src/MonoWebAssemblyJSRuntime.cs +++ b/src/JSInterop/Mono.WebAssembly.Interop/src/MonoWebAssemblyJSRuntime.cs @@ -5,6 +5,7 @@ using System.Runtime.ExceptionServices; using System.Text.Json; using Microsoft.JSInterop; +using Microsoft.JSInterop.Infrastructure; using WebAssembly.JSInterop; namespace Mono.WebAssembly.Interop @@ -13,7 +14,7 @@ namespace Mono.WebAssembly.Interop /// Provides methods for invoking JavaScript functions for applications running /// on the Mono WebAssembly runtime. /// - public class MonoWebAssemblyJSRuntime : JSInProcessRuntimeBase + public class MonoWebAssemblyJSRuntime : JSInProcessRuntime { /// protected override string InvokeJS(string identifier, string argsJson) @@ -37,7 +38,7 @@ private static string InvokeDotNet(string assemblyName, string methodIdentifier, // Invoked via Mono's JS interop mechanism (invoke_method) private static void EndInvokeJS(string argsJson) - => DotNetDispatcher.EndInvoke(argsJson); + => DotNetDispatcher.EndInvokeJS(argsJson); // Invoked via Mono's JS interop mechanism (invoke_method) private static void BeginInvokeDotNet(string callId, string assemblyNameOrDotNetObjectId, string methodIdentifier, string argsJson) @@ -58,7 +59,7 @@ private static void BeginInvokeDotNet(string callId, string assemblyNameOrDotNet assemblyName = assemblyNameOrDotNetObjectId; } - DotNetDispatcher.BeginInvoke(callId, assemblyName, methodIdentifier, dotNetObjectId, argsJson); + DotNetDispatcher.BeginInvokeDotNet(callId, assemblyName, methodIdentifier, dotNetObjectId, argsJson); } protected override void EndInvokeDotNet( From c717230b1350ba5ad408de9155f2e784dcf3dbac Mon Sep 17 00:00:00 2001 From: Justin Kotalik Date: Thu, 15 Aug 2019 09:12:53 -0700 Subject: [PATCH 127/183] Cleanup to skip/flaky attributes (dotnet/extensions#2186) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/cfef5e07fb893b1c5a94566a0d053290f0c75382 --- src/Testing/src/TestPlatformHelper.cs | 2 +- .../src/xunit/ConditionalFactAttribute.cs | 2 +- .../src/xunit/ConditionalFactDiscoverer.cs | 2 +- .../src/xunit/ConditionalTheoryAttribute.cs | 2 +- .../src/xunit/ConditionalTheoryDiscoverer.cs | 2 +- src/Testing/src/xunit/DockerOnlyAttribute.cs | 2 +- ...vironmentVariableSkipConditionAttribute.cs | 2 +- src/Testing/src/xunit/FlakyAttribute.cs | 2 +- src/Testing/src/xunit/FlakyTestDiscoverer.cs | 2 +- .../xunit/FrameworkSkipConditionAttribute.cs | 2 +- src/Testing/src/xunit/IEnvironmentVariable.cs | 2 +- src/Testing/src/xunit/ITestCondition.cs | 2 +- .../src/xunit/MinimumOsVersionAttribute.cs | 2 +- .../src/xunit/OSSkipConditionAttribute.cs | 2 +- src/Testing/src/xunit/OperatingSystems.cs | 2 +- src/Testing/src/xunit/RuntimeFrameworks.cs | 2 +- src/Testing/src/xunit/SkipOnCIAttribute.cs | 43 ++++++++++++++++ src/Testing/src/xunit/SkipOnHelixAttribute.cs | 50 +++++++++++++++++++ src/Testing/src/xunit/SkippedTestCase.cs | 2 +- src/Testing/src/xunit/TestMethodExtensions.cs | 2 +- src/Testing/src/xunit/WindowsVersions.cs | 2 +- src/Testing/test/ConditionalFactTest.cs | 2 +- src/Testing/test/ConditionalTheoryTest.cs | 2 +- src/Testing/test/DockerTests.cs | 2 +- .../EnvironmentVariableSkipConditionTest.cs | 2 +- src/Testing/test/FlakyAttributeTest.cs | 4 +- .../test/OSSkipConditionAttributeTest.cs | 2 +- src/Testing/test/OSSkipConditionTest.cs | 2 +- src/Testing/test/SkipOnCITests.cs | 22 ++++++++ src/Testing/test/TestPlatformHelperTest.cs | 2 +- 30 files changed, 144 insertions(+), 27 deletions(-) create mode 100644 src/Testing/src/xunit/SkipOnCIAttribute.cs create mode 100644 src/Testing/src/xunit/SkipOnHelixAttribute.cs create mode 100644 src/Testing/test/SkipOnCITests.cs diff --git a/src/Testing/src/TestPlatformHelper.cs b/src/Testing/src/TestPlatformHelper.cs index 1a3f275c7e12..2c13e08eb344 100644 --- a/src/Testing/src/TestPlatformHelper.cs +++ b/src/Testing/src/TestPlatformHelper.cs @@ -20,4 +20,4 @@ public static class TestPlatformHelper public static bool IsMac => RuntimeInformation.IsOSPlatform(OSPlatform.OSX); } -} \ No newline at end of file +} diff --git a/src/Testing/src/xunit/ConditionalFactAttribute.cs b/src/Testing/src/xunit/ConditionalFactAttribute.cs index ce37df2e5656..fdc108190afc 100644 --- a/src/Testing/src/xunit/ConditionalFactAttribute.cs +++ b/src/Testing/src/xunit/ConditionalFactAttribute.cs @@ -5,7 +5,7 @@ using Xunit; using Xunit.Sdk; -namespace Microsoft.AspNetCore.Testing.xunit +namespace Microsoft.AspNetCore.Testing { [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] [XunitTestCaseDiscoverer("Microsoft.AspNetCore.Testing.xunit." + nameof(ConditionalFactDiscoverer), "Microsoft.AspNetCore.Testing")] diff --git a/src/Testing/src/xunit/ConditionalFactDiscoverer.cs b/src/Testing/src/xunit/ConditionalFactDiscoverer.cs index cf49b29e5a7f..ce190376fc17 100644 --- a/src/Testing/src/xunit/ConditionalFactDiscoverer.cs +++ b/src/Testing/src/xunit/ConditionalFactDiscoverer.cs @@ -4,7 +4,7 @@ using Xunit.Abstractions; using Xunit.Sdk; -namespace Microsoft.AspNetCore.Testing.xunit +namespace Microsoft.AspNetCore.Testing { internal class ConditionalFactDiscoverer : FactDiscoverer { diff --git a/src/Testing/src/xunit/ConditionalTheoryAttribute.cs b/src/Testing/src/xunit/ConditionalTheoryAttribute.cs index fe45f2ffc666..58b460e96e38 100644 --- a/src/Testing/src/xunit/ConditionalTheoryAttribute.cs +++ b/src/Testing/src/xunit/ConditionalTheoryAttribute.cs @@ -5,7 +5,7 @@ using Xunit; using Xunit.Sdk; -namespace Microsoft.AspNetCore.Testing.xunit +namespace Microsoft.AspNetCore.Testing { [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] [XunitTestCaseDiscoverer("Microsoft.AspNetCore.Testing.xunit." + nameof(ConditionalTheoryDiscoverer), "Microsoft.AspNetCore.Testing")] diff --git a/src/Testing/src/xunit/ConditionalTheoryDiscoverer.cs b/src/Testing/src/xunit/ConditionalTheoryDiscoverer.cs index 9e413cd5801a..c9ee58889a74 100644 --- a/src/Testing/src/xunit/ConditionalTheoryDiscoverer.cs +++ b/src/Testing/src/xunit/ConditionalTheoryDiscoverer.cs @@ -5,7 +5,7 @@ using Xunit.Abstractions; using Xunit.Sdk; -namespace Microsoft.AspNetCore.Testing.xunit +namespace Microsoft.AspNetCore.Testing { internal class ConditionalTheoryDiscoverer : TheoryDiscoverer { diff --git a/src/Testing/src/xunit/DockerOnlyAttribute.cs b/src/Testing/src/xunit/DockerOnlyAttribute.cs index d67a35a672af..7d809884d6ca 100644 --- a/src/Testing/src/xunit/DockerOnlyAttribute.cs +++ b/src/Testing/src/xunit/DockerOnlyAttribute.cs @@ -6,7 +6,7 @@ using System.Linq; using System.Runtime.InteropServices; -namespace Microsoft.AspNetCore.Testing.xunit +namespace Microsoft.AspNetCore.Testing { [AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = false)] public sealed class DockerOnlyAttribute : Attribute, ITestCondition diff --git a/src/Testing/src/xunit/EnvironmentVariableSkipConditionAttribute.cs b/src/Testing/src/xunit/EnvironmentVariableSkipConditionAttribute.cs index fe215a8e0ba1..0599e319011c 100644 --- a/src/Testing/src/xunit/EnvironmentVariableSkipConditionAttribute.cs +++ b/src/Testing/src/xunit/EnvironmentVariableSkipConditionAttribute.cs @@ -4,7 +4,7 @@ using System; using System.Linq; -namespace Microsoft.AspNetCore.Testing.xunit +namespace Microsoft.AspNetCore.Testing { /// /// Skips a test when the value of an environment variable matches any of the supplied values. diff --git a/src/Testing/src/xunit/FlakyAttribute.cs b/src/Testing/src/xunit/FlakyAttribute.cs index ab4450e68537..acea96f3cb94 100644 --- a/src/Testing/src/xunit/FlakyAttribute.cs +++ b/src/Testing/src/xunit/FlakyAttribute.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using Xunit.Sdk; -namespace Microsoft.AspNetCore.Testing.xunit +namespace Microsoft.AspNetCore.Testing { /// /// Marks a test as "Flaky" so that the build will sequester it and ignore failures. diff --git a/src/Testing/src/xunit/FlakyTestDiscoverer.cs b/src/Testing/src/xunit/FlakyTestDiscoverer.cs index 344b9b237803..aea2f9ea5b90 100644 --- a/src/Testing/src/xunit/FlakyTestDiscoverer.cs +++ b/src/Testing/src/xunit/FlakyTestDiscoverer.cs @@ -4,7 +4,7 @@ using Xunit.Abstractions; using Xunit.Sdk; -namespace Microsoft.AspNetCore.Testing.xunit +namespace Microsoft.AspNetCore.Testing { public class FlakyTestDiscoverer : ITraitDiscoverer { diff --git a/src/Testing/src/xunit/FrameworkSkipConditionAttribute.cs b/src/Testing/src/xunit/FrameworkSkipConditionAttribute.cs index 168076a4347d..b7719848a690 100644 --- a/src/Testing/src/xunit/FrameworkSkipConditionAttribute.cs +++ b/src/Testing/src/xunit/FrameworkSkipConditionAttribute.cs @@ -3,7 +3,7 @@ using System; -namespace Microsoft.AspNetCore.Testing.xunit +namespace Microsoft.AspNetCore.Testing { [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] public class FrameworkSkipConditionAttribute : Attribute, ITestCondition diff --git a/src/Testing/src/xunit/IEnvironmentVariable.cs b/src/Testing/src/xunit/IEnvironmentVariable.cs index 068c210611c7..ed06ed65055b 100644 --- a/src/Testing/src/xunit/IEnvironmentVariable.cs +++ b/src/Testing/src/xunit/IEnvironmentVariable.cs @@ -1,7 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -namespace Microsoft.AspNetCore.Testing.xunit +namespace Microsoft.AspNetCore.Testing { internal interface IEnvironmentVariable { diff --git a/src/Testing/src/xunit/ITestCondition.cs b/src/Testing/src/xunit/ITestCondition.cs index bb6ff1f03125..34767b8574e1 100644 --- a/src/Testing/src/xunit/ITestCondition.cs +++ b/src/Testing/src/xunit/ITestCondition.cs @@ -1,7 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -namespace Microsoft.AspNetCore.Testing.xunit +namespace Microsoft.AspNetCore.Testing { public interface ITestCondition { diff --git a/src/Testing/src/xunit/MinimumOsVersionAttribute.cs b/src/Testing/src/xunit/MinimumOsVersionAttribute.cs index 89e3b19556bd..df4985d338bc 100644 --- a/src/Testing/src/xunit/MinimumOsVersionAttribute.cs +++ b/src/Testing/src/xunit/MinimumOsVersionAttribute.cs @@ -5,7 +5,7 @@ using System.Runtime.InteropServices; using Microsoft.Win32; -namespace Microsoft.AspNetCore.Testing.xunit +namespace Microsoft.AspNetCore.Testing { /// /// Skips a test if the OS is the given type (Windows) and the OS version is less than specified. diff --git a/src/Testing/src/xunit/OSSkipConditionAttribute.cs b/src/Testing/src/xunit/OSSkipConditionAttribute.cs index 99965107182b..7655a3b45acc 100644 --- a/src/Testing/src/xunit/OSSkipConditionAttribute.cs +++ b/src/Testing/src/xunit/OSSkipConditionAttribute.cs @@ -6,7 +6,7 @@ using System.Linq; using System.Runtime.InteropServices; -namespace Microsoft.AspNetCore.Testing.xunit +namespace Microsoft.AspNetCore.Testing { [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly, AllowMultiple = true)] public class OSSkipConditionAttribute : Attribute, ITestCondition diff --git a/src/Testing/src/xunit/OperatingSystems.cs b/src/Testing/src/xunit/OperatingSystems.cs index c575d3e1977f..2ddacacab98d 100644 --- a/src/Testing/src/xunit/OperatingSystems.cs +++ b/src/Testing/src/xunit/OperatingSystems.cs @@ -3,7 +3,7 @@ using System; -namespace Microsoft.AspNetCore.Testing.xunit +namespace Microsoft.AspNetCore.Testing { [Flags] public enum OperatingSystems diff --git a/src/Testing/src/xunit/RuntimeFrameworks.cs b/src/Testing/src/xunit/RuntimeFrameworks.cs index 2ec5ea7ec1a5..3a69022b8857 100644 --- a/src/Testing/src/xunit/RuntimeFrameworks.cs +++ b/src/Testing/src/xunit/RuntimeFrameworks.cs @@ -3,7 +3,7 @@ using System; -namespace Microsoft.AspNetCore.Testing.xunit +namespace Microsoft.AspNetCore.Testing { [Flags] public enum RuntimeFrameworks diff --git a/src/Testing/src/xunit/SkipOnCIAttribute.cs b/src/Testing/src/xunit/SkipOnCIAttribute.cs new file mode 100644 index 000000000000..1ee0b8cde8b6 --- /dev/null +++ b/src/Testing/src/xunit/SkipOnCIAttribute.cs @@ -0,0 +1,43 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.AspNetCore.Testing +{ + /// + /// Skip test if running on CI + /// + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false)] + public class SkipOnCIAttribute : Attribute, ITestCondition + { + public SkipOnCIAttribute(string issueUrl = "") + { + IssueUrl = issueUrl; + } + + public string IssueUrl { get; } + + public bool IsMet + { + get + { + return !OnCI(); + } + } + + public string SkipReason + { + get + { + return $"This test is skipped on CI"; + } + } + + public static bool OnCI() => OnHelix() || OnAzdo(); + public static bool OnHelix() => !string.IsNullOrEmpty(GetTargetHelixQueue()); + public static string GetTargetHelixQueue() => Environment.GetEnvironmentVariable("helix"); + public static bool OnAzdo() => !string.IsNullOrEmpty(GetIfOnAzdo()); + public static string GetIfOnAzdo() => Environment.GetEnvironmentVariable("AGENT_OS"); + } +} diff --git a/src/Testing/src/xunit/SkipOnHelixAttribute.cs b/src/Testing/src/xunit/SkipOnHelixAttribute.cs new file mode 100644 index 000000000000..85e82c11547f --- /dev/null +++ b/src/Testing/src/xunit/SkipOnHelixAttribute.cs @@ -0,0 +1,50 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Linq; + +namespace Microsoft.AspNetCore.Testing +{ + /// + /// Skip test if running on helix (or a particular helix queue). + /// + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false)] + public class SkipOnHelixAttribute : Attribute, ITestCondition + { + public SkipOnHelixAttribute(string issueUrl) + { + if (string.IsNullOrEmpty(issueUrl)) + { + throw new ArgumentException(); + } + IssueUrl = issueUrl; + } + + public string IssueUrl { get; } + + public bool IsMet + { + get + { + var skip = OnHelix() && (Queues == null || Queues.ToLowerInvariant().Split(';').Contains(GetTargetHelixQueue().ToLowerInvariant())); + return !skip; + } + } + + // Queues that should be skipped on, i.e. "Windows.10.Amd64.ClientRS4.VS2017.Open;OSX.1012.Amd64.Open" + public string Queues { get; set; } + + public string SkipReason + { + get + { + return $"This test is skipped on helix"; + } + } + + public static bool OnHelix() => !string.IsNullOrEmpty(GetTargetHelixQueue()); + + public static string GetTargetHelixQueue() => Environment.GetEnvironmentVariable("helix"); + } +} diff --git a/src/Testing/src/xunit/SkippedTestCase.cs b/src/Testing/src/xunit/SkippedTestCase.cs index 1c25c507b972..b514c572097d 100644 --- a/src/Testing/src/xunit/SkippedTestCase.cs +++ b/src/Testing/src/xunit/SkippedTestCase.cs @@ -5,7 +5,7 @@ using Xunit.Abstractions; using Xunit.Sdk; -namespace Microsoft.AspNetCore.Testing.xunit +namespace Microsoft.AspNetCore.Testing { public class SkippedTestCase : XunitTestCase { diff --git a/src/Testing/src/xunit/TestMethodExtensions.cs b/src/Testing/src/xunit/TestMethodExtensions.cs index 5ec3bb4ec377..96dd93eb7cde 100644 --- a/src/Testing/src/xunit/TestMethodExtensions.cs +++ b/src/Testing/src/xunit/TestMethodExtensions.cs @@ -5,7 +5,7 @@ using Xunit.Abstractions; using Xunit.Sdk; -namespace Microsoft.AspNetCore.Testing.xunit +namespace Microsoft.AspNetCore.Testing { public static class TestMethodExtensions { diff --git a/src/Testing/src/xunit/WindowsVersions.cs b/src/Testing/src/xunit/WindowsVersions.cs index ff8312b36385..d89da44de38f 100644 --- a/src/Testing/src/xunit/WindowsVersions.cs +++ b/src/Testing/src/xunit/WindowsVersions.cs @@ -1,7 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -namespace Microsoft.AspNetCore.Testing.xunit +namespace Microsoft.AspNetCore.Testing { public static class WindowsVersions { diff --git a/src/Testing/test/ConditionalFactTest.cs b/src/Testing/test/ConditionalFactTest.cs index 9c5c6d037d23..efc3a16dea8d 100644 --- a/src/Testing/test/ConditionalFactTest.cs +++ b/src/Testing/test/ConditionalFactTest.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using Microsoft.AspNetCore.Testing.xunit; +using Microsoft.AspNetCore.Testing; using Xunit; namespace Microsoft.AspNetCore.Testing diff --git a/src/Testing/test/ConditionalTheoryTest.cs b/src/Testing/test/ConditionalTheoryTest.cs index d824eb61b489..07cf6a968f78 100644 --- a/src/Testing/test/ConditionalTheoryTest.cs +++ b/src/Testing/test/ConditionalTheoryTest.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using Microsoft.AspNetCore.Testing.xunit; +using Microsoft.AspNetCore.Testing; using Xunit; using Xunit.Abstractions; diff --git a/src/Testing/test/DockerTests.cs b/src/Testing/test/DockerTests.cs index c66fdd679c26..12735057d3a7 100644 --- a/src/Testing/test/DockerTests.cs +++ b/src/Testing/test/DockerTests.cs @@ -3,7 +3,7 @@ using System; using System.Runtime.InteropServices; -using Microsoft.AspNetCore.Testing.xunit; +using Microsoft.AspNetCore.Testing; using Xunit; namespace Microsoft.AspNetCore.Testing diff --git a/src/Testing/test/EnvironmentVariableSkipConditionTest.cs b/src/Testing/test/EnvironmentVariableSkipConditionTest.cs index d5e7b6342b31..cbc8e9adadc0 100644 --- a/src/Testing/test/EnvironmentVariableSkipConditionTest.cs +++ b/src/Testing/test/EnvironmentVariableSkipConditionTest.cs @@ -3,7 +3,7 @@ using Xunit; -namespace Microsoft.AspNetCore.Testing.xunit +namespace Microsoft.AspNetCore.Testing { public class EnvironmentVariableSkipConditionTest { diff --git a/src/Testing/test/FlakyAttributeTest.cs b/src/Testing/test/FlakyAttributeTest.cs index 1b9a122d9315..ae06e5cf5054 100644 --- a/src/Testing/test/FlakyAttributeTest.cs +++ b/src/Testing/test/FlakyAttributeTest.cs @@ -1,4 +1,6 @@ -using Microsoft.AspNetCore.Testing.xunit; +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + using System; using System.Collections.Generic; using Xunit; diff --git a/src/Testing/test/OSSkipConditionAttributeTest.cs b/src/Testing/test/OSSkipConditionAttributeTest.cs index 0120eb7a4c1a..199af3ab6e60 100644 --- a/src/Testing/test/OSSkipConditionAttributeTest.cs +++ b/src/Testing/test/OSSkipConditionAttributeTest.cs @@ -5,7 +5,7 @@ using System.Runtime.InteropServices; using Xunit; -namespace Microsoft.AspNetCore.Testing.xunit +namespace Microsoft.AspNetCore.Testing { public class OSSkipConditionAttributeTest { diff --git a/src/Testing/test/OSSkipConditionTest.cs b/src/Testing/test/OSSkipConditionTest.cs index 2d76f2c2cd81..a7904b17305b 100644 --- a/src/Testing/test/OSSkipConditionTest.cs +++ b/src/Testing/test/OSSkipConditionTest.cs @@ -5,7 +5,7 @@ using System.Runtime.InteropServices; using Xunit; -namespace Microsoft.AspNetCore.Testing.xunit +namespace Microsoft.AspNetCore.Testing { public class OSSkipConditionTest { diff --git a/src/Testing/test/SkipOnCITests.cs b/src/Testing/test/SkipOnCITests.cs new file mode 100644 index 000000000000..8df5e73c3027 --- /dev/null +++ b/src/Testing/test/SkipOnCITests.cs @@ -0,0 +1,22 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNetCore.Testing; +using Xunit; + +namespace Microsoft.AspNetCore.Testing.Tests +{ + public class SkipOnCITests + { + [ConditionalFact] + [SkipOnCI] + public void AlwaysSkipOnCI() + { + if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("HELIX")) || !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("AGENT_OS"))) + { + throw new Exception("Flaky!"); + } + } + } +} diff --git a/src/Testing/test/TestPlatformHelperTest.cs b/src/Testing/test/TestPlatformHelperTest.cs index 8e35e164d5c5..b1c2fbf2f82d 100644 --- a/src/Testing/test/TestPlatformHelperTest.cs +++ b/src/Testing/test/TestPlatformHelperTest.cs @@ -1,7 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using Microsoft.AspNetCore.Testing.xunit; +using Microsoft.AspNetCore.Testing; using Xunit; namespace Microsoft.AspNetCore.Testing From ef83e3359d0a40f78c0e3e65d884db64c76cb5cb Mon Sep 17 00:00:00 2001 From: Pranav K Date: Thu, 15 Aug 2019 17:14:03 -0700 Subject: [PATCH 128/183] Change JSInterop to avoid using async locals (dotnet/extensions#2163) * Remove the use of async local JSRuntime * Update DotNetDispatcher to accept a JSRuntime instance rather than use a ambient value. * Modify DotNetObjectReference to start tracking it's value during serialization. \n\nCommit migrated from https://github.com/dotnet/extensions/commit/ae9878bb9945423ad20f0ba97033fcebfb5d8419 --- .../src/src/Microsoft.JSInterop.ts | 6 +- .../ref/Microsoft.JSInterop.netcoreapp3.0.cs | 8 +- .../ref/Microsoft.JSInterop.netstandard2.0.cs | 8 +- .../src/DotNetObjectReference.cs | 4 +- .../src/DotNetObjectReferenceOfT.cs | 42 ++- .../src/Infrastructure/DotNetDispatcher.cs | 49 ++-- .../DotNetObjectReferenceJsonConverter.cs | 15 +- ...tNetObjectReferenceJsonConverterFactory.cs | 9 +- .../DotNetObjectReferenceManager.cs | 51 ---- .../src/JSInProcessRuntime.cs | 4 +- .../Microsoft.JSInterop/src/JSRuntime.cs | 90 +++++-- .../src/JsonSerializerOptionsProvider.cs | 17 -- .../test/DotNetObjectReferenceTest.cs | 84 +++++- .../Infrastructure/DotNetDispatcherTest.cs | 255 +++++++++--------- .../DotNetObjectReferenceJsonConverterTest.cs | 72 ++--- .../test/JSInProcessRuntimeTest.cs | 9 +- .../Microsoft.JSInterop/test/JSRuntimeTest.cs | 30 +-- .../Microsoft.JSInterop/test/TestJSRuntime.cs | 12 - ...Mono.WebAssembly.Interop.netstandard2.0.cs | 1 + .../src/MonoWebAssemblyJSRuntime.cs | 27 +- 20 files changed, 431 insertions(+), 362 deletions(-) delete mode 100644 src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetObjectReferenceManager.cs delete mode 100644 src/JSInterop/Microsoft.JSInterop/src/JsonSerializerOptionsProvider.cs diff --git a/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts b/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts index 30a91bde4de5..3af355f6d016 100644 --- a/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts +++ b/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts @@ -55,7 +55,7 @@ module DotNet { return invokePossibleInstanceMethodAsync(assemblyName, methodIdentifier, null, args); } - function invokePossibleInstanceMethod(assemblyName: string | null, methodIdentifier: string, dotNetObjectId: number | null, args: any[]): T { + function invokePossibleInstanceMethod(assemblyName: string | null, methodIdentifier: string, dotNetObjectId: number | null, args: any[] | null): T { const dispatcher = getRequiredDispatcher(); if (dispatcher.invokeDotNetFromJS) { const argsJson = JSON.stringify(args, argReplacer); @@ -66,7 +66,7 @@ module DotNet { } } - function invokePossibleInstanceMethodAsync(assemblyName: string | null, methodIdentifier: string, dotNetObjectId: number | null, ...args: any[]): Promise { + function invokePossibleInstanceMethodAsync(assemblyName: string | null, methodIdentifier: string, dotNetObjectId: number | null, args: any[] | null): Promise { if (assemblyName && dotNetObjectId) { throw new Error(`For instance method calls, assemblyName should be null. Received '${assemblyName}'.`) ; } @@ -273,7 +273,7 @@ module DotNet { } public dispose() { - const promise = invokePossibleInstanceMethodAsync(null, '__Dispose', this._id); + const promise = invokePossibleInstanceMethodAsync(null, '__Dispose', this._id, null); promise.catch(error => console.error(error)); } diff --git a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netcoreapp3.0.cs b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netcoreapp3.0.cs index a5fbbc768a1e..953f8b0329b2 100644 --- a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netcoreapp3.0.cs +++ b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netcoreapp3.0.cs @@ -48,11 +48,11 @@ public abstract partial class JSRuntime : Microsoft.JSInterop.IJSRuntime { protected JSRuntime() { } protected System.TimeSpan? DefaultAsyncTimeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + protected internal System.Text.Json.JsonSerializerOptions JsonSerializerOptions { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } protected abstract void BeginInvokeJS(long taskId, string identifier, string argsJson); protected internal abstract void EndInvokeDotNet(string callId, bool success, object resultOrError, string assemblyName, string methodIdentifier, long dotNetObjectId); public System.Threading.Tasks.ValueTask InvokeAsync(string identifier, object[] args) { throw null; } public System.Threading.Tasks.ValueTask InvokeAsync(string identifier, System.Threading.CancellationToken cancellationToken, object[] args) { throw null; } - public static void SetCurrentJSRuntime(Microsoft.JSInterop.IJSRuntime instance) { } } public static partial class JSRuntimeExtensions { @@ -72,8 +72,8 @@ namespace Microsoft.JSInterop.Infrastructure { public static partial class DotNetDispatcher { - public static void BeginInvokeDotNet(string callId, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { } - public static void EndInvokeJS(string arguments) { } - public static string Invoke(string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { throw null; } + public static void BeginInvokeDotNet(Microsoft.JSInterop.JSRuntime jsRuntime, string callId, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { } + public static void EndInvokeJS(Microsoft.JSInterop.JSRuntime jsRuntime, string arguments) { } + public static string Invoke(Microsoft.JSInterop.JSRuntime jsRuntime, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { throw null; } } } diff --git a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs index a5fbbc768a1e..953f8b0329b2 100644 --- a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs +++ b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs @@ -48,11 +48,11 @@ public abstract partial class JSRuntime : Microsoft.JSInterop.IJSRuntime { protected JSRuntime() { } protected System.TimeSpan? DefaultAsyncTimeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + protected internal System.Text.Json.JsonSerializerOptions JsonSerializerOptions { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } protected abstract void BeginInvokeJS(long taskId, string identifier, string argsJson); protected internal abstract void EndInvokeDotNet(string callId, bool success, object resultOrError, string assemblyName, string methodIdentifier, long dotNetObjectId); public System.Threading.Tasks.ValueTask InvokeAsync(string identifier, object[] args) { throw null; } public System.Threading.Tasks.ValueTask InvokeAsync(string identifier, System.Threading.CancellationToken cancellationToken, object[] args) { throw null; } - public static void SetCurrentJSRuntime(Microsoft.JSInterop.IJSRuntime instance) { } } public static partial class JSRuntimeExtensions { @@ -72,8 +72,8 @@ namespace Microsoft.JSInterop.Infrastructure { public static partial class DotNetDispatcher { - public static void BeginInvokeDotNet(string callId, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { } - public static void EndInvokeJS(string arguments) { } - public static string Invoke(string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { throw null; } + public static void BeginInvokeDotNet(Microsoft.JSInterop.JSRuntime jsRuntime, string callId, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { } + public static void EndInvokeJS(Microsoft.JSInterop.JSRuntime jsRuntime, string arguments) { } + public static string Invoke(Microsoft.JSInterop.JSRuntime jsRuntime, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { throw null; } } } diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReference.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReference.cs index 24b13f0c859b..989d8062bb63 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReference.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReference.cs @@ -1,8 +1,6 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using Microsoft.JSInterop.Infrastructure; - namespace Microsoft.JSInterop { /// @@ -17,7 +15,7 @@ public static class DotNetObjectReference /// An instance of . public static DotNetObjectReference Create(TValue value) where TValue : class { - return new DotNetObjectReference(DotNetObjectReferenceManager.Current, value); + return new DotNetObjectReference(value); } } } diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceOfT.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceOfT.cs index eb1ac6a23457..773c2ed9a357 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceOfT.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReferenceOfT.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Text.Json.Serialization; +using System.Diagnostics; using Microsoft.JSInterop.Infrastructure; namespace Microsoft.JSInterop @@ -14,22 +14,18 @@ namespace Microsoft.JSInterop /// To avoid leaking memory, the reference must later be disposed by JS code or by .NET code. /// /// The type of the value to wrap. - [JsonConverter(typeof(DotNetObjectReferenceJsonConverterFactory))] public sealed class DotNetObjectReference : IDotNetObjectReference, IDisposable where TValue : class { - private readonly DotNetObjectReferenceManager _referenceManager; private readonly TValue _value; - private readonly long _objectId; + private long _objectId; + private JSRuntime _jsRuntime; /// /// Initializes a new instance of . /// - /// /// The value to pass by reference. - internal DotNetObjectReference(DotNetObjectReferenceManager referenceManager, TValue value) + internal DotNetObjectReference(TValue value) { - _referenceManager = referenceManager; - _objectId = _referenceManager.TrackObject(this); _value = value; } @@ -50,8 +46,30 @@ internal long ObjectId get { ThrowIfDisposed(); + Debug.Assert(_objectId != 0, "Accessing ObjectId without tracking is always incorrect."); + return _objectId; } + set + { + ThrowIfDisposed(); + _objectId = value; + } + } + + internal JSRuntime JSRuntime + { + get + { + ThrowIfDisposed(); + return _jsRuntime; + } + set + { + ThrowIfDisposed(); + _jsRuntime = value; + } + } object IDotNetObjectReference.Value => Value; @@ -68,11 +86,15 @@ public void Dispose() if (!Disposed) { Disposed = true; - _referenceManager.ReleaseDotNetObject(_objectId); + + if (_jsRuntime != null) + { + _jsRuntime.ReleaseObjectReference(_objectId); + } } } - private void ThrowIfDisposed() + internal void ThrowIfDisposed() { if (Disposed) { diff --git a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs index d4a4de14ddac..92afc6278d5e 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs @@ -27,12 +27,13 @@ public static class DotNetDispatcher /// /// Receives a call from JS to .NET, locating and invoking the specified method. /// + /// The . /// The assembly containing the method to be invoked. /// The identifier of the method to be invoked. The method must be annotated with a matching this identifier string. /// For instance method calls, identifies the target object. /// A JSON representation of the parameters. /// A JSON representation of the return value, or null. - public static string Invoke(string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) + public static string Invoke(JSRuntime jsRuntime, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { // This method doesn't need [JSInvokable] because the platform is responsible for having // some way to dispatch calls here. The logic inside here is the thing that checks whether @@ -42,41 +43,38 @@ public static string Invoke(string assemblyName, string methodIdentifier, long d IDotNetObjectReference targetInstance = default; if (dotNetObjectId != default) { - targetInstance = DotNetObjectReferenceManager.Current.FindDotNetObject(dotNetObjectId); + targetInstance = jsRuntime.GetObjectReference(dotNetObjectId); } - var syncResult = InvokeSynchronously(assemblyName, methodIdentifier, targetInstance, argsJson); + var syncResult = InvokeSynchronously(jsRuntime, assemblyName, methodIdentifier, targetInstance, argsJson); if (syncResult == null) { return null; } - return JsonSerializer.Serialize(syncResult, JsonSerializerOptionsProvider.Options); + return JsonSerializer.Serialize(syncResult, jsRuntime.JsonSerializerOptions); } /// /// Receives a call from JS to .NET, locating and invoking the specified method asynchronously. /// + /// The . /// A value identifying the asynchronous call that should be passed back with the result, or null if no result notification is required. /// The assembly containing the method to be invoked. /// The identifier of the method to be invoked. The method must be annotated with a matching this identifier string. /// For instance method calls, identifies the target object. /// A JSON representation of the parameters. /// A JSON representation of the return value, or null. - public static void BeginInvokeDotNet(string callId, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) + public static void BeginInvokeDotNet(JSRuntime jsRuntime, string callId, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { // This method doesn't need [JSInvokable] because the platform is responsible for having // some way to dispatch calls here. The logic inside here is the thing that checks whether // the targeted method has [JSInvokable]. It is not itself subject to that restriction, // because there would be nobody to police that. This method *is* the police. - // DotNetDispatcher only works with JSRuntimeBase instances. - // If the developer wants to use a totally custom IJSRuntime, then their JS-side - // code has to implement its own way of returning async results. - var jsRuntimeBaseInstance = (JSRuntime)JSRuntime.Current; - // Using ExceptionDispatchInfo here throughout because we want to always preserve // original stack traces. + object syncResult = null; ExceptionDispatchInfo syncException = null; IDotNetObjectReference targetInstance = null; @@ -85,10 +83,10 @@ public static void BeginInvokeDotNet(string callId, string assemblyName, string { if (dotNetObjectId != default) { - targetInstance = DotNetObjectReferenceManager.Current.FindDotNetObject(dotNetObjectId); + targetInstance = jsRuntime.GetObjectReference(dotNetObjectId); } - syncResult = InvokeSynchronously(assemblyName, methodIdentifier, targetInstance, argsJson); + syncResult = InvokeSynchronously(jsRuntime, assemblyName, methodIdentifier, targetInstance, argsJson); } catch (Exception ex) { @@ -103,7 +101,7 @@ public static void BeginInvokeDotNet(string callId, string assemblyName, string else if (syncException != null) { // Threw synchronously, let's respond. - jsRuntimeBaseInstance.EndInvokeDotNet(callId, false, syncException, assemblyName, methodIdentifier, dotNetObjectId); + jsRuntime.EndInvokeDotNet(callId, false, syncException, assemblyName, methodIdentifier, dotNetObjectId); } else if (syncResult is Task task) { @@ -115,20 +113,20 @@ public static void BeginInvokeDotNet(string callId, string assemblyName, string { var exception = t.Exception.GetBaseException(); - jsRuntimeBaseInstance.EndInvokeDotNet(callId, false, ExceptionDispatchInfo.Capture(exception), assemblyName, methodIdentifier, dotNetObjectId); + jsRuntime.EndInvokeDotNet(callId, false, ExceptionDispatchInfo.Capture(exception), assemblyName, methodIdentifier, dotNetObjectId); } var result = TaskGenericsUtil.GetTaskResult(task); - jsRuntimeBaseInstance.EndInvokeDotNet(callId, true, result, assemblyName, methodIdentifier, dotNetObjectId); + jsRuntime.EndInvokeDotNet(callId, true, result, assemblyName, methodIdentifier, dotNetObjectId); }, TaskScheduler.Current); } else { - jsRuntimeBaseInstance.EndInvokeDotNet(callId, true, syncResult, assemblyName, methodIdentifier, dotNetObjectId); + jsRuntime.EndInvokeDotNet(callId, true, syncResult, assemblyName, methodIdentifier, dotNetObjectId); } } - private static object InvokeSynchronously(string assemblyName, string methodIdentifier, IDotNetObjectReference objectReference, string argsJson) + private static object InvokeSynchronously(JSRuntime jsRuntime, string assemblyName, string methodIdentifier, IDotNetObjectReference objectReference, string argsJson) { AssemblyKey assemblyKey; if (objectReference is null) @@ -154,7 +152,7 @@ private static object InvokeSynchronously(string assemblyName, string methodIden var (methodInfo, parameterTypes) = GetCachedMethodInfo(assemblyKey, methodIdentifier); - var suppliedArgs = ParseArguments(methodIdentifier, argsJson, parameterTypes); + var suppliedArgs = ParseArguments(jsRuntime, methodIdentifier, argsJson, parameterTypes); try { @@ -173,7 +171,7 @@ private static object InvokeSynchronously(string assemblyName, string methodIden } } - internal static object[] ParseArguments(string methodIdentifier, string arguments, Type[] parameterTypes) + internal static object[] ParseArguments(JSRuntime jsRuntime, string methodIdentifier, string arguments, Type[] parameterTypes) { if (parameterTypes.Length == 0) { @@ -198,7 +196,7 @@ internal static object[] ParseArguments(string methodIdentifier, string argument throw new InvalidOperationException($"In call to '{methodIdentifier}', parameter of type '{parameterType.Name}' at index {(index + 1)} must be declared as type 'DotNetObjectRef<{parameterType.Name}>' to receive the incoming value."); } - suppliedArgs[index] = JsonSerializer.Deserialize(ref reader, parameterType, JsonSerializerOptionsProvider.Options); + suppliedArgs[index] = JsonSerializer.Deserialize(ref reader, parameterType, jsRuntime.JsonSerializerOptions); index++; } @@ -247,18 +245,13 @@ static bool IsIncorrectDotNetObjectRefUse(Type parameterType, Utf8JsonReader jso /// method is responsible for handling any possible exception generated from the arguments /// passed in as parameters. /// + /// The . /// The serialized arguments for the callback completion. /// /// This method can throw any exception either from the argument received or as a result /// of executing any callback synchronously upon completion. /// - public static void EndInvokeJS(string arguments) - { - var jsRuntimeBase = (JSRuntime)JSRuntime.Current; - ParseEndInvokeArguments(jsRuntimeBase, arguments); - } - - internal static void ParseEndInvokeArguments(JSRuntime jsRuntimeBase, string arguments) + public static void EndInvokeJS(JSRuntime jsRuntime, string arguments) { var utf8JsonBytes = Encoding.UTF8.GetBytes(arguments); @@ -281,7 +274,7 @@ internal static void ParseEndInvokeArguments(JSRuntime jsRuntimeBase, string arg var success = reader.GetBoolean(); reader.Read(); - jsRuntimeBase.EndInvokeJS(taskId, success, ref reader); + jsRuntime.EndInvokeJS(taskId, success, ref reader); if (!reader.Read() || reader.TokenType != JsonTokenType.EndArray) { diff --git a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetObjectReferenceJsonConverter.cs b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetObjectReferenceJsonConverter.cs index c077ac0b1709..7658bbc2c355 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetObjectReferenceJsonConverter.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetObjectReferenceJsonConverter.cs @@ -9,8 +9,15 @@ namespace Microsoft.JSInterop.Infrastructure { internal sealed class DotNetObjectReferenceJsonConverter : JsonConverter> where TValue : class { + public DotNetObjectReferenceJsonConverter(JSRuntime jsRuntime) + { + JSRuntime = jsRuntime; + } + private static JsonEncodedText DotNetObjectRefKey => DotNetDispatcher.DotNetObjectRefKey; + public JSRuntime JSRuntime { get; } + public override DotNetObjectReference Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { long dotNetObjectId = 0; @@ -40,14 +47,16 @@ public override DotNetObjectReference Read(ref Utf8JsonReader reader, Ty throw new JsonException($"Required property {DotNetObjectRefKey} not found."); } - var referenceManager = DotNetObjectReferenceManager.Current; - return (DotNetObjectReference)referenceManager.FindDotNetObject(dotNetObjectId); + var value = (DotNetObjectReference)JSRuntime.GetObjectReference(dotNetObjectId); + return value; } public override void Write(Utf8JsonWriter writer, DotNetObjectReference value, JsonSerializerOptions options) { + var objectId = JSRuntime.TrackObjectReference(value); + writer.WriteStartObject(); - writer.WriteNumber(DotNetObjectRefKey, value.ObjectId); + writer.WriteNumber(DotNetObjectRefKey, objectId); writer.WriteEndObject(); } } diff --git a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetObjectReferenceJsonConverterFactory.cs b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetObjectReferenceJsonConverterFactory.cs index 350530b62409..288bfdd090b1 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetObjectReferenceJsonConverterFactory.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetObjectReferenceJsonConverterFactory.cs @@ -9,6 +9,13 @@ namespace Microsoft.JSInterop.Infrastructure { internal sealed class DotNetObjectReferenceJsonConverterFactory : JsonConverterFactory { + public DotNetObjectReferenceJsonConverterFactory(JSRuntime jsRuntime) + { + JSRuntime = jsRuntime; + } + + public JSRuntime JSRuntime { get; } + public override bool CanConvert(Type typeToConvert) { return typeToConvert.IsGenericType && typeToConvert.GetGenericTypeDefinition() == typeof(DotNetObjectReference<>); @@ -20,7 +27,7 @@ public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializer var instanceType = typeToConvert.GetGenericArguments()[0]; var converterType = typeof(DotNetObjectReferenceJsonConverter<>).MakeGenericType(instanceType); - return (JsonConverter)Activator.CreateInstance(converterType); + return (JsonConverter)Activator.CreateInstance(converterType, JSRuntime); } } } diff --git a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetObjectReferenceManager.cs b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetObjectReferenceManager.cs deleted file mode 100644 index 709dd963fad3..000000000000 --- a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetObjectReferenceManager.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Concurrent; -using System.Threading; - -namespace Microsoft.JSInterop.Infrastructure -{ - internal class DotNetObjectReferenceManager - { - private long _nextId = 0; // 0 signals no object, but we increment prior to assignment. The first tracked object should have id 1 - private readonly ConcurrentDictionary _trackedRefsById = new ConcurrentDictionary(); - - public static DotNetObjectReferenceManager Current - { - get - { - if (!(JSRuntime.Current is JSRuntime jsRuntime)) - { - throw new InvalidOperationException("JSRuntime must be set up correctly and must be an instance of JSRuntimeBase to use DotNetObjectReference."); - } - - return jsRuntime.ObjectRefManager; - } - } - - public long TrackObject(IDotNetObjectReference dotNetObjectRef) - { - var dotNetObjectId = Interlocked.Increment(ref _nextId); - _trackedRefsById[dotNetObjectId] = dotNetObjectRef; - - return dotNetObjectId; - } - - public IDotNetObjectReference FindDotNetObject(long dotNetObjectId) - { - return _trackedRefsById.TryGetValue(dotNetObjectId, out var dotNetObjectRef) - ? dotNetObjectRef - : throw new ArgumentException($"There is no tracked object with id '{dotNetObjectId}'. Perhaps the DotNetObjectRef instance was already disposed.", nameof(dotNetObjectId)); - - } - - /// - /// Stops tracking the specified .NET object reference. - /// This may be invoked either by disposing a DotNetObjectRef in .NET code, or via JS interop by calling "dispose" on the corresponding instance in JavaScript code - /// - /// The ID of the . - public void ReleaseDotNetObject(long dotNetObjectId) => _trackedRefsById.TryRemove(dotNetObjectId, out _); - } -} diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntime.cs b/src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntime.cs index cf8cc7030be9..2b96bbbbb5bd 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntime.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntime.cs @@ -19,13 +19,13 @@ public abstract class JSInProcessRuntime : JSRuntime, IJSInProcessRuntime /// An instance of obtained by JSON-deserializing the return value. public TValue Invoke(string identifier, params object[] args) { - var resultJson = InvokeJS(identifier, JsonSerializer.Serialize(args, JsonSerializerOptionsProvider.Options)); + var resultJson = InvokeJS(identifier, JsonSerializer.Serialize(args, JsonSerializerOptions)); if (resultJson is null) { return default; } - return JsonSerializer.Deserialize(resultJson, JsonSerializerOptionsProvider.Options); + return JsonSerializer.Deserialize(resultJson, JsonSerializerOptions); } /// diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs b/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs index 598b47c4d41e..ba411b72dbd5 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Concurrent; +using System.Diagnostics; using System.Linq; using System.Text.Json; using System.Threading; @@ -16,35 +17,39 @@ namespace Microsoft.JSInterop /// public abstract partial class JSRuntime : IJSRuntime { - private static readonly AsyncLocal _currentJSRuntime = new AsyncLocal(); - - internal static IJSRuntime Current => _currentJSRuntime.Value; - + private long _nextObjectReferenceId = 0; // 0 signals no object, but we increment prior to assignment. The first tracked object should have id 1 private long _nextPendingTaskId = 1; // Start at 1 because zero signals "no response needed" - private readonly ConcurrentDictionary _pendingTasks - = new ConcurrentDictionary(); - + private readonly ConcurrentDictionary _pendingTasks = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _trackedRefsById = new ConcurrentDictionary(); private readonly ConcurrentDictionary _cancellationRegistrations = new ConcurrentDictionary(); - internal DotNetObjectReferenceManager ObjectRefManager { get; } = new DotNetObjectReferenceManager(); + /// + /// Initializes a new instance of . + /// + protected JSRuntime() + { + JsonSerializerOptions = new JsonSerializerOptions + { + MaxDepth = 32, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + PropertyNameCaseInsensitive = true, + Converters = + { + new DotNetObjectReferenceJsonConverterFactory(this), + } + }; + } /// - /// Gets or sets the default timeout for asynchronous JavaScript calls. + /// Gets the used to serialize and deserialize interop payloads. /// - protected TimeSpan? DefaultAsyncTimeout { get; set; } + protected internal JsonSerializerOptions JsonSerializerOptions { get; } /// - /// Sets the current JS runtime to the supplied instance. - /// - /// This is intended for framework use. Developers should not normally need to call this method. + /// Gets or sets the default timeout for asynchronous JavaScript calls. /// - /// The new current . - public static void SetCurrentJSRuntime(IJSRuntime instance) - { - _currentJSRuntime.Value = instance - ?? throw new ArgumentNullException(nameof(instance)); - } + protected TimeSpan? DefaultAsyncTimeout { get; set; } /// /// Invokes the specified JavaScript function asynchronously. @@ -103,7 +108,7 @@ public ValueTask InvokeAsync(string identifier, CancellationToke } var argsJson = args?.Any() == true ? - JsonSerializer.Serialize(args, JsonSerializerOptionsProvider.Options) : + JsonSerializer.Serialize(args, JsonSerializerOptions) : null; BeginInvokeJS(taskId, identifier, argsJson); @@ -176,7 +181,7 @@ internal void EndInvokeJS(long taskId, bool succeeded, ref Utf8JsonReader jsonRe { var resultType = TaskGenericsUtil.GetTaskCompletionSourceResultType(tcs); - var result = JsonSerializer.Deserialize(ref jsonReader, resultType, JsonSerializerOptionsProvider.Options); + var result = JsonSerializer.Deserialize(ref jsonReader, resultType, JsonSerializerOptions); TaskGenericsUtil.SetTaskCompletionSourceResult(tcs, result); } else @@ -191,5 +196,48 @@ internal void EndInvokeJS(long taskId, bool succeeded, ref Utf8JsonReader jsonRe TaskGenericsUtil.SetTaskCompletionSourceException(tcs, new JSException(message, exception)); } } + + internal long TrackObjectReference(DotNetObjectReference dotNetObjectReference) where TValue : class + { + if (dotNetObjectReference == null) + { + throw new ArgumentNullException(nameof(dotNetObjectReference)); + } + + dotNetObjectReference.ThrowIfDisposed(); + + var jsRuntime = dotNetObjectReference.JSRuntime; + if (jsRuntime is null) + { + var dotNetObjectId = Interlocked.Increment(ref _nextObjectReferenceId); + + dotNetObjectReference.JSRuntime = this; + dotNetObjectReference.ObjectId = dotNetObjectId; + + _trackedRefsById[dotNetObjectId] = dotNetObjectReference; + } + else if (!ReferenceEquals(this, jsRuntime)) + { + throw new InvalidOperationException($"{dotNetObjectReference.GetType().Name} is already being tracked by a different instance of {nameof(JSRuntime)}." + + $" A common cause is caching an instance of {nameof(DotNetObjectReference)} globally. Consider creating instances of {nameof(DotNetObjectReference)} at the JSInterop callsite."); + } + + Debug.Assert(dotNetObjectReference.ObjectId != 0); + return dotNetObjectReference.ObjectId; + } + + internal IDotNetObjectReference GetObjectReference(long dotNetObjectId) + { + return _trackedRefsById.TryGetValue(dotNetObjectId, out var dotNetObjectRef) + ? dotNetObjectRef + : throw new ArgumentException($"There is no tracked object with id '{dotNetObjectId}'. Perhaps the DotNetObjectReference instance was already disposed.", nameof(dotNetObjectId)); + } + + /// + /// Stops tracking the specified .NET object reference. + /// This may be invoked either by disposing a DotNetObjectRef in .NET code, or via JS interop by calling "dispose" on the corresponding instance in JavaScript code + /// + /// The ID of the . + internal void ReleaseObjectReference(long dotNetObjectId) => _trackedRefsById.TryRemove(dotNetObjectId, out _); } } diff --git a/src/JSInterop/Microsoft.JSInterop/src/JsonSerializerOptionsProvider.cs b/src/JSInterop/Microsoft.JSInterop/src/JsonSerializerOptionsProvider.cs deleted file mode 100644 index 62244270e38b..000000000000 --- a/src/JSInterop/Microsoft.JSInterop/src/JsonSerializerOptionsProvider.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Text.Json; - -namespace Microsoft.JSInterop -{ - internal static class JsonSerializerOptionsProvider - { - public static readonly JsonSerializerOptions Options = new JsonSerializerOptions - { - MaxDepth = 32, - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - PropertyNameCaseInsensitive = true, - }; - } -} diff --git a/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectReferenceTest.cs b/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectReferenceTest.cs index bcd5c9502865..95fad485a7a4 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectReferenceTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectReferenceTest.cs @@ -2,34 +2,102 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Threading.Tasks; using Xunit; -using static Microsoft.JSInterop.TestJSRuntime; namespace Microsoft.JSInterop { public class DotNetObjectReferenceTest { [Fact] - public Task CanAccessValue() => WithJSRuntime(_ => + public void CanAccessValue() { var obj = new object(); Assert.Same(obj, DotNetObjectReference.Create(obj).Value); - }); + } [Fact] - public Task NotifiesAssociatedJsRuntimeOfDisposal() => WithJSRuntime(jsRuntime => + public void TrackObjectReference_AssignsObjectId() { // Arrange + var jsRuntime = new TestJSRuntime(); var objRef = DotNetObjectReference.Create(new object()); // Act + var objectId = jsRuntime.TrackObjectReference(objRef); + + // Act + Assert.Equal(objectId, objRef.ObjectId); Assert.Equal(1, objRef.ObjectId); + } + + [Fact] + public void TrackObjectReference_AllowsMultipleCallsUsingTheSameJSRuntime() + { + // Arrange + var jsRuntime = new TestJSRuntime(); + var objRef = DotNetObjectReference.Create(new object()); + + // Act + var objectId1 = jsRuntime.TrackObjectReference(objRef); + var objectId2 = jsRuntime.TrackObjectReference(objRef); + + // Act + Assert.Equal(objectId1, objectId2); + } + + [Fact] + public void TrackObjectReference_ThrowsIfDifferentJSRuntimeInstancesAreUsed() + { + // Arrange + var objRef = DotNetObjectReference.Create("Hello world"); + var expected = $"{objRef.GetType().Name} is already being tracked by a different instance of {nameof(JSRuntime)}. A common cause is caching an instance of {nameof(DotNetObjectReference)}" + + $" globally. Consider creating instances of {nameof(DotNetObjectReference)} at the JSInterop callsite."; + var jsRuntime1 = new TestJSRuntime(); + var jsRuntime2 = new TestJSRuntime(); + jsRuntime1.TrackObjectReference(objRef); + + // Act + var ex = Assert.Throws(() => jsRuntime2.TrackObjectReference(objRef)); + + // Assert + Assert.Equal(expected, ex.Message); + } + + [Fact] + public void Dispose_StopsTrackingObject() + { + // Arrange + var objRef = DotNetObjectReference.Create("Hello world"); + var jsRuntime = new TestJSRuntime(); + jsRuntime.TrackObjectReference(objRef); + var objectId = objRef.ObjectId; + var expected = $"There is no tracked object with id '{objectId}'. Perhaps the DotNetObjectReference instance was already disposed."; + + // Act + Assert.Same(objRef, jsRuntime.GetObjectReference(objectId)); objRef.Dispose(); // Assert - var ex = Assert.Throws(() => jsRuntime.ObjectRefManager.FindDotNetObject(1)); - Assert.StartsWith("There is no tracked object with id '1'.", ex.Message); - }); + Assert.True(objRef.Disposed); + Assert.Throws(() => jsRuntime.GetObjectReference(objectId)); + } + + [Fact] + public void DoubleDispose_Works() + { + // Arrange + var objRef = DotNetObjectReference.Create("Hello world"); + var jsRuntime = new TestJSRuntime(); + jsRuntime.TrackObjectReference(objRef); + var objectId = objRef.ObjectId; + + // Act + Assert.Same(objRef, jsRuntime.GetObjectReference(objectId)); + objRef.Dispose(); + + // Assert + objRef.Dispose(); + // If we got this far, this did not throw. + } } } diff --git a/src/JSInterop/Microsoft.JSInterop/test/Infrastructure/DotNetDispatcherTest.cs b/src/JSInterop/Microsoft.JSInterop/test/Infrastructure/DotNetDispatcherTest.cs index d9ddac2a89d1..2d8208b7a604 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/Infrastructure/DotNetDispatcherTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/Infrastructure/DotNetDispatcherTest.cs @@ -20,7 +20,7 @@ public void CannotInvokeWithEmptyAssemblyName() { var ex = Assert.Throws(() => { - DotNetDispatcher.Invoke(" ", "SomeMethod", default, "[]"); + DotNetDispatcher.Invoke(new TestJSRuntime(), " ", "SomeMethod", default, "[]"); }); Assert.StartsWith("Cannot be null, empty, or whitespace.", ex.Message); @@ -32,7 +32,7 @@ public void CannotInvokeWithEmptyMethodIdentifier() { var ex = Assert.Throws(() => { - DotNetDispatcher.Invoke("SomeAssembly", " ", default, "[]"); + DotNetDispatcher.Invoke(new TestJSRuntime(), "SomeAssembly", " ", default, "[]"); }); Assert.StartsWith("Cannot be null, empty, or whitespace.", ex.Message); @@ -45,7 +45,7 @@ public void CannotInvokeMethodsOnUnloadedAssembly() var assemblyName = "Some.Fake.Assembly"; var ex = Assert.Throws(() => { - DotNetDispatcher.Invoke(assemblyName, "SomeMethod", default, null); + DotNetDispatcher.Invoke(new TestJSRuntime(), assemblyName, "SomeMethod", default, null); }); Assert.Equal($"There is no loaded assembly with the name '{assemblyName}'.", ex.Message); @@ -67,52 +67,56 @@ public void CannotInvokeUnsuitableMethods(string methodIdentifier) { var ex = Assert.Throws(() => { - DotNetDispatcher.Invoke(thisAssemblyName, methodIdentifier, default, null); + DotNetDispatcher.Invoke(new TestJSRuntime(), thisAssemblyName, methodIdentifier, default, null); }); Assert.Equal($"The assembly '{thisAssemblyName}' does not contain a public method with [JSInvokableAttribute(\"{methodIdentifier}\")].", ex.Message); } [Fact] - public Task CanInvokeStaticVoidMethod() => WithJSRuntime(jsRuntime => + public void CanInvokeStaticVoidMethod() { // Arrange/Act + var jsRuntime = new TestJSRuntime(); SomePublicType.DidInvokeMyInvocableStaticVoid = false; - var resultJson = DotNetDispatcher.Invoke(thisAssemblyName, "InvocableStaticVoid", default, null); + var resultJson = DotNetDispatcher.Invoke(jsRuntime, thisAssemblyName, "InvocableStaticVoid", default, null); // Assert Assert.Null(resultJson); Assert.True(SomePublicType.DidInvokeMyInvocableStaticVoid); - }); + } [Fact] - public Task CanInvokeStaticNonVoidMethod() => WithJSRuntime(jsRuntime => + public void CanInvokeStaticNonVoidMethod() { // Arrange/Act - var resultJson = DotNetDispatcher.Invoke(thisAssemblyName, "InvocableStaticNonVoid", default, null); - var result = JsonSerializer.Deserialize(resultJson, JsonSerializerOptionsProvider.Options); + var jsRuntime = new TestJSRuntime(); + var resultJson = DotNetDispatcher.Invoke(jsRuntime, thisAssemblyName, "InvocableStaticNonVoid", default, null); + var result = JsonSerializer.Deserialize(resultJson, jsRuntime.JsonSerializerOptions); // Assert Assert.Equal("Test", result.StringVal); Assert.Equal(123, result.IntVal); - }); + } [Fact] - public Task CanInvokeStaticNonVoidMethodWithoutCustomIdentifier() => WithJSRuntime(jsRuntime => + public void CanInvokeStaticNonVoidMethodWithoutCustomIdentifier() { // Arrange/Act - var resultJson = DotNetDispatcher.Invoke(thisAssemblyName, nameof(SomePublicType.InvokableMethodWithoutCustomIdentifier), default, null); - var result = JsonSerializer.Deserialize(resultJson, JsonSerializerOptionsProvider.Options); + var jsRuntime = new TestJSRuntime(); + var resultJson = DotNetDispatcher.Invoke(jsRuntime, thisAssemblyName, nameof(SomePublicType.InvokableMethodWithoutCustomIdentifier), default, null); + var result = JsonSerializer.Deserialize(resultJson, jsRuntime.JsonSerializerOptions); // Assert Assert.Equal("InvokableMethodWithoutCustomIdentifier", result.StringVal); Assert.Equal(456, result.IntVal); - }); + } [Fact] - public Task CanInvokeStaticWithParams() => WithJSRuntime(jsRuntime => + public void CanInvokeStaticWithParams() { // Arrange: Track a .NET object to use as an arg + var jsRuntime = new TestJSRuntime(); var arg3 = new TestDTO { IntVal = 999, StringVal = "My string" }; var objectRef = DotNetObjectReference.Create(arg3); jsRuntime.Invoke("unimportant", objectRef); @@ -123,15 +127,15 @@ public Task CanInvokeStaticWithParams() => WithJSRuntime(jsRuntime => new TestDTO { StringVal = "Another string", IntVal = 456 }, new[] { 100, 200 }, objectRef - }, JsonSerializerOptionsProvider.Options); + }, jsRuntime.JsonSerializerOptions); // Act - var resultJson = DotNetDispatcher.Invoke(thisAssemblyName, "InvocableStaticWithParams", default, argsJson); + var resultJson = DotNetDispatcher.Invoke(jsRuntime, thisAssemblyName, "InvocableStaticWithParams", default, argsJson); var result = JsonDocument.Parse(resultJson); var root = result.RootElement; // Assert: First result value marshalled via JSON - var resultDto1 = JsonSerializer.Deserialize(root[0].GetRawText(), JsonSerializerOptionsProvider.Options); + var resultDto1 = JsonSerializer.Deserialize(root[0].GetRawText(), jsRuntime.JsonSerializerOptions); Assert.Equal("ANOTHER STRING", resultDto1.StringVal); Assert.Equal(756, resultDto1.IntVal); @@ -142,15 +146,16 @@ public Task CanInvokeStaticWithParams() => WithJSRuntime(jsRuntime => Assert.False(resultDto2Ref.TryGetProperty(nameof(TestDTO.IntVal), out _)); Assert.True(resultDto2Ref.TryGetProperty(DotNetDispatcher.DotNetObjectRefKey.EncodedUtf8Bytes, out var property)); - var resultDto2 = Assert.IsType>(DotNetObjectReferenceManager.Current.FindDotNetObject(property.GetInt64())).Value; + var resultDto2 = Assert.IsType>(jsRuntime.GetObjectReference(property.GetInt64())).Value; Assert.Equal("MY STRING", resultDto2.StringVal); Assert.Equal(1299, resultDto2.IntVal); - }); + } [Fact] - public Task InvokingWithIncorrectUseOfDotNetObjectRefThrows() => WithJSRuntime(jsRuntime => + public void InvokingWithIncorrectUseOfDotNetObjectRefThrows() { // Arrange + var jsRuntime = new TestJSRuntime(); var method = nameof(SomePublicType.IncorrectDotNetObjectRefUsage); var arg3 = new TestDTO { IntVal = 999, StringVal = "My string" }; var objectRef = DotNetObjectReference.Create(arg3); @@ -162,67 +167,72 @@ public Task InvokingWithIncorrectUseOfDotNetObjectRefThrows() => WithJSRuntime(j new TestDTO { StringVal = "Another string", IntVal = 456 }, new[] { 100, 200 }, objectRef - }, JsonSerializerOptionsProvider.Options); + }, jsRuntime.JsonSerializerOptions); // Act & Assert var ex = Assert.Throws(() => - DotNetDispatcher.Invoke(thisAssemblyName, method, default, argsJson)); + DotNetDispatcher.Invoke(jsRuntime, thisAssemblyName, method, default, argsJson)); Assert.Equal($"In call to '{method}', parameter of type '{nameof(TestDTO)}' at index 3 must be declared as type 'DotNetObjectRef' to receive the incoming value.", ex.Message); - }); + } [Fact] - public Task CanInvokeInstanceVoidMethod() => WithJSRuntime(jsRuntime => + public void CanInvokeInstanceVoidMethod() { // Arrange: Track some instance + var jsRuntime = new TestJSRuntime(); var targetInstance = new SomePublicType(); var objectRef = DotNetObjectReference.Create(targetInstance); jsRuntime.Invoke("unimportant", objectRef); // Act - var resultJson = DotNetDispatcher.Invoke(null, "InvokableInstanceVoid", 1, null); + var resultJson = DotNetDispatcher.Invoke(jsRuntime, null, "InvokableInstanceVoid", 1, null); // Assert Assert.Null(resultJson); Assert.True(targetInstance.DidInvokeMyInvocableInstanceVoid); - }); + } [Fact] - public Task CanInvokeBaseInstanceVoidMethod() => WithJSRuntime(jsRuntime => + public void CanInvokeBaseInstanceVoidMethod() { // Arrange: Track some instance + var jsRuntime = new TestJSRuntime(); var targetInstance = new DerivedClass(); var objectRef = DotNetObjectReference.Create(targetInstance); jsRuntime.Invoke("unimportant", objectRef); // Act - var resultJson = DotNetDispatcher.Invoke(null, "BaseClassInvokableInstanceVoid", 1, null); + var resultJson = DotNetDispatcher.Invoke(jsRuntime, null, "BaseClassInvokableInstanceVoid", 1, null); // Assert Assert.Null(resultJson); Assert.True(targetInstance.DidInvokeMyBaseClassInvocableInstanceVoid); - }); + } [Fact] - public Task DotNetObjectReferencesCanBeDisposed() => WithJSRuntime(jsRuntime => + public void DotNetObjectReferencesCanBeDisposed() { // Arrange + var jsRuntime = new TestJSRuntime(); var targetInstance = new SomePublicType(); var objectRef = DotNetObjectReference.Create(targetInstance); + jsRuntime.Invoke("unimportant", objectRef); // Act - DotNetDispatcher.BeginInvokeDotNet(null, null, "__Dispose", objectRef.ObjectId, null); + DotNetDispatcher.BeginInvokeDotNet(jsRuntime, null, null, "__Dispose", objectRef.ObjectId, null); // Assert Assert.True(objectRef.Disposed); - }); + } [Fact] - public Task CannotUseDotNetObjectRefAfterDisposal() => WithJSRuntime(jsRuntime => + public void CannotUseDotNetObjectRefAfterDisposal() { // This test addresses the case where the developer calls objectRef.Dispose() // from .NET code, as opposed to .dispose() from JS code // Arrange: Track some instance, then dispose it + var jsRuntime = new TestJSRuntime(); var targetInstance = new SomePublicType(); var objectRef = DotNetObjectReference.Create(targetInstance); jsRuntime.Invoke("unimportant", objectRef); @@ -230,17 +240,18 @@ public Task CannotUseDotNetObjectRefAfterDisposal() => WithJSRuntime(jsRuntime = // Act/Assert var ex = Assert.Throws( - () => DotNetDispatcher.Invoke(null, "InvokableInstanceVoid", 1, null)); + () => DotNetDispatcher.Invoke(jsRuntime, null, "InvokableInstanceVoid", 1, null)); Assert.StartsWith("There is no tracked object with id '1'.", ex.Message); - }); + } [Fact] - public Task CannotUseDotNetObjectRefAfterReleaseDotNetObject() => WithJSRuntime(jsRuntime => + public void CannotUseDotNetObjectRefAfterReleaseDotNetObject() { // This test addresses the case where the developer calls .dispose() // from JS code, as opposed to objectRef.Dispose() from .NET code // Arrange: Track some instance, then dispose it + var jsRuntime = new TestJSRuntime(); var targetInstance = new SomePublicType(); var objectRef = DotNetObjectReference.Create(targetInstance); jsRuntime.Invoke("unimportant", objectRef); @@ -248,80 +259,85 @@ public Task CannotUseDotNetObjectRefAfterReleaseDotNetObject() => WithJSRuntime( // Act/Assert var ex = Assert.Throws( - () => DotNetDispatcher.Invoke(null, "InvokableInstanceVoid", 1, null)); + () => DotNetDispatcher.Invoke(jsRuntime, null, "InvokableInstanceVoid", 1, null)); Assert.StartsWith("There is no tracked object with id '1'.", ex.Message); - }); + } [Fact] - public Task EndInvoke_WithSuccessValue() => WithJSRuntime(jsRuntime => + public void EndInvoke_WithSuccessValue() { // Arrange + var jsRuntime = new TestJSRuntime(); var testDTO = new TestDTO { StringVal = "Hello", IntVal = 4 }; var task = jsRuntime.InvokeAsync("unimportant"); - var argsJson = JsonSerializer.Serialize(new object[] { jsRuntime.LastInvocationAsyncHandle, true, testDTO }, JsonSerializerOptionsProvider.Options); + var argsJson = JsonSerializer.Serialize(new object[] { jsRuntime.LastInvocationAsyncHandle, true, testDTO }, jsRuntime.JsonSerializerOptions); // Act - DotNetDispatcher.EndInvokeJS(argsJson); + DotNetDispatcher.EndInvokeJS(jsRuntime, argsJson); // Assert Assert.True(task.IsCompletedSuccessfully); var result = task.Result; Assert.Equal(testDTO.StringVal, result.StringVal); Assert.Equal(testDTO.IntVal, result.IntVal); - }); + } [Fact] - public Task EndInvoke_WithErrorString() => WithJSRuntime(async jsRuntime => + public async Task EndInvoke_WithErrorString() { // Arrange + var jsRuntime = new TestJSRuntime(); var expected = "Some error"; var task = jsRuntime.InvokeAsync("unimportant"); - var argsJson = JsonSerializer.Serialize(new object[] { jsRuntime.LastInvocationAsyncHandle, false, expected }, JsonSerializerOptionsProvider.Options); + var argsJson = JsonSerializer.Serialize(new object[] { jsRuntime.LastInvocationAsyncHandle, false, expected }, jsRuntime.JsonSerializerOptions); // Act - DotNetDispatcher.EndInvokeJS(argsJson); + DotNetDispatcher.EndInvokeJS(jsRuntime, argsJson); // Assert var ex = await Assert.ThrowsAsync(async () => await task); Assert.Equal(expected, ex.Message); - }); + } [Fact(Skip = "https://github.com/aspnet/AspNetCore/issues/12357")] - public Task EndInvoke_AfterCancel() => WithJSRuntime(jsRuntime => + public void EndInvoke_AfterCancel() { // Arrange + var jsRuntime = new TestJSRuntime(); var testDTO = new TestDTO { StringVal = "Hello", IntVal = 4 }; var cts = new CancellationTokenSource(); var task = jsRuntime.InvokeAsync("unimportant", cts.Token); - var argsJson = JsonSerializer.Serialize(new object[] { jsRuntime.LastInvocationAsyncHandle, true, testDTO }, JsonSerializerOptionsProvider.Options); + var argsJson = JsonSerializer.Serialize(new object[] { jsRuntime.LastInvocationAsyncHandle, true, testDTO }, jsRuntime.JsonSerializerOptions); // Act cts.Cancel(); - DotNetDispatcher.EndInvokeJS(argsJson); + DotNetDispatcher.EndInvokeJS(jsRuntime, argsJson); // Assert Assert.True(task.IsCanceled); - }); + } [Fact] - public Task EndInvoke_WithNullError() => WithJSRuntime(async jsRuntime => + public async Task EndInvoke_WithNullError() { // Arrange + var jsRuntime = new TestJSRuntime(); var task = jsRuntime.InvokeAsync("unimportant"); - var argsJson = JsonSerializer.Serialize(new object[] { jsRuntime.LastInvocationAsyncHandle, false, null }, JsonSerializerOptionsProvider.Options); + var argsJson = JsonSerializer.Serialize(new object[] { jsRuntime.LastInvocationAsyncHandle, false, null }, jsRuntime.JsonSerializerOptions); // Act - DotNetDispatcher.EndInvokeJS(argsJson); + DotNetDispatcher.EndInvokeJS(jsRuntime, argsJson); // Assert var ex = await Assert.ThrowsAsync(async () => await task); Assert.Empty(ex.Message); - }); + } [Fact] - public Task CanInvokeInstanceMethodWithParams() => WithJSRuntime(jsRuntime => + public void CanInvokeInstanceMethodWithParams() { // Arrange: Track some instance plus another object we'll pass as a param + var jsRuntime = new TestJSRuntime(); var targetInstance = new SomePublicType(); var arg2 = new TestDTO { IntVal = 1234, StringVal = "My string" }; jsRuntime.Invoke("unimportant", @@ -330,38 +346,40 @@ public Task CanInvokeInstanceMethodWithParams() => WithJSRuntime(jsRuntime => var argsJson = "[\"myvalue\",{\"__dotNetObject\":2}]"; // Act - var resultJson = DotNetDispatcher.Invoke(null, "InvokableInstanceMethod", 1, argsJson); + var resultJson = DotNetDispatcher.Invoke(jsRuntime, null, "InvokableInstanceMethod", 1, argsJson); // Assert Assert.Equal("[\"You passed myvalue\",{\"__dotNetObject\":3}]", resultJson); - var resultDto = ((DotNetObjectReference)jsRuntime.ObjectRefManager.FindDotNetObject(3)).Value; + var resultDto = ((DotNetObjectReference)jsRuntime.GetObjectReference(3)).Value; Assert.Equal(1235, resultDto.IntVal); Assert.Equal("MY STRING", resultDto.StringVal); - }); + } [Fact] - public Task CannotInvokeWithFewerNumberOfParameters() => WithJSRuntime(jsRuntime => + public void CannotInvokeWithFewerNumberOfParameters() { // Arrange + var jsRuntime = new TestJSRuntime(); var argsJson = JsonSerializer.Serialize(new object[] { new TestDTO { StringVal = "Another string", IntVal = 456 }, new[] { 100, 200 }, - }, JsonSerializerOptionsProvider.Options); + }, jsRuntime.JsonSerializerOptions); // Act/Assert var ex = Assert.Throws(() => { - DotNetDispatcher.Invoke(thisAssemblyName, "InvocableStaticWithParams", default, argsJson); + DotNetDispatcher.Invoke(jsRuntime, thisAssemblyName, "InvocableStaticWithParams", default, argsJson); }); Assert.Equal("The call to 'InvocableStaticWithParams' expects '3' parameters, but received '2'.", ex.Message); - }); + } [Fact] - public Task CannotInvokeWithMoreParameters() => WithJSRuntime(jsRuntime => + public void CannotInvokeWithMoreParameters() { // Arrange + var jsRuntime = new TestJSRuntime(); var objectRef = DotNetObjectReference.Create(new TestDTO { IntVal = 4 }); var argsJson = JsonSerializer.Serialize(new object[] { @@ -369,21 +387,22 @@ public Task CannotInvokeWithMoreParameters() => WithJSRuntime(jsRuntime => new[] { 100, 200 }, objectRef, 7, - }, JsonSerializerOptionsProvider.Options); + }, jsRuntime.JsonSerializerOptions); // Act/Assert var ex = Assert.Throws(() => { - DotNetDispatcher.Invoke(thisAssemblyName, "InvocableStaticWithParams", default, argsJson); + DotNetDispatcher.Invoke(jsRuntime, thisAssemblyName, "InvocableStaticWithParams", default, argsJson); }); Assert.Equal("Unexpected JSON token Number. Ensure that the call to `InvocableStaticWithParams' is supplied with exactly '3' parameters.", ex.Message); - }); + } [Fact] - public Task CanInvokeAsyncMethod() => WithJSRuntime(async jsRuntime => + public async Task CanInvokeAsyncMethod() { // Arrange: Track some instance plus another object we'll pass as a param + var jsRuntime = new TestJSRuntime(); var targetInstance = new SomePublicType(); var arg2 = new TestDTO { IntVal = 1234, StringVal = "My string" }; var arg1Ref = DotNetObjectReference.Create(targetInstance); @@ -395,12 +414,12 @@ public Task CanInvokeAsyncMethod() => WithJSRuntime(async jsRuntime => { new TestDTO { IntVal = 1000, StringVal = "String via JSON" }, arg2Ref, - }, JsonSerializerOptionsProvider.Options); + }, jsRuntime.JsonSerializerOptions); // Act var callId = "123"; var resultTask = jsRuntime.NextInvocationTask; - DotNetDispatcher.BeginInvokeDotNet(callId, null, "InvokableAsyncMethod", 1, argsJson); + DotNetDispatcher.BeginInvokeDotNet(jsRuntime, callId, null, "InvokableAsyncMethod", 1, argsJson); await resultTask; // Assert: Correct completion information @@ -417,17 +436,18 @@ public Task CanInvokeAsyncMethod() => WithJSRuntime(async jsRuntime => var resultDto2 = resultDto2Ref.Value; Assert.Equal("MY STRING", resultDto2.StringVal); Assert.Equal(2468, resultDto2.IntVal); - }); + } [Fact] - public Task CanInvokeSyncThrowingMethod() => WithJSRuntime(async jsRuntime => + public async Task CanInvokeSyncThrowingMethod() { // Arrange + var jsRuntime = new TestJSRuntime(); // Act var callId = "123"; var resultTask = jsRuntime.NextInvocationTask; - DotNetDispatcher.BeginInvokeDotNet(callId, thisAssemblyName, nameof(ThrowingClass.ThrowingMethod), default, default); + DotNetDispatcher.BeginInvokeDotNet(jsRuntime, callId, thisAssemblyName, nameof(ThrowingClass.ThrowingMethod), default, default); await resultTask; // This won't throw, it sets properties on the jsRuntime. @@ -439,17 +459,18 @@ public Task CanInvokeSyncThrowingMethod() => WithJSRuntime(async jsRuntime => // https://github.com/aspnet/AspNetCore/issues/8612 var exception = jsRuntime.LastCompletionResult is ExceptionDispatchInfo edi ? edi.SourceException.ToString() : null; Assert.Contains(nameof(ThrowingClass.ThrowingMethod), exception); - }); + } [Fact] - public Task CanInvokeAsyncThrowingMethod() => WithJSRuntime(async jsRuntime => + public async Task CanInvokeAsyncThrowingMethod() { // Arrange + var jsRuntime = new TestJSRuntime(); // Act var callId = "123"; var resultTask = jsRuntime.NextInvocationTask; - DotNetDispatcher.BeginInvokeDotNet(callId, thisAssemblyName, nameof(ThrowingClass.AsyncThrowingMethod), default, default); + DotNetDispatcher.BeginInvokeDotNet(jsRuntime, callId, thisAssemblyName, nameof(ThrowingClass.AsyncThrowingMethod), default, default); await resultTask; // This won't throw, it sets properties on the jsRuntime. @@ -461,15 +482,16 @@ public Task CanInvokeAsyncThrowingMethod() => WithJSRuntime(async jsRuntime => // https://github.com/aspnet/AspNetCore/issues/8612 var exception = jsRuntime.LastCompletionResult is ExceptionDispatchInfo edi ? edi.SourceException.ToString() : null; Assert.Contains(nameof(ThrowingClass.AsyncThrowingMethod), exception); - }); + } [Fact] - public Task BeginInvoke_ThrowsWithInvalidArgsJson_WithCallId() => WithJSRuntime(async jsRuntime => + public async Task BeginInvoke_ThrowsWithInvalidArgsJson_WithCallId() { // Arrange + var jsRuntime = new TestJSRuntime(); var callId = "123"; var resultTask = jsRuntime.NextInvocationTask; - DotNetDispatcher.BeginInvokeDotNet(callId, thisAssemblyName, "InvocableStaticWithParams", default, "not json"); + DotNetDispatcher.BeginInvokeDotNet(jsRuntime, callId, thisAssemblyName, "InvocableStaticWithParams", default, "not json"); await resultTask; // This won't throw, it sets properties on the jsRuntime. @@ -478,29 +500,30 @@ public Task BeginInvoke_ThrowsWithInvalidArgsJson_WithCallId() => WithJSRuntime( Assert.False(jsRuntime.LastCompletionStatus); // Fails var result = Assert.IsType(jsRuntime.LastCompletionResult); Assert.Contains("JsonReaderException: '<' is an invalid start of a value.", result.SourceException.ToString()); - }); + } [Fact] - public Task BeginInvoke_ThrowsWithInvalid_DotNetObjectRef() => WithJSRuntime(jsRuntime => + public void BeginInvoke_ThrowsWithInvalid_DotNetObjectRef() { // Arrange + var jsRuntime = new TestJSRuntime(); var callId = "123"; var resultTask = jsRuntime.NextInvocationTask; - DotNetDispatcher.BeginInvokeDotNet(callId, null, "InvokableInstanceVoid", 1, null); + DotNetDispatcher.BeginInvokeDotNet(jsRuntime, callId, null, "InvokableInstanceVoid", 1, null); // Assert Assert.Equal(callId, jsRuntime.LastCompletionCallId); Assert.False(jsRuntime.LastCompletionStatus); // Fails var result = Assert.IsType(jsRuntime.LastCompletionResult); - Assert.StartsWith("System.ArgumentException: There is no tracked object with id '1'. Perhaps the DotNetObjectRef instance was already disposed.", result.SourceException.ToString()); - }); + Assert.StartsWith("System.ArgumentException: There is no tracked object with id '1'. Perhaps the DotNetObjectReference instance was already disposed.", result.SourceException.ToString()); + } [Theory] [InlineData("")] [InlineData("")] public void ParseArguments_ThrowsIfJsonIsInvalid(string arguments) { - Assert.ThrowsAny(() => DotNetDispatcher.ParseArguments("SomeMethod", arguments, new[] { typeof(string) })); + Assert.ThrowsAny(() => DotNetDispatcher.ParseArguments(new TestJSRuntime(), "SomeMethod", arguments, new[] { typeof(string) })); } [Theory] @@ -509,7 +532,7 @@ public void ParseArguments_ThrowsIfJsonIsInvalid(string arguments) public void ParseArguments_ThrowsIfTheArgsJsonIsNotArray(string arguments) { // Act & Assert - Assert.ThrowsAny(() => DotNetDispatcher.ParseArguments("SomeMethod", arguments, new[] { typeof(string) })); + Assert.ThrowsAny(() => DotNetDispatcher.ParseArguments(new TestJSRuntime(), "SomeMethod", arguments, new[] { typeof(string) })); } [Theory] @@ -518,7 +541,7 @@ public void ParseArguments_ThrowsIfTheArgsJsonIsNotArray(string arguments) public void ParseArguments_ThrowsIfTheArgsJsonIsInvalidArray(string arguments) { // Act & Assert - Assert.ThrowsAny(() => DotNetDispatcher.ParseArguments("SomeMethod", arguments, new[] { typeof(string) })); + Assert.ThrowsAny(() => DotNetDispatcher.ParseArguments(new TestJSRuntime(), "SomeMethod", arguments, new[] { typeof(string) })); } [Fact] @@ -528,7 +551,7 @@ public void ParseArguments_Works() var arguments = "[\"Hello\", 2]"; // Act - var result = DotNetDispatcher.ParseArguments("SomeMethod", arguments, new[] { typeof(string), typeof(int), }); + var result = DotNetDispatcher.ParseArguments(new TestJSRuntime(), "SomeMethod", arguments, new[] { typeof(string), typeof(int), }); // Assert Assert.Equal(new object[] { "Hello", 2 }, result); @@ -541,7 +564,7 @@ public void ParseArguments_SingleArgument() var arguments = "[{\"IntVal\": 7}]"; // Act - var result = DotNetDispatcher.ParseArguments("SomeMethod", arguments, new[] { typeof(TestDTO), }); + var result = DotNetDispatcher.ParseArguments(new TestJSRuntime(), "SomeMethod", arguments, new[] { typeof(TestDTO), }); // Assert var value = Assert.IsType(Assert.Single(result)); @@ -556,7 +579,7 @@ public void ParseArguments_NullArgument() var arguments = "[4, null]"; // Act - var result = DotNetDispatcher.ParseArguments("SomeMethod", arguments, new[] { typeof(int), typeof(TestDTO), }); + var result = DotNetDispatcher.ParseArguments(new TestJSRuntime(), "SomeMethod", arguments, new[] { typeof(int), typeof(TestDTO), }); // Assert Assert.Collection( @@ -573,92 +596,72 @@ public void ParseArguments_Throws_WithIncorrectDotNetObjectRefUsage() var arguments = "[4, {\"__dotNetObject\": 7}]"; // Act - var ex = Assert.Throws(() => DotNetDispatcher.ParseArguments(method, arguments, new[] { typeof(int), typeof(TestDTO), })); + var ex = Assert.Throws(() => DotNetDispatcher.ParseArguments(new TestJSRuntime(), method, arguments, new[] { typeof(int), typeof(TestDTO), })); // Assert Assert.Equal($"In call to '{method}', parameter of type '{nameof(TestDTO)}' at index 2 must be declared as type 'DotNetObjectRef' to receive the incoming value.", ex.Message); } [Fact] - public void ParseEndInvokeArguments_ThrowsIfJsonIsEmptyString() + public void EndInvokeJS_ThrowsIfJsonIsEmptyString() { - Assert.ThrowsAny(() => DotNetDispatcher.ParseEndInvokeArguments(new TestJSRuntime(), "")); + Assert.ThrowsAny(() => DotNetDispatcher.EndInvokeJS(new TestJSRuntime(), "")); } [Fact] - public void ParseEndInvokeArguments_ThrowsIfJsonIsNotArray() + public void EndInvokeJS_ThrowsIfJsonIsNotArray() { - Assert.ThrowsAny(() => DotNetDispatcher.ParseEndInvokeArguments(new TestJSRuntime(), "{\"key\": \"value\"}")); + Assert.ThrowsAny(() => DotNetDispatcher.EndInvokeJS(new TestJSRuntime(), "{\"key\": \"value\"}")); } [Fact] - public void ParseEndInvokeArguments_ThrowsIfJsonArrayIsInComplete() + public void EndInvokeJS_ThrowsIfJsonArrayIsInComplete() { - Assert.ThrowsAny(() => DotNetDispatcher.ParseEndInvokeArguments(new TestJSRuntime(), "[7, false")); + Assert.ThrowsAny(() => DotNetDispatcher.EndInvokeJS(new TestJSRuntime(), "[7, false")); } [Fact] - public void ParseEndInvokeArguments_ThrowsIfJsonArrayHasMoreThan3Arguments() + public void EndInvokeJS_ThrowsIfJsonArrayHasMoreThan3Arguments() { - Assert.ThrowsAny(() => DotNetDispatcher.ParseEndInvokeArguments(new TestJSRuntime(), "[7, false, \"Hello\", 5]")); + Assert.ThrowsAny(() => DotNetDispatcher.EndInvokeJS(new TestJSRuntime(), "[7, false, \"Hello\", 5]")); } [Fact] - public void ParseEndInvokeArguments_Works() + public void EndInvokeJS_Works() { var jsRuntime = new TestJSRuntime(); var task = jsRuntime.InvokeAsync("somemethod"); - DotNetDispatcher.ParseEndInvokeArguments(jsRuntime, $"[{jsRuntime.LastInvocationAsyncHandle}, true, {{\"intVal\": 7}}]"); + DotNetDispatcher.EndInvokeJS(jsRuntime, $"[{jsRuntime.LastInvocationAsyncHandle}, true, {{\"intVal\": 7}}]"); Assert.True(task.IsCompletedSuccessfully); Assert.Equal(7, task.Result.IntVal); } [Fact] - public void ParseEndInvokeArguments_WithArrayValue() + public void EndInvokeJS_WithArrayValue() { var jsRuntime = new TestJSRuntime(); var task = jsRuntime.InvokeAsync("somemethod"); - DotNetDispatcher.ParseEndInvokeArguments(jsRuntime, $"[{jsRuntime.LastInvocationAsyncHandle}, true, [1, 2, 3]]"); + DotNetDispatcher.EndInvokeJS(jsRuntime, $"[{jsRuntime.LastInvocationAsyncHandle}, true, [1, 2, 3]]"); Assert.True(task.IsCompletedSuccessfully); Assert.Equal(new[] { 1, 2, 3 }, task.Result); } [Fact] - public void ParseEndInvokeArguments_WithNullValue() + public void EndInvokeJS_WithNullValue() { var jsRuntime = new TestJSRuntime(); var task = jsRuntime.InvokeAsync("somemethod"); - DotNetDispatcher.ParseEndInvokeArguments(jsRuntime, $"[{jsRuntime.LastInvocationAsyncHandle}, true, null]"); + DotNetDispatcher.EndInvokeJS(jsRuntime, $"[{jsRuntime.LastInvocationAsyncHandle}, true, null]"); Assert.True(task.IsCompletedSuccessfully); Assert.Null(task.Result); } - Task WithJSRuntime(Action testCode) - { - return WithJSRuntime(jsRuntime => - { - testCode(jsRuntime); - return Task.CompletedTask; - }); - } - - async Task WithJSRuntime(Func testCode) - { - // Since the tests rely on the asynclocal JSRuntime.Current, ensure we - // are on a distinct async context with a non-null JSRuntime.Current - await Task.Yield(); - - var runtime = new TestJSRuntime(); - JSRuntime.SetCurrentJSRuntime(runtime); - await testCode(runtime); - } - internal class SomeInteralType { [JSInvokable("MethodOnInternalType")] public void MyMethod() { } diff --git a/src/JSInterop/Microsoft.JSInterop/test/Infrastructure/DotNetObjectReferenceJsonConverterTest.cs b/src/JSInterop/Microsoft.JSInterop/test/Infrastructure/DotNetObjectReferenceJsonConverterTest.cs index 541ad2b025d3..8d055aea2cae 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/Infrastructure/DotNetObjectReferenceJsonConverterTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/Infrastructure/DotNetObjectReferenceJsonConverterTest.cs @@ -2,91 +2,93 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Text.Json; -using System.Threading.Tasks; using Xunit; -using static Microsoft.JSInterop.TestJSRuntime; namespace Microsoft.JSInterop.Infrastructure { public class DotNetObjectReferenceJsonConverterTest { + private readonly JSRuntime JSRuntime = new TestJSRuntime(); + private JsonSerializerOptions JsonSerializerOptions => JSRuntime.JsonSerializerOptions; + [Fact] - public Task Read_Throws_IfJsonIsMissingDotNetObjectProperty() => WithJSRuntime(_ => + public void Read_Throws_IfJsonIsMissingDotNetObjectProperty() { // Arrange + var jsRuntime = new TestJSRuntime(); var dotNetObjectRef = DotNetObjectReference.Create(new TestModel()); var json = "{}"; // Act & Assert - var ex = Assert.Throws(() => JsonSerializer.Deserialize>(json)); + var ex = Assert.Throws(() => JsonSerializer.Deserialize>(json, JsonSerializerOptions)); Assert.Equal("Required property __dotNetObject not found.", ex.Message); - }); + } [Fact] - public Task Read_Throws_IfJsonContainsUnknownContent() => WithJSRuntime(_ => + public void Read_Throws_IfJsonContainsUnknownContent() { // Arrange + var jsRuntime = new TestJSRuntime(); var dotNetObjectRef = DotNetObjectReference.Create(new TestModel()); var json = "{\"foo\":2}"; // Act & Assert - var ex = Assert.Throws(() => JsonSerializer.Deserialize>(json)); + var ex = Assert.Throws(() => JsonSerializer.Deserialize>(json, JsonSerializerOptions)); Assert.Equal("Unexcepted JSON property foo.", ex.Message); - }); + } [Fact] - public Task Read_Throws_IfJsonIsIncomplete() => WithJSRuntime(_ => + public void Read_Throws_IfJsonIsIncomplete() { // Arrange var input = new TestModel(); var dotNetObjectRef = DotNetObjectReference.Create(input); - var objectId = dotNetObjectRef.ObjectId; + var objectId = JSRuntime.TrackObjectReference(dotNetObjectRef); var json = $"{{\"__dotNetObject\":{objectId}"; // Act & Assert - var ex = Record.Exception(() => JsonSerializer.Deserialize>(json)); + var ex = Record.Exception(() => JsonSerializer.Deserialize>(json, JsonSerializerOptions)); Assert.IsAssignableFrom(ex); - }); + } [Fact] - public Task Read_Throws_IfDotNetObjectIdAppearsMultipleTimes() => WithJSRuntime(_ => + public void Read_Throws_IfDotNetObjectIdAppearsMultipleTimes() { // Arrange var input = new TestModel(); var dotNetObjectRef = DotNetObjectReference.Create(input); - var objectId = dotNetObjectRef.ObjectId; + var objectId = JSRuntime.TrackObjectReference(dotNetObjectRef); var json = $"{{\"__dotNetObject\":{objectId},\"__dotNetObject\":{objectId}}}"; // Act & Assert - var ex = Record.Exception(() => JsonSerializer.Deserialize>(json)); + var ex = Record.Exception(() => JsonSerializer.Deserialize>(json, JsonSerializerOptions)); Assert.IsAssignableFrom(ex); - }); + } [Fact] - public Task Read_ReadsJson() => WithJSRuntime(_ => + public void Read_ReadsJson() { // Arrange var input = new TestModel(); var dotNetObjectRef = DotNetObjectReference.Create(input); - var objectId = dotNetObjectRef.ObjectId; + var objectId = JSRuntime.TrackObjectReference(dotNetObjectRef); var json = $"{{\"__dotNetObject\":{objectId}}}"; // Act - var deserialized = JsonSerializer.Deserialize>(json); + var deserialized = JsonSerializer.Deserialize>(json, JsonSerializerOptions); // Assert Assert.Same(input, deserialized.Value); Assert.Equal(objectId, deserialized.ObjectId); - }); - + } [Fact] - public Task Read_ReturnsTheCorrectInstance() => WithJSRuntime(_ => + public void Read_ReturnsTheCorrectInstance() { // Arrange // Track a few instances and verify that the deserialized value returns the correct value. @@ -95,23 +97,23 @@ public Task Read_ReturnsTheCorrectInstance() => WithJSRuntime(_ => var ref1 = DotNetObjectReference.Create(instance1); var ref2 = DotNetObjectReference.Create(instance2); - var json = $"[{{\"__dotNetObject\":{ref2.ObjectId}}},{{\"__dotNetObject\":{ref1.ObjectId}}}]"; + var json = $"[{{\"__dotNetObject\":{JSRuntime.TrackObjectReference(ref1)}}},{{\"__dotNetObject\":{JSRuntime.TrackObjectReference(ref2)}}}]"; // Act - var deserialized = JsonSerializer.Deserialize[]>(json); + var deserialized = JsonSerializer.Deserialize[]>(json, JsonSerializerOptions); // Assert - Assert.Same(instance2, deserialized[0].Value); - Assert.Same(instance1, deserialized[1].Value); - }); + Assert.Same(instance1, deserialized[0].Value); + Assert.Same(instance2, deserialized[1].Value); + } [Fact] - public Task Read_ReadsJson_WithFormatting() => WithJSRuntime(_ => + public void Read_ReadsJson_WithFormatting() { // Arrange var input = new TestModel(); var dotNetObjectRef = DotNetObjectReference.Create(input); - var objectId = dotNetObjectRef.ObjectId; + var objectId = JSRuntime.TrackObjectReference(dotNetObjectRef); var json = @$"{{ @@ -119,27 +121,27 @@ public Task Read_ReadsJson_WithFormatting() => WithJSRuntime(_ => }}"; // Act - var deserialized = JsonSerializer.Deserialize>(json); + var deserialized = JsonSerializer.Deserialize>(json, JsonSerializerOptions); // Assert Assert.Same(input, deserialized.Value); Assert.Equal(objectId, deserialized.ObjectId); - }); + } [Fact] - public Task WriteJsonTwice_KeepsObjectId() => WithJSRuntime(_ => + public void WriteJsonTwice_KeepsObjectId() { // Arrange var dotNetObjectRef = DotNetObjectReference.Create(new TestModel()); // Act - var json1 = JsonSerializer.Serialize(dotNetObjectRef); - var json2 = JsonSerializer.Serialize(dotNetObjectRef); + var json1 = JsonSerializer.Serialize(dotNetObjectRef, JsonSerializerOptions); + var json2 = JsonSerializer.Serialize(dotNetObjectRef, JsonSerializerOptions); // Assert Assert.Equal($"{{\"__dotNetObject\":{dotNetObjectRef.ObjectId}}}", json1); Assert.Equal(json1, json2); - }); + } private class TestModel { diff --git a/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeTest.cs index 40541012589f..a1caff595b7b 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeTest.cs @@ -18,7 +18,6 @@ public void DispatchesSyncCallsAndDeserializesResults() { NextResultJson = "{\"intValue\":123,\"stringValue\":\"Hello\"}" }; - JSRuntime.SetCurrentJSRuntime(runtime); // Act var syncResult = runtime.Invoke("test identifier 1", "arg1", 123, true); @@ -36,7 +35,6 @@ public void SerializesDotNetObjectWrappersInKnownFormat() { // Arrange var runtime = new TestJSInProcessRuntime { NextResultJson = null }; - JSRuntime.SetCurrentJSRuntime(runtime); var obj1 = new object(); var obj2 = new object(); var obj3 = new object(); @@ -60,9 +58,9 @@ public void SerializesDotNetObjectWrappersInKnownFormat() Assert.Equal("[{\"__dotNetObject\":1},{\"obj2\":{\"__dotNetObject\":2},\"obj3\":{\"__dotNetObject\":3}}]", call.ArgsJson); // Assert: Objects were tracked - Assert.Same(obj1, runtime.ObjectRefManager.FindDotNetObject(1).Value); - Assert.Same(obj2, runtime.ObjectRefManager.FindDotNetObject(2).Value); - Assert.Same(obj3, runtime.ObjectRefManager.FindDotNetObject(3).Value); + Assert.Same(obj1, runtime.GetObjectReference(1).Value); + Assert.Same(obj2, runtime.GetObjectReference(2).Value); + Assert.Same(obj3, runtime.GetObjectReference(3).Value); } [Fact] @@ -73,7 +71,6 @@ public void SyncCallResultCanIncludeDotNetObjects() { NextResultJson = "[{\"__dotNetObject\":2},{\"__dotNetObject\":1}]" }; - JSRuntime.SetCurrentJSRuntime(runtime); var obj1 = new object(); var obj2 = new object(); diff --git a/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeTest.cs index 4e65ddeb0fe2..b102ecc0b563 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeTest.cs @@ -14,23 +14,6 @@ namespace Microsoft.JSInterop { public class JSRuntimeTest { - #region this will be removed eventually - [Fact] - public async Task CanHaveDistinctJSRuntimeInstancesInEachAsyncContext() - { - var tasks = Enumerable.Range(0, 20).Select(async _ => - { - var jsRuntime = new TestJSRuntime(); - JSRuntime.SetCurrentJSRuntime(jsRuntime); - await Task.Delay(50).ConfigureAwait(false); - Assert.Same(jsRuntime, JSRuntime.Current); - }); - - await Task.WhenAll(tasks); - Assert.Null(JSRuntime.Current); - } - #endregion - [Fact] public void DispatchesAsyncCallsWithDistinctAsyncHandles() { @@ -274,7 +257,6 @@ public void SerializesDotNetObjectWrappersInKnownFormat() { // Arrange var runtime = new TestJSRuntime(); - JSRuntime.SetCurrentJSRuntime(runtime); var obj1 = new object(); var obj2 = new object(); var obj3 = new object(); @@ -296,15 +278,15 @@ public void SerializesDotNetObjectWrappersInKnownFormat() // Assert: Serialized as expected var call = runtime.BeginInvokeCalls.Single(); Assert.Equal("test identifier", call.Identifier); - Assert.Equal("[{\"__dotNetObject\":1},{\"obj2\":{\"__dotNetObject\":3},\"obj3\":{\"__dotNetObject\":4},\"obj1SameRef\":{\"__dotNetObject\":1},\"obj1DifferentRef\":{\"__dotNetObject\":2}}]", call.ArgsJson); + Assert.Equal("[{\"__dotNetObject\":1},{\"obj2\":{\"__dotNetObject\":2},\"obj3\":{\"__dotNetObject\":3},\"obj1SameRef\":{\"__dotNetObject\":1},\"obj1DifferentRef\":{\"__dotNetObject\":4}}]", call.ArgsJson); // Assert: Objects were tracked - Assert.Same(obj1Ref, runtime.ObjectRefManager.FindDotNetObject(1)); + Assert.Same(obj1Ref, runtime.GetObjectReference(1)); Assert.Same(obj1, obj1Ref.Value); - Assert.NotSame(obj1Ref, runtime.ObjectRefManager.FindDotNetObject(2)); - Assert.Same(obj1, runtime.ObjectRefManager.FindDotNetObject(2).Value); - Assert.Same(obj2, runtime.ObjectRefManager.FindDotNetObject(3).Value); - Assert.Same(obj3, runtime.ObjectRefManager.FindDotNetObject(4).Value); + Assert.NotSame(obj1Ref, runtime.GetObjectReference(2)); + Assert.Same(obj2, runtime.GetObjectReference(2).Value); + Assert.Same(obj3, runtime.GetObjectReference(3).Value); + Assert.Same(obj1, runtime.GetObjectReference(4).Value); } [Fact] diff --git a/src/JSInterop/Microsoft.JSInterop/test/TestJSRuntime.cs b/src/JSInterop/Microsoft.JSInterop/test/TestJSRuntime.cs index 48782fc4df8f..740f02b8da9f 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/TestJSRuntime.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/TestJSRuntime.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Threading.Tasks; namespace Microsoft.JSInterop { @@ -17,16 +16,5 @@ protected internal override void EndInvokeDotNet(string callId, bool success, ob { throw new NotImplementedException(); } - - public static async Task WithJSRuntime(Action testCode) - { - // Since the tests rely on the asynclocal JSRuntime.Current, ensure we - // are on a distinct async context with a non-null JSRuntime.Current - await Task.Yield(); - - var runtime = new TestJSRuntime(); - JSRuntime.SetCurrentJSRuntime(runtime); - testCode(runtime); - } } } diff --git a/src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.netstandard2.0.cs b/src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.netstandard2.0.cs index 2e4defd1b7be..0996795ca326 100644 --- a/src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.netstandard2.0.cs +++ b/src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.netstandard2.0.cs @@ -8,6 +8,7 @@ public partial class MonoWebAssemblyJSRuntime : Microsoft.JSInterop.JSInProcessR public MonoWebAssemblyJSRuntime() { } protected override void BeginInvokeJS(long asyncHandle, string identifier, string argsJson) { } protected override void EndInvokeDotNet(string callId, bool success, object resultOrError, string assemblyName, string methodIdentifier, long dotNetObjectId) { } + protected static void Initialize(Mono.WebAssembly.Interop.MonoWebAssemblyJSRuntime jsRuntime) { } protected override string InvokeJS(string identifier, string argsJson) { throw null; } public TRes InvokeUnmarshalled(string identifier) { throw null; } public TRes InvokeUnmarshalled(string identifier, T0 arg0) { throw null; } diff --git a/src/JSInterop/Mono.WebAssembly.Interop/src/MonoWebAssemblyJSRuntime.cs b/src/JSInterop/Mono.WebAssembly.Interop/src/MonoWebAssemblyJSRuntime.cs index 0e292a3e3ce4..b6b01d754d65 100644 --- a/src/JSInterop/Mono.WebAssembly.Interop/src/MonoWebAssemblyJSRuntime.cs +++ b/src/JSInterop/Mono.WebAssembly.Interop/src/MonoWebAssemblyJSRuntime.cs @@ -16,6 +16,25 @@ namespace Mono.WebAssembly.Interop /// public class MonoWebAssemblyJSRuntime : JSInProcessRuntime { + /// + /// Gets the used to perform operations using . + /// + private static MonoWebAssemblyJSRuntime Instance { get; set; } + + /// + /// Initializes the to be used to perform operations using . + /// + /// The instance. + protected static void Initialize(MonoWebAssemblyJSRuntime jsRuntime) + { + if (Instance != null) + { + throw new InvalidOperationException("MonoWebAssemblyJSRuntime has already been initialized."); + } + + Instance = jsRuntime ?? throw new ArgumentNullException(nameof(jsRuntime)); + } + /// protected override string InvokeJS(string identifier, string argsJson) { @@ -34,11 +53,11 @@ protected override void BeginInvokeJS(long asyncHandle, string identifier, strin // Invoked via Mono's JS interop mechanism (invoke_method) private static string InvokeDotNet(string assemblyName, string methodIdentifier, string dotNetObjectId, string argsJson) - => DotNetDispatcher.Invoke(assemblyName, methodIdentifier, dotNetObjectId == null ? default : long.Parse(dotNetObjectId), argsJson); + => DotNetDispatcher.Invoke(Instance, assemblyName, methodIdentifier, dotNetObjectId == null ? default : long.Parse(dotNetObjectId), argsJson); // Invoked via Mono's JS interop mechanism (invoke_method) private static void EndInvokeJS(string argsJson) - => DotNetDispatcher.EndInvokeJS(argsJson); + => DotNetDispatcher.EndInvokeJS(Instance, argsJson); // Invoked via Mono's JS interop mechanism (invoke_method) private static void BeginInvokeDotNet(string callId, string assemblyNameOrDotNetObjectId, string methodIdentifier, string argsJson) @@ -59,7 +78,7 @@ private static void BeginInvokeDotNet(string callId, string assemblyNameOrDotNet assemblyName = assemblyNameOrDotNetObjectId; } - DotNetDispatcher.BeginInvokeDotNet(callId, assemblyName, methodIdentifier, dotNetObjectId, argsJson); + DotNetDispatcher.BeginInvokeDotNet(Instance, callId, assemblyName, methodIdentifier, dotNetObjectId, argsJson); } protected override void EndInvokeDotNet( @@ -84,7 +103,7 @@ protected override void EndInvokeDotNet( // We pass 0 as the async handle because we don't want the JS-side code to // send back any notification (we're just providing a result for an existing async call) - var args = JsonSerializer.Serialize(new[] { callId, success, resultOrError }, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); + var args = JsonSerializer.Serialize(new[] { callId, success, resultOrError }, JsonSerializerOptions); BeginInvokeJS(0, "DotNet.jsCallDispatcher.endInvokeDotNetFromJS", args); } From 591d6c13ec665d9ac0ab71056c04841530804841 Mon Sep 17 00:00:00 2001 From: Brennan Date: Fri, 16 Aug 2019 13:47:32 -0700 Subject: [PATCH 129/183] Add support for netcoreapp5.0 to micro benchmarks (dotnet/extensions#2190) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/72bcb43aee992f77287165de5b6140553c5750eb --- src/Shared/BenchmarkRunner/DefaultCoreConfig.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Shared/BenchmarkRunner/DefaultCoreConfig.cs b/src/Shared/BenchmarkRunner/DefaultCoreConfig.cs index a61833ab269d..d24bb439a146 100644 --- a/src/Shared/BenchmarkRunner/DefaultCoreConfig.cs +++ b/src/Shared/BenchmarkRunner/DefaultCoreConfig.cs @@ -30,8 +30,12 @@ public DefaultCoreConfig() Add(Job.Core #if NETCOREAPP2_1 .With(CsProjCoreToolchain.From(NetCoreAppSettings.NetCoreApp21)) -#else +#elif NETCOREAPP3_0 .With(CsProjCoreToolchain.From(new NetCoreAppSettings("netcoreapp3.0", null, ".NET Core 3.0"))) +#elif NETCOREAPP5_0 + .With(CsProjCoreToolchain.From(new NetCoreAppSettings("netcoreapp5.0", null, ".NET Core 5.0"))) +#else +#error Target frameworks need to be updated. #endif .With(new GcMode { Server = true }) .With(RunStrategy.Throughput)); From d46d569b8122ec3337fcae6a6814a6fb316bc45c Mon Sep 17 00:00:00 2001 From: Pranav K Date: Fri, 16 Aug 2019 16:19:23 -0700 Subject: [PATCH 130/183] Simplify JSRuntime method signature (dotnet/extensions#2188) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/9c392a92efa88707023d8f0a47681aee217a9c56 --- .../ref/Microsoft.JSInterop.netcoreapp3.0.cs | 29 ++++++- .../ref/Microsoft.JSInterop.netstandard2.0.cs | 29 ++++++- .../src/Infrastructure/DotNetDispatcher.cs | 46 +++++----- .../Infrastructure/DotNetInvocationInfo.cs | 48 +++++++++++ .../Infrastructure/DotNetInvocationResult.cs | 55 ++++++++++++ .../Microsoft.JSInterop/src/JSRuntime.cs | 16 +--- .../Infrastructure/DotNetDispatcherTest.cs | 86 ++++++++----------- .../test/JSInProcessRuntimeTest.cs | 5 +- .../Microsoft.JSInterop/test/JSRuntimeTest.cs | 24 +++--- .../Microsoft.JSInterop/test/TestJSRuntime.cs | 3 +- ...Mono.WebAssembly.Interop.netstandard2.0.cs | 2 +- .../src/MonoWebAssemblyJSRuntime.cs | 28 ++---- 12 files changed, 249 insertions(+), 122 deletions(-) create mode 100644 src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetInvocationInfo.cs create mode 100644 src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetInvocationResult.cs diff --git a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netcoreapp3.0.cs b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netcoreapp3.0.cs index 953f8b0329b2..2d8c51caaf8e 100644 --- a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netcoreapp3.0.cs +++ b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netcoreapp3.0.cs @@ -50,7 +50,7 @@ protected JSRuntime() { } protected System.TimeSpan? DefaultAsyncTimeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } protected internal System.Text.Json.JsonSerializerOptions JsonSerializerOptions { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } protected abstract void BeginInvokeJS(long taskId, string identifier, string argsJson); - protected internal abstract void EndInvokeDotNet(string callId, bool success, object resultOrError, string assemblyName, string methodIdentifier, long dotNetObjectId); + protected internal abstract void EndInvokeDotNet(Microsoft.JSInterop.Infrastructure.DotNetInvocationInfo invocationInfo, in Microsoft.JSInterop.Infrastructure.DotNetInvocationResult invocationResult); public System.Threading.Tasks.ValueTask InvokeAsync(string identifier, object[] args) { throw null; } public System.Threading.Tasks.ValueTask InvokeAsync(string identifier, System.Threading.CancellationToken cancellationToken, object[] args) { throw null; } } @@ -72,8 +72,31 @@ namespace Microsoft.JSInterop.Infrastructure { public static partial class DotNetDispatcher { - public static void BeginInvokeDotNet(Microsoft.JSInterop.JSRuntime jsRuntime, string callId, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { } + public static void BeginInvokeDotNet(Microsoft.JSInterop.JSRuntime jsRuntime, Microsoft.JSInterop.Infrastructure.DotNetInvocationInfo invocationInfo, string argsJson) { } public static void EndInvokeJS(Microsoft.JSInterop.JSRuntime jsRuntime, string arguments) { } - public static string Invoke(Microsoft.JSInterop.JSRuntime jsRuntime, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { throw null; } + public static string Invoke(Microsoft.JSInterop.JSRuntime jsRuntime, in Microsoft.JSInterop.Infrastructure.DotNetInvocationInfo invocationInfo, string argsJson) { throw null; } + } + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] + public readonly partial struct DotNetInvocationInfo + { + private readonly object _dummy; + private readonly int _dummyPrimitive; + public DotNetInvocationInfo(string assemblyName, string methodIdentifier, long dotNetObjectId, string callId) { throw null; } + public string AssemblyName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string CallId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public long DotNetObjectId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string MethodIdentifier { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] + public readonly partial struct DotNetInvocationResult + { + private readonly object _dummy; + private readonly int _dummyPrimitive; + public DotNetInvocationResult(System.Exception exception, string errorKind) { throw null; } + public DotNetInvocationResult(object result) { throw null; } + public string ErrorKind { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Exception Exception { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public object Result { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public bool Success { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } } } diff --git a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs index 953f8b0329b2..2d8c51caaf8e 100644 --- a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs +++ b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs @@ -50,7 +50,7 @@ protected JSRuntime() { } protected System.TimeSpan? DefaultAsyncTimeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } protected internal System.Text.Json.JsonSerializerOptions JsonSerializerOptions { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } protected abstract void BeginInvokeJS(long taskId, string identifier, string argsJson); - protected internal abstract void EndInvokeDotNet(string callId, bool success, object resultOrError, string assemblyName, string methodIdentifier, long dotNetObjectId); + protected internal abstract void EndInvokeDotNet(Microsoft.JSInterop.Infrastructure.DotNetInvocationInfo invocationInfo, in Microsoft.JSInterop.Infrastructure.DotNetInvocationResult invocationResult); public System.Threading.Tasks.ValueTask InvokeAsync(string identifier, object[] args) { throw null; } public System.Threading.Tasks.ValueTask InvokeAsync(string identifier, System.Threading.CancellationToken cancellationToken, object[] args) { throw null; } } @@ -72,8 +72,31 @@ namespace Microsoft.JSInterop.Infrastructure { public static partial class DotNetDispatcher { - public static void BeginInvokeDotNet(Microsoft.JSInterop.JSRuntime jsRuntime, string callId, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { } + public static void BeginInvokeDotNet(Microsoft.JSInterop.JSRuntime jsRuntime, Microsoft.JSInterop.Infrastructure.DotNetInvocationInfo invocationInfo, string argsJson) { } public static void EndInvokeJS(Microsoft.JSInterop.JSRuntime jsRuntime, string arguments) { } - public static string Invoke(Microsoft.JSInterop.JSRuntime jsRuntime, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { throw null; } + public static string Invoke(Microsoft.JSInterop.JSRuntime jsRuntime, in Microsoft.JSInterop.Infrastructure.DotNetInvocationInfo invocationInfo, string argsJson) { throw null; } + } + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] + public readonly partial struct DotNetInvocationInfo + { + private readonly object _dummy; + private readonly int _dummyPrimitive; + public DotNetInvocationInfo(string assemblyName, string methodIdentifier, long dotNetObjectId, string callId) { throw null; } + public string AssemblyName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string CallId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public long DotNetObjectId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string MethodIdentifier { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] + public readonly partial struct DotNetInvocationResult + { + private readonly object _dummy; + private readonly int _dummyPrimitive; + public DotNetInvocationResult(System.Exception exception, string errorKind) { throw null; } + public DotNetInvocationResult(object result) { throw null; } + public string ErrorKind { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Exception Exception { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public object Result { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public bool Success { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } } } diff --git a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs index 92afc6278d5e..6a3a4f8d5fec 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs @@ -28,12 +28,10 @@ public static class DotNetDispatcher /// Receives a call from JS to .NET, locating and invoking the specified method. /// /// The . - /// The assembly containing the method to be invoked. - /// The identifier of the method to be invoked. The method must be annotated with a matching this identifier string. - /// For instance method calls, identifies the target object. + /// The . /// A JSON representation of the parameters. /// A JSON representation of the return value, or null. - public static string Invoke(JSRuntime jsRuntime, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) + public static string Invoke(JSRuntime jsRuntime, in DotNetInvocationInfo invocationInfo, string argsJson) { // This method doesn't need [JSInvokable] because the platform is responsible for having // some way to dispatch calls here. The logic inside here is the thing that checks whether @@ -41,12 +39,12 @@ public static string Invoke(JSRuntime jsRuntime, string assemblyName, string met // because there would be nobody to police that. This method *is* the police. IDotNetObjectReference targetInstance = default; - if (dotNetObjectId != default) + if (invocationInfo.DotNetObjectId != default) { - targetInstance = jsRuntime.GetObjectReference(dotNetObjectId); + targetInstance = jsRuntime.GetObjectReference(invocationInfo.DotNetObjectId); } - var syncResult = InvokeSynchronously(jsRuntime, assemblyName, methodIdentifier, targetInstance, argsJson); + var syncResult = InvokeSynchronously(jsRuntime, invocationInfo, targetInstance, argsJson); if (syncResult == null) { return null; @@ -59,13 +57,10 @@ public static string Invoke(JSRuntime jsRuntime, string assemblyName, string met /// Receives a call from JS to .NET, locating and invoking the specified method asynchronously. /// /// The . - /// A value identifying the asynchronous call that should be passed back with the result, or null if no result notification is required. - /// The assembly containing the method to be invoked. - /// The identifier of the method to be invoked. The method must be annotated with a matching this identifier string. - /// For instance method calls, identifies the target object. + /// The . /// A JSON representation of the parameters. /// A JSON representation of the return value, or null. - public static void BeginInvokeDotNet(JSRuntime jsRuntime, string callId, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) + public static void BeginInvokeDotNet(JSRuntime jsRuntime, DotNetInvocationInfo invocationInfo, string argsJson) { // This method doesn't need [JSInvokable] because the platform is responsible for having // some way to dispatch calls here. The logic inside here is the thing that checks whether @@ -75,18 +70,19 @@ public static void BeginInvokeDotNet(JSRuntime jsRuntime, string callId, string // Using ExceptionDispatchInfo here throughout because we want to always preserve // original stack traces. + var callId = invocationInfo.CallId; + object syncResult = null; ExceptionDispatchInfo syncException = null; IDotNetObjectReference targetInstance = null; - try { - if (dotNetObjectId != default) + if (invocationInfo.DotNetObjectId != default) { - targetInstance = jsRuntime.GetObjectReference(dotNetObjectId); + targetInstance = jsRuntime.GetObjectReference(invocationInfo.DotNetObjectId); } - syncResult = InvokeSynchronously(jsRuntime, assemblyName, methodIdentifier, targetInstance, argsJson); + syncResult = InvokeSynchronously(jsRuntime, invocationInfo, targetInstance, argsJson); } catch (Exception ex) { @@ -101,7 +97,7 @@ public static void BeginInvokeDotNet(JSRuntime jsRuntime, string callId, string else if (syncException != null) { // Threw synchronously, let's respond. - jsRuntime.EndInvokeDotNet(callId, false, syncException, assemblyName, methodIdentifier, dotNetObjectId); + jsRuntime.EndInvokeDotNet(invocationInfo, new DotNetInvocationResult(syncException.SourceException, "InvocationFailure")); } else if (syncResult is Task task) { @@ -111,23 +107,27 @@ public static void BeginInvokeDotNet(JSRuntime jsRuntime, string callId, string { if (t.Exception != null) { - var exception = t.Exception.GetBaseException(); - - jsRuntime.EndInvokeDotNet(callId, false, ExceptionDispatchInfo.Capture(exception), assemblyName, methodIdentifier, dotNetObjectId); + var exceptionDispatchInfo = ExceptionDispatchInfo.Capture(t.Exception.GetBaseException()); + var dispatchResult = new DotNetInvocationResult(exceptionDispatchInfo.SourceException, "InvocationFailure"); + jsRuntime.EndInvokeDotNet(invocationInfo, dispatchResult); } var result = TaskGenericsUtil.GetTaskResult(task); - jsRuntime.EndInvokeDotNet(callId, true, result, assemblyName, methodIdentifier, dotNetObjectId); + jsRuntime.EndInvokeDotNet(invocationInfo, new DotNetInvocationResult(result)); }, TaskScheduler.Current); } else { - jsRuntime.EndInvokeDotNet(callId, true, syncResult, assemblyName, methodIdentifier, dotNetObjectId); + var dispatchResult = new DotNetInvocationResult(syncResult); + jsRuntime.EndInvokeDotNet(invocationInfo, dispatchResult); } } - private static object InvokeSynchronously(JSRuntime jsRuntime, string assemblyName, string methodIdentifier, IDotNetObjectReference objectReference, string argsJson) + private static object InvokeSynchronously(JSRuntime jsRuntime, in DotNetInvocationInfo callInfo, IDotNetObjectReference objectReference, string argsJson) { + var assemblyName = callInfo.AssemblyName; + var methodIdentifier = callInfo.MethodIdentifier; + AssemblyKey assemblyKey; if (objectReference is null) { diff --git a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetInvocationInfo.cs b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetInvocationInfo.cs new file mode 100644 index 000000000000..942fc34da009 --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetInvocationInfo.cs @@ -0,0 +1,48 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.JSInterop.Infrastructure +{ + /// + /// Information about a JSInterop call from JavaScript to .NET. + /// + public readonly struct DotNetInvocationInfo + { + /// + /// Initializes a new instance of . + /// + /// The name of the assembly containing the method. + /// The identifier of the method to be invoked. + /// The object identifier for instance method calls. + /// The call identifier. + public DotNetInvocationInfo(string assemblyName, string methodIdentifier, long dotNetObjectId, string callId) + { + CallId = callId; + AssemblyName = assemblyName; + MethodIdentifier = methodIdentifier; + DotNetObjectId = dotNetObjectId; + } + + /// + /// Gets the name of the assembly containing the method. + /// Only one of or may be specified. + /// + public string AssemblyName { get; } + + /// + /// Gets the identifier of the method to be invoked. This is the value specified in the . + /// + public string MethodIdentifier { get; } + + /// + /// Gets the object identifier for instance method calls. + /// Only one of or may be specified. + /// + public long DotNetObjectId { get; } + + /// + /// Gets the call identifier. This value is when the client does not expect a value to be returned. + /// + public string CallId { get; } + } +} diff --git a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetInvocationResult.cs b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetInvocationResult.cs new file mode 100644 index 000000000000..d62dd532ee04 --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetInvocationResult.cs @@ -0,0 +1,55 @@ +using System; + +namespace Microsoft.JSInterop.Infrastructure +{ + /// + /// Result of a .NET invocation that is returned to JavaScript. + /// + public readonly struct DotNetInvocationResult + { + /// + /// Constructor for a failed invocation. + /// + /// The that caused the failure. + /// The error kind. + public DotNetInvocationResult(Exception exception, string errorKind) + { + Result = default; + Exception = exception ?? throw new ArgumentNullException(nameof(exception)); + ErrorKind = errorKind; + Success = false; + } + + /// + /// Constructor for a successful invocation. + /// + /// The result. + public DotNetInvocationResult(object result) + { + Result = result; + Exception = default; + ErrorKind = default; + Success = true; + } + + /// + /// Gets the that caused the failure. + /// + public Exception Exception { get; } + + /// + /// Gets the error kind. + /// + public string ErrorKind { get; } + + /// + /// Gets the result of a successful invocation. + /// + public object Result { get; } + + /// + /// if the invocation succeeded, otherwise . + /// + public bool Success { get; } + } +} diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs b/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs index ba411b72dbd5..4dca7a5db3f6 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs @@ -150,19 +150,11 @@ private async ValueTask InvokeWithDefaultCancellation(string identifier, o /// /// Completes an async JS interop call from JavaScript to .NET /// - /// The id of the JavaScript callback to execute on completion. - /// Whether the operation succeeded or not. - /// The result of the operation or an object containing error details. - /// The name of the method assembly if the invocation was for a static method. - /// The identifier for the method within the assembly. - /// The tracking id of the dotnet object if the invocation was for an instance method. + /// The . + /// The . protected internal abstract void EndInvokeDotNet( - string callId, - bool success, - object resultOrError, - string assemblyName, - string methodIdentifier, - long dotNetObjectId); + DotNetInvocationInfo invocationInfo, + in DotNetInvocationResult invocationResult); internal void EndInvokeJS(long taskId, bool succeeded, ref Utf8JsonReader jsonReader) { diff --git a/src/JSInterop/Microsoft.JSInterop/test/Infrastructure/DotNetDispatcherTest.cs b/src/JSInterop/Microsoft.JSInterop/test/Infrastructure/DotNetDispatcherTest.cs index 2d8208b7a604..7e82a47a89eb 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/Infrastructure/DotNetDispatcherTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/Infrastructure/DotNetDispatcherTest.cs @@ -20,7 +20,7 @@ public void CannotInvokeWithEmptyAssemblyName() { var ex = Assert.Throws(() => { - DotNetDispatcher.Invoke(new TestJSRuntime(), " ", "SomeMethod", default, "[]"); + DotNetDispatcher.Invoke(new TestJSRuntime(), new DotNetInvocationInfo(" ", "SomeMethod", default, default), "[]"); }); Assert.StartsWith("Cannot be null, empty, or whitespace.", ex.Message); @@ -32,7 +32,7 @@ public void CannotInvokeWithEmptyMethodIdentifier() { var ex = Assert.Throws(() => { - DotNetDispatcher.Invoke(new TestJSRuntime(), "SomeAssembly", " ", default, "[]"); + DotNetDispatcher.Invoke(new TestJSRuntime(), new DotNetInvocationInfo("SomeAssembly", " ", default, default), "[]"); }); Assert.StartsWith("Cannot be null, empty, or whitespace.", ex.Message); @@ -45,7 +45,7 @@ public void CannotInvokeMethodsOnUnloadedAssembly() var assemblyName = "Some.Fake.Assembly"; var ex = Assert.Throws(() => { - DotNetDispatcher.Invoke(new TestJSRuntime(), assemblyName, "SomeMethod", default, null); + DotNetDispatcher.Invoke(new TestJSRuntime(), new DotNetInvocationInfo(assemblyName, "SomeMethod", default, default), null); }); Assert.Equal($"There is no loaded assembly with the name '{assemblyName}'.", ex.Message); @@ -67,7 +67,7 @@ public void CannotInvokeUnsuitableMethods(string methodIdentifier) { var ex = Assert.Throws(() => { - DotNetDispatcher.Invoke(new TestJSRuntime(), thisAssemblyName, methodIdentifier, default, null); + DotNetDispatcher.Invoke(new TestJSRuntime(), new DotNetInvocationInfo(thisAssemblyName, methodIdentifier, default, default), null); }); Assert.Equal($"The assembly '{thisAssemblyName}' does not contain a public method with [JSInvokableAttribute(\"{methodIdentifier}\")].", ex.Message); @@ -79,7 +79,7 @@ public void CanInvokeStaticVoidMethod() // Arrange/Act var jsRuntime = new TestJSRuntime(); SomePublicType.DidInvokeMyInvocableStaticVoid = false; - var resultJson = DotNetDispatcher.Invoke(jsRuntime, thisAssemblyName, "InvocableStaticVoid", default, null); + var resultJson = DotNetDispatcher.Invoke(jsRuntime, new DotNetInvocationInfo(thisAssemblyName, "InvocableStaticVoid", default, default), null); // Assert Assert.Null(resultJson); @@ -91,7 +91,7 @@ public void CanInvokeStaticNonVoidMethod() { // Arrange/Act var jsRuntime = new TestJSRuntime(); - var resultJson = DotNetDispatcher.Invoke(jsRuntime, thisAssemblyName, "InvocableStaticNonVoid", default, null); + var resultJson = DotNetDispatcher.Invoke(jsRuntime, new DotNetInvocationInfo(thisAssemblyName, "InvocableStaticNonVoid", default, default), null); var result = JsonSerializer.Deserialize(resultJson, jsRuntime.JsonSerializerOptions); // Assert @@ -104,7 +104,7 @@ public void CanInvokeStaticNonVoidMethodWithoutCustomIdentifier() { // Arrange/Act var jsRuntime = new TestJSRuntime(); - var resultJson = DotNetDispatcher.Invoke(jsRuntime, thisAssemblyName, nameof(SomePublicType.InvokableMethodWithoutCustomIdentifier), default, null); + var resultJson = DotNetDispatcher.Invoke(jsRuntime, new DotNetInvocationInfo(thisAssemblyName, nameof(SomePublicType.InvokableMethodWithoutCustomIdentifier), default, default), null); var result = JsonSerializer.Deserialize(resultJson, jsRuntime.JsonSerializerOptions); // Assert @@ -130,7 +130,7 @@ public void CanInvokeStaticWithParams() }, jsRuntime.JsonSerializerOptions); // Act - var resultJson = DotNetDispatcher.Invoke(jsRuntime, thisAssemblyName, "InvocableStaticWithParams", default, argsJson); + var resultJson = DotNetDispatcher.Invoke(jsRuntime, new DotNetInvocationInfo(thisAssemblyName, "InvocableStaticWithParams", default, default), argsJson); var result = JsonDocument.Parse(resultJson); var root = result.RootElement; @@ -171,7 +171,7 @@ public void InvokingWithIncorrectUseOfDotNetObjectRefThrows() // Act & Assert var ex = Assert.Throws(() => - DotNetDispatcher.Invoke(jsRuntime, thisAssemblyName, method, default, argsJson)); + DotNetDispatcher.Invoke(jsRuntime, new DotNetInvocationInfo(thisAssemblyName, method, default, default), argsJson)); Assert.Equal($"In call to '{method}', parameter of type '{nameof(TestDTO)}' at index 3 must be declared as type 'DotNetObjectRef' to receive the incoming value.", ex.Message); } @@ -185,7 +185,7 @@ public void CanInvokeInstanceVoidMethod() jsRuntime.Invoke("unimportant", objectRef); // Act - var resultJson = DotNetDispatcher.Invoke(jsRuntime, null, "InvokableInstanceVoid", 1, null); + var resultJson = DotNetDispatcher.Invoke(jsRuntime, new DotNetInvocationInfo(null, "InvokableInstanceVoid", 1, default), null); // Assert Assert.Null(resultJson); @@ -202,7 +202,7 @@ public void CanInvokeBaseInstanceVoidMethod() jsRuntime.Invoke("unimportant", objectRef); // Act - var resultJson = DotNetDispatcher.Invoke(jsRuntime, null, "BaseClassInvokableInstanceVoid", 1, null); + var resultJson = DotNetDispatcher.Invoke(jsRuntime, new DotNetInvocationInfo(null, "BaseClassInvokableInstanceVoid", 1, default), null); // Assert Assert.Null(resultJson); @@ -219,7 +219,7 @@ public void DotNetObjectReferencesCanBeDisposed() jsRuntime.Invoke("unimportant", objectRef); // Act - DotNetDispatcher.BeginInvokeDotNet(jsRuntime, null, null, "__Dispose", objectRef.ObjectId, null); + DotNetDispatcher.BeginInvokeDotNet(jsRuntime, new DotNetInvocationInfo(null, "__Dispose", objectRef.ObjectId, default), null); // Assert Assert.True(objectRef.Disposed); @@ -240,7 +240,7 @@ public void CannotUseDotNetObjectRefAfterDisposal() // Act/Assert var ex = Assert.Throws( - () => DotNetDispatcher.Invoke(jsRuntime, null, "InvokableInstanceVoid", 1, null)); + () => DotNetDispatcher.Invoke(jsRuntime, new DotNetInvocationInfo(null, "InvokableInstanceVoid", 1, default), null)); Assert.StartsWith("There is no tracked object with id '1'.", ex.Message); } @@ -259,7 +259,7 @@ public void CannotUseDotNetObjectRefAfterReleaseDotNetObject() // Act/Assert var ex = Assert.Throws( - () => DotNetDispatcher.Invoke(jsRuntime, null, "InvokableInstanceVoid", 1, null)); + () => DotNetDispatcher.Invoke(jsRuntime, new DotNetInvocationInfo(null, "InvokableInstanceVoid", 1, default), null)); Assert.StartsWith("There is no tracked object with id '1'.", ex.Message); } @@ -346,7 +346,7 @@ public void CanInvokeInstanceMethodWithParams() var argsJson = "[\"myvalue\",{\"__dotNetObject\":2}]"; // Act - var resultJson = DotNetDispatcher.Invoke(jsRuntime, null, "InvokableInstanceMethod", 1, argsJson); + var resultJson = DotNetDispatcher.Invoke(jsRuntime, new DotNetInvocationInfo(null, "InvokableInstanceMethod", 1, default), argsJson); // Assert Assert.Equal("[\"You passed myvalue\",{\"__dotNetObject\":3}]", resultJson); @@ -369,7 +369,7 @@ public void CannotInvokeWithFewerNumberOfParameters() // Act/Assert var ex = Assert.Throws(() => { - DotNetDispatcher.Invoke(jsRuntime, thisAssemblyName, "InvocableStaticWithParams", default, argsJson); + DotNetDispatcher.Invoke(jsRuntime, new DotNetInvocationInfo(thisAssemblyName, "InvocableStaticWithParams", default, default), argsJson); }); Assert.Equal("The call to 'InvocableStaticWithParams' expects '3' parameters, but received '2'.", ex.Message); @@ -392,7 +392,7 @@ public void CannotInvokeWithMoreParameters() // Act/Assert var ex = Assert.Throws(() => { - DotNetDispatcher.Invoke(jsRuntime, thisAssemblyName, "InvocableStaticWithParams", default, argsJson); + DotNetDispatcher.Invoke(jsRuntime, new DotNetInvocationInfo(thisAssemblyName, "InvocableStaticWithParams", default, default), argsJson); }); Assert.Equal("Unexpected JSON token Number. Ensure that the call to `InvocableStaticWithParams' is supplied with exactly '3' parameters.", ex.Message); @@ -419,13 +419,13 @@ public async Task CanInvokeAsyncMethod() // Act var callId = "123"; var resultTask = jsRuntime.NextInvocationTask; - DotNetDispatcher.BeginInvokeDotNet(jsRuntime, callId, null, "InvokableAsyncMethod", 1, argsJson); + DotNetDispatcher.BeginInvokeDotNet(jsRuntime, new DotNetInvocationInfo(null, "InvokableAsyncMethod", 1, callId), argsJson); await resultTask; // Assert: Correct completion information Assert.Equal(callId, jsRuntime.LastCompletionCallId); - Assert.True(jsRuntime.LastCompletionStatus); - var result = Assert.IsType(jsRuntime.LastCompletionResult); + Assert.True(jsRuntime.LastCompletionResult.Success); + var result = Assert.IsType(jsRuntime.LastCompletionResult.Result); var resultDto1 = Assert.IsType(result[0]); Assert.Equal("STRING VIA JSON", resultDto1.StringVal); @@ -447,18 +447,17 @@ public async Task CanInvokeSyncThrowingMethod() // Act var callId = "123"; var resultTask = jsRuntime.NextInvocationTask; - DotNetDispatcher.BeginInvokeDotNet(jsRuntime, callId, thisAssemblyName, nameof(ThrowingClass.ThrowingMethod), default, default); + DotNetDispatcher.BeginInvokeDotNet(jsRuntime, new DotNetInvocationInfo(thisAssemblyName, nameof(ThrowingClass.ThrowingMethod), default, callId), default); await resultTask; // This won't throw, it sets properties on the jsRuntime. // Assert Assert.Equal(callId, jsRuntime.LastCompletionCallId); - Assert.False(jsRuntime.LastCompletionStatus); // Fails + Assert.False(jsRuntime.LastCompletionResult.Success); // Fails // Make sure the method that threw the exception shows up in the call stack // https://github.com/aspnet/AspNetCore/issues/8612 - var exception = jsRuntime.LastCompletionResult is ExceptionDispatchInfo edi ? edi.SourceException.ToString() : null; - Assert.Contains(nameof(ThrowingClass.ThrowingMethod), exception); + Assert.Contains(nameof(ThrowingClass.ThrowingMethod), jsRuntime.LastCompletionResult.Exception.ToString()); } [Fact] @@ -470,18 +469,17 @@ public async Task CanInvokeAsyncThrowingMethod() // Act var callId = "123"; var resultTask = jsRuntime.NextInvocationTask; - DotNetDispatcher.BeginInvokeDotNet(jsRuntime, callId, thisAssemblyName, nameof(ThrowingClass.AsyncThrowingMethod), default, default); + DotNetDispatcher.BeginInvokeDotNet(jsRuntime, new DotNetInvocationInfo(thisAssemblyName, nameof(ThrowingClass.AsyncThrowingMethod), default, callId), default); await resultTask; // This won't throw, it sets properties on the jsRuntime. // Assert Assert.Equal(callId, jsRuntime.LastCompletionCallId); - Assert.False(jsRuntime.LastCompletionStatus); // Fails + Assert.False(jsRuntime.LastCompletionResult.Success); // Fails // Make sure the method that threw the exception shows up in the call stack // https://github.com/aspnet/AspNetCore/issues/8612 - var exception = jsRuntime.LastCompletionResult is ExceptionDispatchInfo edi ? edi.SourceException.ToString() : null; - Assert.Contains(nameof(ThrowingClass.AsyncThrowingMethod), exception); + Assert.Contains(nameof(ThrowingClass.AsyncThrowingMethod), jsRuntime.LastCompletionResult.Exception.ToString()); } [Fact] @@ -491,15 +489,15 @@ public async Task BeginInvoke_ThrowsWithInvalidArgsJson_WithCallId() var jsRuntime = new TestJSRuntime(); var callId = "123"; var resultTask = jsRuntime.NextInvocationTask; - DotNetDispatcher.BeginInvokeDotNet(jsRuntime, callId, thisAssemblyName, "InvocableStaticWithParams", default, "not json"); + DotNetDispatcher.BeginInvokeDotNet(jsRuntime, new DotNetInvocationInfo(thisAssemblyName, "InvocableStaticWithParams", default, callId), "not json"); await resultTask; // This won't throw, it sets properties on the jsRuntime. // Assert Assert.Equal(callId, jsRuntime.LastCompletionCallId); - Assert.False(jsRuntime.LastCompletionStatus); // Fails - var result = Assert.IsType(jsRuntime.LastCompletionResult); - Assert.Contains("JsonReaderException: '<' is an invalid start of a value.", result.SourceException.ToString()); + Assert.False(jsRuntime.LastCompletionResult.Success); // Fails + var exception = jsRuntime.LastCompletionResult.Exception; + Assert.Contains("JsonReaderException: '<' is an invalid start of a value.", exception.ToString()); } [Fact] @@ -509,13 +507,13 @@ public void BeginInvoke_ThrowsWithInvalid_DotNetObjectRef() var jsRuntime = new TestJSRuntime(); var callId = "123"; var resultTask = jsRuntime.NextInvocationTask; - DotNetDispatcher.BeginInvokeDotNet(jsRuntime, callId, null, "InvokableInstanceVoid", 1, null); + DotNetDispatcher.BeginInvokeDotNet(jsRuntime, new DotNetInvocationInfo(null, "InvokableInstanceVoid", 1, callId), null); // Assert Assert.Equal(callId, jsRuntime.LastCompletionCallId); - Assert.False(jsRuntime.LastCompletionStatus); // Fails - var result = Assert.IsType(jsRuntime.LastCompletionResult); - Assert.StartsWith("System.ArgumentException: There is no tracked object with id '1'. Perhaps the DotNetObjectReference instance was already disposed.", result.SourceException.ToString()); + Assert.False(jsRuntime.LastCompletionResult.Success); // Fails + var exception = jsRuntime.LastCompletionResult.Exception; + Assert.StartsWith("System.ArgumentException: There is no tracked object with id '1'. Perhaps the DotNetObjectReference instance was already disposed.", exception.ToString()); } [Theory] @@ -801,8 +799,7 @@ public class TestJSRuntime : JSInProcessRuntime public string LastInvocationArgsJson { get; private set; } public string LastCompletionCallId { get; private set; } - public bool LastCompletionStatus { get; private set; } - public object LastCompletionResult { get; private set; } + public DotNetInvocationResult LastCompletionResult { get; private set; } protected override void BeginInvokeJS(long asyncHandle, string identifier, string argsJson) { @@ -823,17 +820,10 @@ protected override string InvokeJS(string identifier, string argsJson) return null; } - protected internal override void EndInvokeDotNet( - string callId, - bool success, - object resultOrError, - string assemblyName, - string methodIdentifier, - long dotNetObjectId) + protected internal override void EndInvokeDotNet(DotNetInvocationInfo invocationInfo, in DotNetInvocationResult invocationResult) { - LastCompletionCallId = callId; - LastCompletionStatus = success; - LastCompletionResult = resultOrError; + LastCompletionCallId = invocationInfo.CallId; + LastCompletionResult = invocationResult; _nextInvocationTcs.SetResult(null); _nextInvocationTcs = new TaskCompletionSource(); } diff --git a/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeTest.cs index a1caff595b7b..f42e0801a097 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeTest.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.JSInterop.Infrastructure; using Xunit; namespace Microsoft.JSInterop @@ -113,8 +114,8 @@ public class InvokeArgs protected override void BeginInvokeJS(long asyncHandle, string identifier, string argsJson) => throw new NotImplementedException("This test only covers sync calls"); - protected internal override void EndInvokeDotNet(string callId, bool success, object resultOrError, string assemblyName, string methodIdentifier, long dotNetObjectId) => - throw new NotImplementedException("This test only covers sync calls"); + protected internal override void EndInvokeDotNet(DotNetInvocationInfo invocationInfo, in DotNetInvocationResult invocationResult) + => throw new NotImplementedException("This test only covers sync calls"); } } } diff --git a/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeTest.cs index b102ecc0b563..66e0033d2a35 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeTest.cs @@ -8,6 +8,7 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using Microsoft.JSInterop.Infrastructure; using Xunit; namespace Microsoft.JSInterop @@ -296,18 +297,20 @@ public void CanSanitizeDotNetInteropExceptions() var expectedMessage = "An error ocurred while invoking '[Assembly]::Method'. Swapping to 'Development' environment will " + "display more detailed information about the error that occurred."; - string GetMessage(string assembly, string method) => $"An error ocurred while invoking '[{assembly}]::{method}'. Swapping to 'Development' environment will " + + string GetMessage(DotNetInvocationInfo info) => $"An error ocurred while invoking '[{info.AssemblyName}]::{info.MethodIdentifier}'. Swapping to 'Development' environment will " + "display more detailed information about the error that occurred."; var runtime = new TestJSRuntime() { - OnDotNetException = (e, a, m) => new JSError { Message = GetMessage(a, m) } + OnDotNetException = (invocationInfo) => new JSError { Message = GetMessage(invocationInfo) } }; var exception = new Exception("Some really sensitive data in here"); + var invocation = new DotNetInvocationInfo("Assembly", "Method", 0, "0"); + var result = new DotNetInvocationResult(exception, default); // Act - runtime.EndInvokeDotNet("0", false, exception, "Assembly", "Method", 0); + runtime.EndInvokeDotNet(invocation, result); // Assert var call = runtime.EndInvokeDotNetCalls.Single(); @@ -356,20 +359,21 @@ public class EndInvokeDotNetArgs public object ResultOrError { get; set; } } - public Func OnDotNetException { get; set; } + public Func OnDotNetException { get; set; } - protected internal override void EndInvokeDotNet(string callId, bool success, object resultOrError, string assemblyName, string methodIdentifier, long dotNetObjectId) + protected internal override void EndInvokeDotNet(DotNetInvocationInfo invocationInfo, in DotNetInvocationResult invocationResult) { - if (OnDotNetException != null && !success) + var resultOrError = invocationResult.Success ? invocationResult.Result : invocationResult.Exception; + if (OnDotNetException != null && !invocationResult.Success) { - resultOrError = OnDotNetException(resultOrError as Exception, assemblyName, methodIdentifier); + resultOrError = OnDotNetException(invocationInfo); } EndInvokeDotNetCalls.Add(new EndInvokeDotNetArgs { - CallId = callId, - Success = success, - ResultOrError = resultOrError + CallId = invocationInfo.CallId, + Success = invocationResult.Success, + ResultOrError = resultOrError, }); } diff --git a/src/JSInterop/Microsoft.JSInterop/test/TestJSRuntime.cs b/src/JSInterop/Microsoft.JSInterop/test/TestJSRuntime.cs index 740f02b8da9f..db9c5ddd36bf 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/TestJSRuntime.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/TestJSRuntime.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using Microsoft.JSInterop.Infrastructure; namespace Microsoft.JSInterop { @@ -12,7 +13,7 @@ protected override void BeginInvokeJS(long asyncHandle, string identifier, strin throw new NotImplementedException(); } - protected internal override void EndInvokeDotNet(string callId, bool success, object resultOrError, string assemblyName, string methodIdentifier, long dotNetObjectId) + protected internal override void EndInvokeDotNet(DotNetInvocationInfo invocationInfo, in DotNetInvocationResult invocationResult) { throw new NotImplementedException(); } diff --git a/src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.netstandard2.0.cs b/src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.netstandard2.0.cs index 0996795ca326..8dd70b946a6e 100644 --- a/src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.netstandard2.0.cs +++ b/src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.netstandard2.0.cs @@ -7,7 +7,7 @@ public partial class MonoWebAssemblyJSRuntime : Microsoft.JSInterop.JSInProcessR { public MonoWebAssemblyJSRuntime() { } protected override void BeginInvokeJS(long asyncHandle, string identifier, string argsJson) { } - protected override void EndInvokeDotNet(string callId, bool success, object resultOrError, string assemblyName, string methodIdentifier, long dotNetObjectId) { } + protected override void EndInvokeDotNet(Microsoft.JSInterop.Infrastructure.DotNetInvocationInfo callInfo, in Microsoft.JSInterop.Infrastructure.DotNetInvocationResult dispatchResult) { } protected static void Initialize(Mono.WebAssembly.Interop.MonoWebAssemblyJSRuntime jsRuntime) { } protected override string InvokeJS(string identifier, string argsJson) { throw null; } public TRes InvokeUnmarshalled(string identifier) { throw null; } diff --git a/src/JSInterop/Mono.WebAssembly.Interop/src/MonoWebAssemblyJSRuntime.cs b/src/JSInterop/Mono.WebAssembly.Interop/src/MonoWebAssemblyJSRuntime.cs index b6b01d754d65..654263a12331 100644 --- a/src/JSInterop/Mono.WebAssembly.Interop/src/MonoWebAssemblyJSRuntime.cs +++ b/src/JSInterop/Mono.WebAssembly.Interop/src/MonoWebAssemblyJSRuntime.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Runtime.ExceptionServices; using System.Text.Json; using Microsoft.JSInterop; using Microsoft.JSInterop.Infrastructure; @@ -53,7 +52,10 @@ protected override void BeginInvokeJS(long asyncHandle, string identifier, strin // Invoked via Mono's JS interop mechanism (invoke_method) private static string InvokeDotNet(string assemblyName, string methodIdentifier, string dotNetObjectId, string argsJson) - => DotNetDispatcher.Invoke(Instance, assemblyName, methodIdentifier, dotNetObjectId == null ? default : long.Parse(dotNetObjectId), argsJson); + { + var callInfo = new DotNetInvocationInfo(assemblyName, methodIdentifier, dotNetObjectId == null ? default : long.Parse(dotNetObjectId), callId: null); + return DotNetDispatcher.Invoke(Instance, callInfo, argsJson); + } // Invoked via Mono's JS interop mechanism (invoke_method) private static void EndInvokeJS(string argsJson) @@ -78,32 +80,20 @@ private static void BeginInvokeDotNet(string callId, string assemblyNameOrDotNet assemblyName = assemblyNameOrDotNetObjectId; } - DotNetDispatcher.BeginInvokeDotNet(Instance, callId, assemblyName, methodIdentifier, dotNetObjectId, argsJson); + var callInfo = new DotNetInvocationInfo(assemblyName, methodIdentifier, dotNetObjectId, callId); + DotNetDispatcher.BeginInvokeDotNet(Instance, callInfo, argsJson); } - protected override void EndInvokeDotNet( - string callId, - bool success, - object resultOrError, - string assemblyName, - string methodIdentifier, - long dotNetObjectId) + protected override void EndInvokeDotNet(DotNetInvocationInfo callInfo, in DotNetInvocationResult dispatchResult) { // For failures, the common case is to call EndInvokeDotNet with the Exception object. // For these we'll serialize as something that's useful to receive on the JS side. // If the value is not an Exception, we'll just rely on it being directly JSON-serializable. - if (!success && resultOrError is Exception ex) - { - resultOrError = ex.ToString(); - } - else if (!success && resultOrError is ExceptionDispatchInfo edi) - { - resultOrError = edi.SourceException.ToString(); - } + var resultOrError = dispatchResult.Success ? dispatchResult.Result : dispatchResult.Exception.ToString(); // We pass 0 as the async handle because we don't want the JS-side code to // send back any notification (we're just providing a result for an existing async call) - var args = JsonSerializer.Serialize(new[] { callId, success, resultOrError }, JsonSerializerOptions); + var args = JsonSerializer.Serialize(new[] { callInfo.CallId, dispatchResult.Success, resultOrError }, JsonSerializerOptions); BeginInvokeJS(0, "DotNet.jsCallDispatcher.endInvokeDotNetFromJS", args); } From 29e0146528d108c5a45aafb3485aeeb33efb67bd Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Sat, 17 Aug 2019 13:06:53 +1200 Subject: [PATCH 131/183] Update TypeNameHelper.cs (dotnet/extensions#2194) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/204a4eef27df6ca694ae4f8b768b91a090786d5c --- src/Shared/TypeNameHelper/TypeNameHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Shared/TypeNameHelper/TypeNameHelper.cs b/src/Shared/TypeNameHelper/TypeNameHelper.cs index 3994a074b6d4..d08b9b043946 100644 --- a/src/Shared/TypeNameHelper/TypeNameHelper.cs +++ b/src/Shared/TypeNameHelper/TypeNameHelper.cs @@ -7,7 +7,7 @@ namespace Microsoft.Extensions.Internal { - internal class TypeNameHelper + internal static class TypeNameHelper { private const char DefaultNestedTypeDelimiter = '+'; From bd300b52204aa73283e9cdd081ee3c0a8c18a7bf Mon Sep 17 00:00:00 2001 From: Doug Bunting <6431421+dougbu@users.noreply.github.com> Date: Fri, 16 Aug 2019 18:28:51 -0700 Subject: [PATCH 132/183] Rebuilt ref/ code for Microsoft.JSInterop \n\nCommit migrated from https://github.com/dotnet/extensions/commit/b791bce755e554c0092fe0c7b39dc06289ad229a --- .../ref/Microsoft.JSInterop.netcoreapp5.0.cs | 92 +++++++++++++------ 1 file changed, 65 insertions(+), 27 deletions(-) diff --git a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netcoreapp5.0.cs b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netcoreapp5.0.cs index 654ae9d6173a..2d8c51caaf8e 100644 --- a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netcoreapp5.0.cs +++ b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netcoreapp5.0.cs @@ -3,22 +3,14 @@ namespace Microsoft.JSInterop { - public static partial class DotNetDispatcher - { - public static void BeginInvoke(string callId, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { } - public static void EndInvoke(string arguments) { } - public static string Invoke(string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { throw null; } - [Microsoft.JSInterop.JSInvokableAttribute("DotNetDispatcher.ReleaseDotNetObject")] - public static void ReleaseDotNetObject(long dotNetObjectId) { } - } - public static partial class DotNetObjectRef + public static partial class DotNetObjectReference { - public static Microsoft.JSInterop.DotNetObjectRef Create(TValue value) where TValue : class { throw null; } + public static Microsoft.JSInterop.DotNetObjectReference Create(TValue value) where TValue : class { throw null; } } - public sealed partial class DotNetObjectRef : System.IDisposable where TValue : class + public sealed partial class DotNetObjectReference : System.IDisposable where TValue : class { - internal DotNetObjectRef() { } - public TValue Value { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + internal DotNetObjectReference() { } + public TValue Value { get { throw null; } } public void Dispose() { } } public partial interface IJSInProcessRuntime : Microsoft.JSInterop.IJSRuntime @@ -27,38 +19,84 @@ public partial interface IJSInProcessRuntime : Microsoft.JSInterop.IJSRuntime } public partial interface IJSRuntime { - System.Threading.Tasks.Task InvokeAsync(string identifier, System.Collections.Generic.IEnumerable args, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); - System.Threading.Tasks.Task InvokeAsync(string identifier, params object[] args); + System.Threading.Tasks.ValueTask InvokeAsync(string identifier, object[] args); + System.Threading.Tasks.ValueTask InvokeAsync(string identifier, System.Threading.CancellationToken cancellationToken, object[] args); } public partial class JSException : System.Exception { public JSException(string message) { } public JSException(string message, System.Exception innerException) { } } - public abstract partial class JSInProcessRuntimeBase : Microsoft.JSInterop.JSRuntimeBase, Microsoft.JSInterop.IJSInProcessRuntime, Microsoft.JSInterop.IJSRuntime + public abstract partial class JSInProcessRuntime : Microsoft.JSInterop.JSRuntime, Microsoft.JSInterop.IJSInProcessRuntime, Microsoft.JSInterop.IJSRuntime { - protected JSInProcessRuntimeBase() { } + protected JSInProcessRuntime() { } protected abstract string InvokeJS(string identifier, string argsJson); public TValue Invoke(string identifier, params object[] args) { throw null; } } + public static partial class JSInProcessRuntimeExtensions + { + public static void InvokeVoid(this Microsoft.JSInterop.IJSInProcessRuntime jsRuntime, string identifier, params object[] args) { } + } [System.AttributeUsageAttribute(System.AttributeTargets.Method, AllowMultiple=true)] - public partial class JSInvokableAttribute : System.Attribute + public sealed partial class JSInvokableAttribute : System.Attribute { public JSInvokableAttribute() { } public JSInvokableAttribute(string identifier) { } public string Identifier { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } } - public static partial class JSRuntime + public abstract partial class JSRuntime : Microsoft.JSInterop.IJSRuntime { - public static void SetCurrentJSRuntime(Microsoft.JSInterop.IJSRuntime instance) { } - } - public abstract partial class JSRuntimeBase : Microsoft.JSInterop.IJSRuntime - { - protected JSRuntimeBase() { } + protected JSRuntime() { } protected System.TimeSpan? DefaultAsyncTimeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + protected internal System.Text.Json.JsonSerializerOptions JsonSerializerOptions { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } protected abstract void BeginInvokeJS(long taskId, string identifier, string argsJson); - protected internal abstract void EndInvokeDotNet(string callId, bool success, object resultOrError, string assemblyName, string methodIdentifier, long dotNetObjectId); - public System.Threading.Tasks.Task InvokeAsync(string identifier, System.Collections.Generic.IEnumerable args, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - public System.Threading.Tasks.Task InvokeAsync(string identifier, params object[] args) { throw null; } + protected internal abstract void EndInvokeDotNet(Microsoft.JSInterop.Infrastructure.DotNetInvocationInfo invocationInfo, in Microsoft.JSInterop.Infrastructure.DotNetInvocationResult invocationResult); + public System.Threading.Tasks.ValueTask InvokeAsync(string identifier, object[] args) { throw null; } + public System.Threading.Tasks.ValueTask InvokeAsync(string identifier, System.Threading.CancellationToken cancellationToken, object[] args) { throw null; } + } + public static partial class JSRuntimeExtensions + { + public static System.Threading.Tasks.ValueTask InvokeAsync(this Microsoft.JSInterop.IJSRuntime jsRuntime, string identifier, params object[] args) { throw null; } + public static System.Threading.Tasks.ValueTask InvokeAsync(this Microsoft.JSInterop.IJSRuntime jsRuntime, string identifier, System.Threading.CancellationToken cancellationToken, params object[] args) { throw null; } + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.ValueTask InvokeAsync(this Microsoft.JSInterop.IJSRuntime jsRuntime, string identifier, System.TimeSpan timeout, params object[] args) { throw null; } + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.ValueTask InvokeVoidAsync(this Microsoft.JSInterop.IJSRuntime jsRuntime, string identifier, params object[] args) { throw null; } + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.ValueTask InvokeVoidAsync(this Microsoft.JSInterop.IJSRuntime jsRuntime, string identifier, System.Threading.CancellationToken cancellationToken, params object[] args) { throw null; } + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.ValueTask InvokeVoidAsync(this Microsoft.JSInterop.IJSRuntime jsRuntime, string identifier, System.TimeSpan timeout, params object[] args) { throw null; } + } +} +namespace Microsoft.JSInterop.Infrastructure +{ + public static partial class DotNetDispatcher + { + public static void BeginInvokeDotNet(Microsoft.JSInterop.JSRuntime jsRuntime, Microsoft.JSInterop.Infrastructure.DotNetInvocationInfo invocationInfo, string argsJson) { } + public static void EndInvokeJS(Microsoft.JSInterop.JSRuntime jsRuntime, string arguments) { } + public static string Invoke(Microsoft.JSInterop.JSRuntime jsRuntime, in Microsoft.JSInterop.Infrastructure.DotNetInvocationInfo invocationInfo, string argsJson) { throw null; } + } + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] + public readonly partial struct DotNetInvocationInfo + { + private readonly object _dummy; + private readonly int _dummyPrimitive; + public DotNetInvocationInfo(string assemblyName, string methodIdentifier, long dotNetObjectId, string callId) { throw null; } + public string AssemblyName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string CallId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public long DotNetObjectId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string MethodIdentifier { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] + public readonly partial struct DotNetInvocationResult + { + private readonly object _dummy; + private readonly int _dummyPrimitive; + public DotNetInvocationResult(System.Exception exception, string errorKind) { throw null; } + public DotNetInvocationResult(object result) { throw null; } + public string ErrorKind { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Exception Exception { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public object Result { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public bool Success { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } } } From 1153f0d1def3b13fa0ca482a9480c43cc7c29224 Mon Sep 17 00:00:00 2001 From: Doug Bunting <6431421+dougbu@users.noreply.github.com> Date: Thu, 22 Aug 2019 14:25:13 -0700 Subject: [PATCH 133/183] Add switch to enable expected and unexpected arguments in any order (dotnet/extensions#2210) - unblocks work on aspnet/AspNetCoredotnet/extensions#4923 - arguments for inside and outside men of service reference doc gen tool are mixed by default - users may add either argument type to the end of the outside man's command line - e.g. "command --unexpected unexpectedValue --expected" can now set the "expected" option - only "--unexpected" and "unexpectedValue" are added to RemainingArguments in that case - default behaviour of the command-line parser is unchanged to avoid breaking existing applications - new switch is supported only when calling `CommandLineApplication` constructor for top-level commands - `dotnet-getdocument` (the outside man) has no (sub)commands and expanding scope would increase churn nits: take VS suggestions in changed files\n\nCommit migrated from https://github.com/dotnet/extensions/commit/e4433979b657848d0cb3b925b7284368931d3772 --- .../CommandLine/CommandLineApplication.cs | 69 ++- .../CommandLineApplicationTests.cs | 505 +++++++++++++++++- 2 files changed, 549 insertions(+), 25 deletions(-) diff --git a/src/Shared/CommandLineUtils/CommandLine/CommandLineApplication.cs b/src/Shared/CommandLineUtils/CommandLine/CommandLineApplication.cs index b7c62b5a0bea..51590d59903a 100644 --- a/src/Shared/CommandLineUtils/CommandLine/CommandLineApplication.cs +++ b/src/Shared/CommandLineUtils/CommandLine/CommandLineApplication.cs @@ -13,14 +13,23 @@ namespace Microsoft.Extensions.CommandLineUtils { internal class CommandLineApplication { - // Indicates whether the parser should throw an exception when it runs into an unexpected argument. - // If this field is set to false, the parser will stop parsing when it sees an unexpected argument, and all - // remaining arguments, including the first unexpected argument, will be stored in RemainingArguments property. + // Indicates whether the parser should throw an exception when it runs into an unexpected argument. If this is + // set to true (the default), the parser will throw on the first unexpected argument. Otherwise, all unexpected + // arguments (including the first) are added to RemainingArguments. private readonly bool _throwOnUnexpectedArg; - public CommandLineApplication(bool throwOnUnexpectedArg = true) + // Indicates whether the parser should check remaining arguments for command or option matches after + // encountering an unexpected argument. Ignored if _throwOnUnexpectedArg is true (the default). If + // _throwOnUnexpectedArg and this are both false, the first unexpected argument and all remaining arguments are + // added to RemainingArguments. If _throwOnUnexpectedArg is false and this is true, only unexpected arguments + // are added to RemainingArguments -- allowing a mix of expected and unexpected arguments, commands and + // options. + private readonly bool _continueAfterUnexpectedArg; + + public CommandLineApplication(bool throwOnUnexpectedArg = true, bool continueAfterUnexpectedArg = false) { _throwOnUnexpectedArg = throwOnUnexpectedArg; + _continueAfterUnexpectedArg = continueAfterUnexpectedArg; Options = new List(); Arguments = new List(); Commands = new List(); @@ -145,6 +154,7 @@ public int Execute(params string[] args) { shortOption = arg.Substring(1).Split(new[] { ':', '=' }, 2); } + if (longOption != null) { processed = true; @@ -153,13 +163,27 @@ public int Execute(params string[] args) if (option == null) { - if (string.IsNullOrEmpty(longOptionName) && !command._throwOnUnexpectedArg && AllowArgumentSeparator) + var ignoreContinueAfterUnexpectedArg = false; + if (string.IsNullOrEmpty(longOptionName) && + !command._throwOnUnexpectedArg && + AllowArgumentSeparator) { - // skip over the '--' argument separator + // Skip over the '--' argument separator then consume all remaining arguments. All + // remaining arguments are unconditionally stored for further use. index++; + ignoreContinueAfterUnexpectedArg = true; + } + + if (HandleUnexpectedArg( + command, + args, + index, + argTypeName: "option", + ignoreContinueAfterUnexpectedArg)) + { + continue; } - HandleUnexpectedArg(command, args, index, argTypeName: "option"); break; } @@ -191,6 +215,7 @@ public int Execute(params string[] args) option = null; } } + if (shortOption != null) { processed = true; @@ -204,7 +229,11 @@ public int Execute(params string[] args) if (option == null) { - HandleUnexpectedArg(command, args, index, argTypeName: "option"); + if (HandleUnexpectedArg(command, args, index, argTypeName: "option")) + { + continue; + } + break; } @@ -268,6 +297,7 @@ public int Execute(params string[] args) processed = true; } } + if (!processed) { if (arguments == null) @@ -280,9 +310,14 @@ public int Execute(params string[] args) arguments.Current.Values.Add(arg); } } + if (!processed) { - HandleUnexpectedArg(command, args, index, argTypeName: "command or argument"); + if (HandleUnexpectedArg(command, args, index, argTypeName: "command or argument")) + { + continue; + } + break; } } @@ -490,17 +525,29 @@ public void ShowRootCommandFullNameAndVersion() Out.WriteLine(); } - private void HandleUnexpectedArg(CommandLineApplication command, string[] args, int index, string argTypeName) + private bool HandleUnexpectedArg( + CommandLineApplication command, + string[] args, + int index, + string argTypeName, + bool ignoreContinueAfterUnexpectedArg = false) { if (command._throwOnUnexpectedArg) { command.ShowHint(); throw new CommandParsingException(command, $"Unrecognized {argTypeName} '{args[index]}'"); } + else if (_continueAfterUnexpectedArg && !ignoreContinueAfterUnexpectedArg) + { + // Store argument for further use. + command.RemainingArguments.Add(args[index]); + return true; + } else { - // All remaining arguments are stored for further use + // Store all remaining arguments for later use. command.RemainingArguments.AddRange(new ArraySegment(args, index, args.Length - index)); + return false; } } diff --git a/src/Shared/test/Shared.Tests/CommandLineApplicationTests.cs b/src/Shared/test/Shared.Tests/CommandLineApplicationTests.cs index 44b7ebdaa819..cc6d0d841ef1 100644 --- a/src/Shared/test/Shared.Tests/CommandLineApplicationTests.cs +++ b/src/Shared/test/Shared.Tests/CommandLineApplicationTests.cs @@ -4,7 +4,6 @@ using System; using System.IO; using System.Linq; -using System.Text; using Microsoft.Extensions.CommandLineUtils; using Xunit; @@ -73,6 +72,30 @@ public void ExtraArgumentCausesException() Assert.Contains("three", ex.Message); } + [Fact] + public void ExtraArgumentAddedToRemaining() + { + CommandArgument first = null; + CommandArgument second = null; + + var app = new CommandLineApplication(); + + var testCommand = app.Command("test", c => + { + first = c.Argument("first", "First argument"); + second = c.Argument("second", "Second argument"); + c.OnExecute(() => 0); + }, + throwOnUnexpectedArg: false); + + app.Execute("test", "one", "two", "three"); + + Assert.Equal("one", first.Value); + Assert.Equal("two", second.Value); + var remaining = Assert.Single(testCommand.RemainingArguments); + Assert.Equal("three", remaining); + } + [Fact] public void UnknownCommandCausesException() { @@ -257,6 +280,145 @@ public void AllowNoThrowBehaviorOnUnexpectedArgument() Assert.Equal(unexpectedArg, arg); } + [Fact] + public void AllowArgumentBeforeNoValueOption() + { + var app = new CommandLineApplication(); + var argument = app.Argument("first", "first argument"); + var option = app.Option("--first", "first option", CommandOptionType.NoValue); + + app.Execute("one", "--first"); + + Assert.Equal("one", argument.Value); + Assert.True(option.HasValue()); + } + + [Fact] + public void AllowArgumentAfterNoValueOption() + { + var app = new CommandLineApplication(); + var argument = app.Argument("first", "first argument"); + var option = app.Option("--first", "first option", CommandOptionType.NoValue); + + app.Execute("--first", "one"); + + Assert.Equal("one", argument.Value); + Assert.True(option.HasValue()); + } + + [Fact] + public void AllowArgumentBeforeSingleValueOption() + { + var app = new CommandLineApplication(); + var argument = app.Argument("first", "first argument"); + var option = app.Option("--first ", "first option", CommandOptionType.SingleValue); + + app.Execute("one", "--first", "two"); + + Assert.Equal("one", argument.Value); + Assert.Equal("two", option.Value()); + } + + [Fact] + public void AllowArgumentAfterSingleValueOption() + { + var app = new CommandLineApplication(); + var argument = app.Argument("first", "first argument"); + var option = app.Option("--first ", "first option", CommandOptionType.SingleValue); + + app.Execute("--first", "one", "two"); + + Assert.Equal("two", argument.Value); + Assert.Equal("one", option.Value()); + } + + [Fact] + public void AllowNoThrowBehaviorOnUnexpectedArgumentBeforeNoValueOption_Default() + { + var arguments = new[] { "UnexpectedArg", "--first" }; + var app = new CommandLineApplication(throwOnUnexpectedArg: false); + var option = app.Option("--first", "first option", CommandOptionType.NoValue); + + // (does not throw) + app.Execute(arguments); + + Assert.Equal(arguments, app.RemainingArguments.ToArray()); + Assert.False(option.HasValue()); + } + + [Fact] + public void AllowNoThrowBehaviorOnUnexpectedArgumentBeforeNoValueOption_Continue() + { + var unexpectedArg = "UnexpectedArg"; + var app = new CommandLineApplication(throwOnUnexpectedArg: false, continueAfterUnexpectedArg: true); + var option = app.Option("--first", "first option", CommandOptionType.NoValue); + + // (does not throw) + app.Execute(unexpectedArg, "--first"); + + var arg = Assert.Single(app.RemainingArguments); + Assert.Equal(unexpectedArg, arg); + Assert.True(option.HasValue()); + } + + [Fact] + public void AllowNoThrowBehaviorOnUnexpectedArgumentAfterNoValueOption() + { + var unexpectedArg = "UnexpectedArg"; + var app = new CommandLineApplication(throwOnUnexpectedArg: false); + var option = app.Option("--first", "first option", CommandOptionType.NoValue); + + // (does not throw) + app.Execute("--first", unexpectedArg); + + var arg = Assert.Single(app.RemainingArguments); + Assert.Equal(unexpectedArg, arg); + Assert.True(option.HasValue()); + } + + [Fact] + public void AllowNoThrowBehaviorOnUnexpectedArgumentBeforeSingleValueOption_Default() + { + var arguments = new[] { "UnexpectedArg", "--first", "one" }; + var app = new CommandLineApplication(throwOnUnexpectedArg: false); + app.Option("--first", "first option", CommandOptionType.SingleValue); + + // (does not throw) + app.Execute(arguments); + + Assert.Equal(arguments, app.RemainingArguments.ToArray()); + } + + [Fact] + public void AllowNoThrowBehaviorOnUnexpectedArgumentBeforeSingleValueOption_Continue() + { + var unexpectedArg = "UnexpectedArg"; + var app = new CommandLineApplication(throwOnUnexpectedArg: false, continueAfterUnexpectedArg: true); + var option = app.Option("--first", "first option", CommandOptionType.SingleValue); + + // (does not throw) + app.Execute(unexpectedArg, "--first", "one"); + + var arg = Assert.Single(app.RemainingArguments); + Assert.Equal(unexpectedArg, arg); + Assert.Equal("one", option.Value()); + } + + [Fact] + public void AllowNoThrowBehaviorOnUnexpectedArgumentAfterSingleValueOption() + { + var unexpectedArg = "UnexpectedArg"; + var app = new CommandLineApplication(throwOnUnexpectedArg: false); + var option = app.Option("--first", "first option", CommandOptionType.SingleValue); + + // (does not throw) + app.Execute("--first", "one", unexpectedArg); + + var arg = Assert.Single(app.RemainingArguments); + Assert.Equal(unexpectedArg, arg); + Assert.Equal("one", option.Value()); + } + [Fact] public void ThrowsExceptionOnUnexpectedLongOptionByDefault() { @@ -290,6 +452,183 @@ public void AllowNoThrowBehaviorOnUnexpectedLongOption() Assert.Equal(unexpectedOption, arg); } + [Fact] + public void AllowNoThrowBehaviorOnUnexpectedLongOptionBeforeNoValueOption_Default() + { + var arguments = new[] { "--unexpected", "--first" }; + var app = new CommandLineApplication(throwOnUnexpectedArg: false); + app.Option("--first", "first option", CommandOptionType.NoValue); + + // (does not throw) + app.Execute(arguments); + + Assert.Equal(arguments, app.RemainingArguments.ToArray()); + } + + [Fact] + public void AllowNoThrowBehaviorOnUnexpectedLongOptionBeforeNoValueOption_Continue() + { + var unexpectedOption = "--unexpected"; + var app = new CommandLineApplication(throwOnUnexpectedArg: false, continueAfterUnexpectedArg: true); + var option = app.Option("--first", "first option", CommandOptionType.NoValue); + + // (does not throw) + app.Execute(unexpectedOption, "--first"); + + var arg = Assert.Single(app.RemainingArguments); + Assert.Equal(unexpectedOption, arg); + Assert.True(option.HasValue()); + } + + [Fact] + public void AllowNoThrowBehaviorOnUnexpectedLongOptionAfterNoValueOption() + { + var unexpectedOption = "--unexpected"; + var app = new CommandLineApplication(throwOnUnexpectedArg: false); + var option = app.Option("--first", "first option", CommandOptionType.NoValue); + + // (does not throw) + app.Execute("--first", unexpectedOption); + + var arg = Assert.Single(app.RemainingArguments); + Assert.Equal(unexpectedOption, arg); + Assert.True(option.HasValue()); + } + + [Fact] + public void AllowNoThrowBehaviorOnUnexpectedLongOptionBeforeSingleValueOption_Default() + { + var arguments = new[] { "--unexpected", "--first", "one" }; + var app = new CommandLineApplication(throwOnUnexpectedArg: false); + app.Option("--first", "first option", CommandOptionType.SingleValue); + + // (does not throw) + app.Execute(arguments); + + Assert.Equal(arguments, app.RemainingArguments.ToArray()); + } + + [Fact] + public void AllowNoThrowBehaviorOnUnexpectedLongOptionBeforeSingleValueOption_Continue() + { + var unexpectedOption = "--unexpected"; + var app = new CommandLineApplication(throwOnUnexpectedArg: false, continueAfterUnexpectedArg: true); + var option = app.Option("--first", "first option", CommandOptionType.SingleValue); + + // (does not throw) + app.Execute(unexpectedOption, "--first", "one"); + + var arg = Assert.Single(app.RemainingArguments); + Assert.Equal(unexpectedOption, arg); + Assert.Equal("one", option.Value()); + } + + [Fact] + public void AllowNoThrowBehaviorOnUnexpectedLongOptionAfterSingleValueOption() + { + var unexpectedOption = "--unexpected"; + var app = new CommandLineApplication(throwOnUnexpectedArg: false); + var option = app.Option("--first", "first option", CommandOptionType.SingleValue); + + // (does not throw) + app.Execute("--first", "one", unexpectedOption); + + var arg = Assert.Single(app.RemainingArguments); + Assert.Equal(unexpectedOption, arg); + Assert.Equal("one", option.Value()); + } + + [Fact] + public void AllowNoThrowBehaviorOnUnexpectedLongOptionWithValueBeforeNoValueOption_Default() + { + var arguments = new[] { "--unexpected", "value", "--first" }; + var app = new CommandLineApplication(throwOnUnexpectedArg: false); + app.Option("--first", "first option", CommandOptionType.NoValue); + + // (does not throw) + app.Execute(arguments); + + Assert.Equal(arguments, app.RemainingArguments.ToArray()); + } + + [Fact] + public void AllowNoThrowBehaviorOnUnexpectedLongOptionWithValueBeforeNoValueOption_Continue() + { + var unexpectedOption = "--unexpected"; + var unexpectedValue = "value"; + var app = new CommandLineApplication(throwOnUnexpectedArg: false, continueAfterUnexpectedArg: true); + var option = app.Option("--first", "first option", CommandOptionType.NoValue); + + // (does not throw) + app.Execute(unexpectedOption, unexpectedValue, "--first"); + + Assert.Equal(new[] { unexpectedOption, unexpectedValue }, app.RemainingArguments.ToArray()); + Assert.True(option.HasValue()); + } + + [Fact] + public void AllowNoThrowBehaviorOnUnexpectedLongOptionWithValueAfterNoValueOption() + { + var unexpectedOption = "--unexpected"; + var unexpectedValue = "value"; + var app = new CommandLineApplication(throwOnUnexpectedArg: false); + var option = app.Option("--first", "first option", CommandOptionType.NoValue); + + // (does not throw) + app.Execute("--first", unexpectedOption, unexpectedValue); + + Assert.Equal(new[] { unexpectedOption, unexpectedValue }, app.RemainingArguments.ToArray()); + Assert.True(option.HasValue()); + } + + [Fact] + public void AllowNoThrowBehaviorOnUnexpectedLongOptionWithValueBeforeSingleValueOption_Default() + { + var unexpectedOption = "--unexpected"; + var unexpectedValue = "value"; + var app = new CommandLineApplication(throwOnUnexpectedArg: false); + app.Option("--first", "first option", CommandOptionType.SingleValue); + + // (does not throw) + app.Execute(unexpectedOption, unexpectedValue, "--first", "one"); + + Assert.Equal( + new[] { unexpectedOption, unexpectedValue, "--first", "one" }, + app.RemainingArguments.ToArray()); + } + + [Fact] + public void AllowNoThrowBehaviorOnUnexpectedLongOptionWithValueBeforeSingleValueOption_Continue() + { + var unexpectedOption = "--unexpected"; + var unexpectedValue = "value"; + var app = new CommandLineApplication(throwOnUnexpectedArg: false, continueAfterUnexpectedArg: true); + var option = app.Option("--first", "first option", CommandOptionType.SingleValue); + + // (does not throw) + app.Execute(unexpectedOption, unexpectedValue, "--first", "one"); + + Assert.Equal( + new[] { unexpectedOption, unexpectedValue }, + app.RemainingArguments.ToArray()); + Assert.Equal("one", option.Value()); + } + + [Fact] + public void AllowNoThrowBehaviorOnUnexpectedLongOptionWithValueAfterSingleValueOption() + { + var unexpectedOption = "--unexpected"; + var unexpectedValue = "value"; + var app = new CommandLineApplication(throwOnUnexpectedArg: false); + var option = app.Option("--first", "first option", CommandOptionType.SingleValue); + + // (does not throw) + app.Execute("--first", "one", unexpectedOption, unexpectedValue); + + Assert.Equal(new[] { unexpectedOption, unexpectedValue }, app.RemainingArguments.ToArray()); + Assert.Equal("one", option.Value()); + } + [Fact] public void ThrowsExceptionOnUnexpectedShortOptionByDefault() { @@ -374,12 +713,34 @@ public void ThrowsExceptionOnUnexpectedOptionBeforeValidSubcommandByDefault() } [Fact] - public void AllowNoThrowBehaviorOnUnexpectedOptionAfterSubcommand() + public void AllowNoThrowBehaviorOnUnexpectedOptionBeforeSubcommand() { var unexpectedOption = "--unexpected"; + var app = new CommandLineApplication(); + CommandLineApplication subCmd = null; + var testCmd = app.Command("k", c => + { + subCmd = c.Command("run", _ => { }); + c.OnExecute(() => 0); + }, + throwOnUnexpectedArg: false); + + // (does not throw) + app.Execute("k", unexpectedOption, "run"); + + Assert.Empty(app.RemainingArguments); + Assert.Equal(new[] { unexpectedOption, "run" }, testCmd.RemainingArguments.ToArray()); + Assert.Empty(subCmd.RemainingArguments); + } + + [Fact] + public void AllowNoThrowBehaviorOnUnexpectedOptionAfterSubcommand() + { + var unexpectedOption = "--unexpected"; var app = new CommandLineApplication(); + CommandLineApplication subCmd = null; var testCmd = app.Command("k", c => { subCmd = c.Command("run", _ => { }, throwOnUnexpectedArg: false); @@ -388,11 +749,44 @@ public void AllowNoThrowBehaviorOnUnexpectedOptionAfterSubcommand() // (does not throw) app.Execute("k", "run", unexpectedOption); + + Assert.Empty(app.RemainingArguments); Assert.Empty(testCmd.RemainingArguments); var arg = Assert.Single(subCmd.RemainingArguments); Assert.Equal(unexpectedOption, arg); } + [Fact] + public void AllowNoThrowBehaviorOnUnexpectedOptionBeforeValidCommand_Default() + { + var arguments = new[] { "--unexpected", "run" }; + var app = new CommandLineApplication(throwOnUnexpectedArg: false); + var commandRan = false; + app.Command("run", c => c.OnExecute(() => { commandRan = true; return 0; })); + app.OnExecute(() => 0); + + app.Execute(arguments); + + Assert.False(commandRan); + Assert.Equal(arguments, app.RemainingArguments.ToArray()); + } + + [Fact] + public void AllowNoThrowBehaviorOnUnexpectedOptionBeforeValidCommand_Continue() + { + var unexpectedOption = "--unexpected"; + var app = new CommandLineApplication(throwOnUnexpectedArg: false, continueAfterUnexpectedArg: true); + var commandRan = false; + app.Command("run", c => c.OnExecute(() => { commandRan = true; return 0; })); + app.OnExecute(() => 0); + + app.Execute(unexpectedOption, "run"); + + Assert.True(commandRan); + var remaining = Assert.Single(app.RemainingArguments); + Assert.Equal(unexpectedOption, remaining); + } + [Fact] public void OptionsCanBeInherited() { @@ -530,6 +924,91 @@ public void ArgumentSeparator(string[] input, string[] expectedRemaining, string Assert.Equal(expectedRemaining, app.RemainingArguments.ToArray()); } + [Theory] + [InlineData(new string[0], new string[0], null, false)] + [InlineData(new[] { "--" }, new[] { "--" }, null, false)] + [InlineData(new[] { "-t", "val" }, new string[0], "val", false)] + [InlineData(new[] { "-t", "val", "--" }, new[] { "--" }, "val", false)] + [InlineData(new[] { "--top", "val", "--", "a" }, new[] { "--", "a" }, "val", false)] + [InlineData(new[] { "-t", "val", "--", "a", "--", "b" }, new[] { "--", "a", "--", "b" }, "val", false)] + [InlineData(new[] { "--help", "--" }, new string[0], null, true)] + [InlineData(new[] { "--version", "--" }, new string[0], null, true)] + public void ArgumentSeparator_TreatedAsUexpected( + string[] input, + string[] expectedRemaining, + string topLevelValue, + bool isShowingInformation) + { + var app = new CommandLineApplication(throwOnUnexpectedArg: false); + var optHelp = app.HelpOption("--help"); + var optVersion = app.VersionOption("--version", "1", "1.0"); + var optTop = app.Option("-t|--top ", "arg for command", CommandOptionType.SingleValue); + + app.Execute(input); + + Assert.Equal(topLevelValue, optTop.Value()); + Assert.Equal(expectedRemaining, app.RemainingArguments.ToArray()); + Assert.Equal(isShowingInformation, app.IsShowingInformation); + + // Help and Version options never get values; parsing ends when encountered. + Assert.False(optHelp.HasValue()); + Assert.False(optVersion.HasValue()); + } + + [Theory] + [InlineData(new[] { "--", "a", "--top", "val" }, new[] { "--", "a", "--top", "val" }, null, false)] + [InlineData(new[] { "--", "--help" }, new[] { "--", "--help" }, null, false)] + [InlineData(new[] { "--", "--version" }, new[] { "--", "--version" }, null, false)] + [InlineData(new[] { "unexpected", "--", "--version" }, new[] { "unexpected", "--", "--version" }, null, false)] + public void ArgumentSeparator_TreatedAsUexpected_Default( + string[] input, + string[] expectedRemaining, + string topLevelValue, + bool isShowingInformation) + { + var app = new CommandLineApplication(throwOnUnexpectedArg: false); + var optHelp = app.HelpOption("--help"); + var optVersion = app.VersionOption("--version", "1", "1.0"); + var optTop = app.Option("-t|--top ", "arg for command", CommandOptionType.SingleValue); + + app.Execute(input); + + Assert.Equal(topLevelValue, optTop.Value()); + Assert.Equal(expectedRemaining, app.RemainingArguments.ToArray()); + Assert.Equal(isShowingInformation, app.IsShowingInformation); + + // Help and Version options never get values; parsing ends when encountered. + Assert.False(optHelp.HasValue()); + Assert.False(optVersion.HasValue()); + } + + [Theory] + [InlineData(new[] { "--", "a", "--top", "val" }, new[] { "--", "a" }, "val", false)] + [InlineData(new[] { "--", "--help" }, new[] { "--" }, null, true)] + [InlineData(new[] { "--", "--version" }, new[] { "--" }, null, true)] + [InlineData(new[] { "unexpected", "--", "--version" }, new[] { "unexpected", "--" }, null, true)] + public void ArgumentSeparator_TreatedAsUexpected_Continue( + string[] input, + string[] expectedRemaining, + string topLevelValue, + bool isShowingInformation) + { + var app = new CommandLineApplication(throwOnUnexpectedArg: false, continueAfterUnexpectedArg: true); + var optHelp = app.HelpOption("--help"); + var optVersion = app.VersionOption("--version", "1", "1.0"); + var optTop = app.Option("-t|--top ", "arg for command", CommandOptionType.SingleValue); + + app.Execute(input); + + Assert.Equal(topLevelValue, optTop.Value()); + Assert.Equal(expectedRemaining, app.RemainingArguments.ToArray()); + Assert.Equal(isShowingInformation, app.IsShowingInformation); + + // Help and Version options never get values; parsing ends when encountered. + Assert.False(optHelp.HasValue()); + Assert.False(optVersion.HasValue()); + } + [Fact] public void HelpTextIgnoresHiddenItems() { @@ -607,20 +1086,18 @@ dotnet befuddle -- I Can Haz Confusion Arguments [InlineData(new[] { "-h", "-f" }, "some flag")] public void HelpAndVersionOptionStopProcessing(string[] input, string expectedOutData) { - using (var outWriter = new StringWriter()) - { - var app = new CommandLineApplication { Out = outWriter }; - app.HelpOption("-h --help"); - app.VersionOption("-V --version", "1", "1.0"); - var optFlag = app.Option("-f |--flag", "some flag", CommandOptionType.NoValue); + using var outWriter = new StringWriter(); + var app = new CommandLineApplication { Out = outWriter }; + app.HelpOption("-h --help"); + app.VersionOption("-V --version", "1", "1.0"); + var optFlag = app.Option("-f |--flag", "some flag", CommandOptionType.NoValue); - app.Execute(input); + app.Execute(input); - outWriter.Flush(); - var outData = outWriter.ToString(); - Assert.Contains(expectedOutData, outData); - Assert.False(optFlag.HasValue()); - } + outWriter.Flush(); + var outData = outWriter.ToString(); + Assert.Contains(expectedOutData, outData); + Assert.False(optFlag.HasValue()); } // disable inaccurate analyzer error https://github.com/xunit/xunit/issues/1274 From e1974283f024bf2ced2a7723dc76928bd497978e Mon Sep 17 00:00:00 2001 From: Kevin Pilch Date: Fri, 23 Aug 2019 13:58:33 -0700 Subject: [PATCH 134/183] Add XML Docs for pubternal types \n\nCommit migrated from https://github.com/dotnet/extensions/commit/033ea04bab004a477acd6f023af171ee853965b8 --- src/FileProviders/Manifest.MSBuildTask/src/Entry.cs | 3 ++- .../Localization/src/Internal/AssemblyWrapper.cs | 6 +++++- .../Localization/src/Internal/IResourceStringProvider.cs | 8 ++++++-- .../src/Internal/ResourceManagerStringProvider.cs | 8 ++++++-- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/FileProviders/Manifest.MSBuildTask/src/Entry.cs b/src/FileProviders/Manifest.MSBuildTask/src/Entry.cs index 40c815fde4d7..1a7f18bef81e 100644 --- a/src/FileProviders/Manifest.MSBuildTask/src/Entry.cs +++ b/src/FileProviders/Manifest.MSBuildTask/src/Entry.cs @@ -8,7 +8,8 @@ namespace Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Internal { /// - /// This type is for internal uses only and is not meant to be consumed by any other library. + /// This API supports infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. /// [DebuggerDisplay("{Name,nq}")] public class Entry : IEquatable diff --git a/src/Localization/Localization/src/Internal/AssemblyWrapper.cs b/src/Localization/Localization/src/Internal/AssemblyWrapper.cs index b0c3c2bce1e4..11e118e3269d 100644 --- a/src/Localization/Localization/src/Internal/AssemblyWrapper.cs +++ b/src/Localization/Localization/src/Internal/AssemblyWrapper.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; @@ -7,6 +7,10 @@ namespace Microsoft.Extensions.Localization.Internal { + /// + /// This API supports infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// public class AssemblyWrapper { public AssemblyWrapper(Assembly assembly) diff --git a/src/Localization/Localization/src/Internal/IResourceStringProvider.cs b/src/Localization/Localization/src/Internal/IResourceStringProvider.cs index b74bd80edaa3..157e8e976e1a 100644 --- a/src/Localization/Localization/src/Internal/IResourceStringProvider.cs +++ b/src/Localization/Localization/src/Internal/IResourceStringProvider.cs @@ -1,11 +1,15 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Globalization; namespace Microsoft.Extensions.Localization.Internal { + /// + /// This API supports infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// public interface IResourceStringProvider { IList GetAllResourceStrings(CultureInfo culture, bool throwOnMissing); diff --git a/src/Localization/Localization/src/Internal/ResourceManagerStringProvider.cs b/src/Localization/Localization/src/Internal/ResourceManagerStringProvider.cs index 9eef8c84a835..62250938c871 100644 --- a/src/Localization/Localization/src/Internal/ResourceManagerStringProvider.cs +++ b/src/Localization/Localization/src/Internal/ResourceManagerStringProvider.cs @@ -1,5 +1,5 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections; using System.Collections.Generic; @@ -9,6 +9,10 @@ namespace Microsoft.Extensions.Localization.Internal { + /// + /// This API supports infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// public class ResourceManagerStringProvider : IResourceStringProvider { private readonly IResourceNamesCache _resourceNamesCache; From 72dc1409d65739f98cbdb185f8ab696e8f941065 Mon Sep 17 00:00:00 2001 From: Brennan Date: Tue, 27 Aug 2019 19:41:44 -0700 Subject: [PATCH 135/183] Fix ConditionalFact and ConditionalTheory (dotnet/extensions#2241) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/ae9d51ffeb3b2f38f6a3a0b9eb3d72e1762319d9 --- .../src/xunit/ConditionalFactAttribute.cs | 2 +- .../src/xunit/ConditionalTheoryAttribute.cs | 2 +- src/Testing/test/AlphabeticalOrderer.cs | 22 +++++++++++++++++++ src/Testing/test/ConditionalFactTest.cs | 10 +++++++-- src/Testing/test/ConditionalTheoryTest.cs | 10 +++++++-- 5 files changed, 40 insertions(+), 6 deletions(-) create mode 100644 src/Testing/test/AlphabeticalOrderer.cs diff --git a/src/Testing/src/xunit/ConditionalFactAttribute.cs b/src/Testing/src/xunit/ConditionalFactAttribute.cs index fdc108190afc..538a055792e9 100644 --- a/src/Testing/src/xunit/ConditionalFactAttribute.cs +++ b/src/Testing/src/xunit/ConditionalFactAttribute.cs @@ -8,7 +8,7 @@ namespace Microsoft.AspNetCore.Testing { [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] - [XunitTestCaseDiscoverer("Microsoft.AspNetCore.Testing.xunit." + nameof(ConditionalFactDiscoverer), "Microsoft.AspNetCore.Testing")] + [XunitTestCaseDiscoverer("Microsoft.AspNetCore.Testing." + nameof(ConditionalFactDiscoverer), "Microsoft.AspNetCore.Testing")] public class ConditionalFactAttribute : FactAttribute { } diff --git a/src/Testing/src/xunit/ConditionalTheoryAttribute.cs b/src/Testing/src/xunit/ConditionalTheoryAttribute.cs index 58b460e96e38..2fbac5d90c81 100644 --- a/src/Testing/src/xunit/ConditionalTheoryAttribute.cs +++ b/src/Testing/src/xunit/ConditionalTheoryAttribute.cs @@ -8,7 +8,7 @@ namespace Microsoft.AspNetCore.Testing { [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] - [XunitTestCaseDiscoverer("Microsoft.AspNetCore.Testing.xunit." + nameof(ConditionalTheoryDiscoverer), "Microsoft.AspNetCore.Testing")] + [XunitTestCaseDiscoverer("Microsoft.AspNetCore.Testing." + nameof(ConditionalTheoryDiscoverer), "Microsoft.AspNetCore.Testing")] public class ConditionalTheoryAttribute : TheoryAttribute { } diff --git a/src/Testing/test/AlphabeticalOrderer.cs b/src/Testing/test/AlphabeticalOrderer.cs new file mode 100644 index 000000000000..24970cc281ef --- /dev/null +++ b/src/Testing/test/AlphabeticalOrderer.cs @@ -0,0 +1,22 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit.Abstractions; +using Xunit.Sdk; + +namespace Microsoft.AspNetCore.Testing +{ + public class AlphabeticalOrderer : ITestCaseOrderer + { + public IEnumerable OrderTestCases(IEnumerable testCases) + where TTestCase : ITestCase + { + var result = testCases.ToList(); + result.Sort((x, y) => StringComparer.OrdinalIgnoreCase.Compare(x.TestMethod.Method.Name, y.TestMethod.Method.Name)); + return result; + } + } +} diff --git a/src/Testing/test/ConditionalFactTest.cs b/src/Testing/test/ConditionalFactTest.cs index efc3a16dea8d..0f1c6ada460c 100644 --- a/src/Testing/test/ConditionalFactTest.cs +++ b/src/Testing/test/ConditionalFactTest.cs @@ -2,11 +2,11 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using Microsoft.AspNetCore.Testing; using Xunit; namespace Microsoft.AspNetCore.Testing { + [TestCaseOrderer("Microsoft.AspNetCore.Testing.AlphabeticalOrderer", "Microsoft.AspNetCore.Testing.Tests")] public class ConditionalFactTest : IClassFixture { public ConditionalFactTest(ConditionalFactAsserter collector) @@ -47,13 +47,19 @@ public void ThisTestMustRunOnCLR() #error Target frameworks need to be updated. #endif + // Test is named this way to be the lowest test in the alphabet, it relies on test ordering + [Fact] + public void ZzzzzzzEnsureThisIsTheLastTest() + { + Assert.True(Asserter.TestRan); + } + public class ConditionalFactAsserter : IDisposable { public bool TestRan { get; set; } public void Dispose() { - Assert.True(TestRan, "If this assertion fails, a conditional fact wasn't discovered."); } } } diff --git a/src/Testing/test/ConditionalTheoryTest.cs b/src/Testing/test/ConditionalTheoryTest.cs index 07cf6a968f78..12deaba4f9bd 100644 --- a/src/Testing/test/ConditionalTheoryTest.cs +++ b/src/Testing/test/ConditionalTheoryTest.cs @@ -2,12 +2,12 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using Microsoft.AspNetCore.Testing; using Xunit; using Xunit.Abstractions; namespace Microsoft.AspNetCore.Testing { + [TestCaseOrderer("Microsoft.AspNetCore.Testing.AlphabeticalOrderer", "Microsoft.AspNetCore.Testing.Tests")] public class ConditionalTheoryTest : IClassFixture { public ConditionalTheoryTest(ConditionalTheoryAsserter asserter) @@ -101,6 +101,13 @@ public void ThisTestMustRunOnCLR(int value) #error Target frameworks need to be updated. #endif + // Test is named this way to be the lowest test in the alphabet, it relies on test ordering + [Fact] + public void ZzzzzzzEnsureThisIsTheLastTest() + { + Assert.True(Asserter.TestRan); + } + public static TheoryData> GetActionTestData => new TheoryData> { @@ -113,7 +120,6 @@ public class ConditionalTheoryAsserter : IDisposable public void Dispose() { - Assert.True(TestRan, "If this assertion fails, a conditional theory wasn't discovered."); } } From 4643c8f350394d570e71652aedd29830c9abb48a Mon Sep 17 00:00:00 2001 From: Brennan Date: Wed, 28 Aug 2019 09:10:43 -0700 Subject: [PATCH 136/183] Fix FlakyAttribute (dotnet/extensions#2245) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/1983ee879cc4d8e7922b0af8fba82fca2739c3c4 --- src/Testing/src/xunit/ConditionalFactDiscoverer.cs | 1 + src/Testing/src/xunit/ConditionalTheoryDiscoverer.cs | 1 + src/Testing/src/xunit/FlakyAttribute.cs | 6 +++--- src/Testing/src/xunit/FlakyTestDiscoverer.cs | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Testing/src/xunit/ConditionalFactDiscoverer.cs b/src/Testing/src/xunit/ConditionalFactDiscoverer.cs index ce190376fc17..e9a6b895ae89 100644 --- a/src/Testing/src/xunit/ConditionalFactDiscoverer.cs +++ b/src/Testing/src/xunit/ConditionalFactDiscoverer.cs @@ -4,6 +4,7 @@ using Xunit.Abstractions; using Xunit.Sdk; +// Do not change this namespace without changing the usage in ConditionalFactAttribute namespace Microsoft.AspNetCore.Testing { internal class ConditionalFactDiscoverer : FactDiscoverer diff --git a/src/Testing/src/xunit/ConditionalTheoryDiscoverer.cs b/src/Testing/src/xunit/ConditionalTheoryDiscoverer.cs index c9ee58889a74..764b613b16af 100644 --- a/src/Testing/src/xunit/ConditionalTheoryDiscoverer.cs +++ b/src/Testing/src/xunit/ConditionalTheoryDiscoverer.cs @@ -5,6 +5,7 @@ using Xunit.Abstractions; using Xunit.Sdk; +// Do not change this namespace without changing the usage in ConditionalTheoryAttribute namespace Microsoft.AspNetCore.Testing { internal class ConditionalTheoryDiscoverer : TheoryDiscoverer diff --git a/src/Testing/src/xunit/FlakyAttribute.cs b/src/Testing/src/xunit/FlakyAttribute.cs index acea96f3cb94..411435b74a01 100644 --- a/src/Testing/src/xunit/FlakyAttribute.cs +++ b/src/Testing/src/xunit/FlakyAttribute.cs @@ -49,7 +49,7 @@ namespace Microsoft.AspNetCore.Testing /// to xunit.console.exe. Similarly, it can run only flaky tests using -trait "Flaky:AzP:OS:all=true" -trait "Flaky:AzP:OS:Darwin=true" /// /// - [TraitDiscoverer("Microsoft.AspNetCore.Testing.xunit.FlakyTestDiscoverer", "Microsoft.AspNetCore.Testing")] + [TraitDiscoverer("Microsoft.AspNetCore.Testing." + nameof(FlakyTestDiscoverer), "Microsoft.AspNetCore.Testing")] [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly)] public sealed class FlakyAttribute : Attribute, ITraitAttribute { @@ -72,12 +72,12 @@ public sealed class FlakyAttribute : Attribute, ITraitAttribute /// A list of additional filters that define where this test is flaky. Use values in . public FlakyAttribute(string gitHubIssueUrl, string firstFilter, params string[] additionalFilters) { - if(string.IsNullOrEmpty(gitHubIssueUrl)) + if (string.IsNullOrEmpty(gitHubIssueUrl)) { throw new ArgumentNullException(nameof(gitHubIssueUrl)); } - if(string.IsNullOrEmpty(firstFilter)) + if (string.IsNullOrEmpty(firstFilter)) { throw new ArgumentNullException(nameof(firstFilter)); } diff --git a/src/Testing/src/xunit/FlakyTestDiscoverer.cs b/src/Testing/src/xunit/FlakyTestDiscoverer.cs index aea2f9ea5b90..0c481e1c4198 100644 --- a/src/Testing/src/xunit/FlakyTestDiscoverer.cs +++ b/src/Testing/src/xunit/FlakyTestDiscoverer.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; -using System.Linq; using Xunit.Abstractions; using Xunit.Sdk; +// Do not change this namespace without changing the usage in FlakyAttribute namespace Microsoft.AspNetCore.Testing { public class FlakyTestDiscoverer : ITraitDiscoverer From b4304367f9d0536983df54e787880ef700234618 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leandro=20L=C3=B3pez?= Date: Thu, 29 Aug 2019 10:22:41 -0300 Subject: [PATCH 137/183] Fix typo\n\nCommit migrated from https://github.com/dotnet/extensions/commit/6df967b79a6dec5c14b308b8b3de1996fd6b8018 --- src/HealthChecks/Abstractions/src/HealthReport.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HealthChecks/Abstractions/src/HealthReport.cs b/src/HealthChecks/Abstractions/src/HealthReport.cs index 91ed798811d5..1dacff45e3c2 100644 --- a/src/HealthChecks/Abstractions/src/HealthReport.cs +++ b/src/HealthChecks/Abstractions/src/HealthReport.cs @@ -34,7 +34,7 @@ public HealthReport(IReadOnlyDictionary entries, Time /// /// Gets a representing the aggregate status of all the health checks. The value of - /// will be the most servere status reported by a health check. If no checks were executed, the value is always . + /// will be the most severe status reported by a health check. If no checks were executed, the value is always . /// public HealthStatus Status { get; } From fd2033e5d5e6db907aa054f3f57aa328d5fae7f9 Mon Sep 17 00:00:00 2001 From: John Luo Date: Thu, 12 Sep 2019 22:34:52 -0700 Subject: [PATCH 138/183] Support netcoreapp3.1 TFM (dotnet/extensions#2336) * Support netcoreapp3.1 TFM * Unpin SDK for source build * Update to preview1 branding \n\nCommit migrated from https://github.com/dotnet/extensions/commit/32cc8162ff38356b293611138d361f757a62e025 --- .../Microsoft.Extensions.Configuration.KeyPerFile.csproj | 6 +++--- ...osoft.Extensions.Configuration.KeyPerFile.netcoreapp.cs} | 0 .../Microsoft.Extensions.Configuration.KeyPerFile.csproj | 4 ++-- ...crosoft.Extensions.Configuration.KeyPerFile.Tests.csproj | 2 +- .../ref/Microsoft.Extensions.FileProviders.Embedded.csproj | 6 +++--- ...crosoft.Extensions.FileProviders.Embedded.netcoreapp.cs} | 0 .../src/Microsoft.Extensions.FileProviders.Embedded.csproj | 6 +++--- ...oft.Extensions.FileProviders.Embedded.multitarget.nuspec | 2 +- ...oft.Extensions.FileProviders.Embedded.netcoreapp.nuspec} | 2 +- ...Microsoft.Extensions.FileProviders.Embedded.Tests.csproj | 2 +- ...nsions.FileProviders.Embedded.Manifest.Task.Tests.csproj | 2 +- ....Extensions.Diagnostics.HealthChecks.Abstractions.csproj | 6 +++--- ...ons.Diagnostics.HealthChecks.Abstractions.netcoreapp.cs} | 0 ....Extensions.Diagnostics.HealthChecks.Abstractions.csproj | 4 ++-- .../Microsoft.Extensions.Diagnostics.HealthChecks.csproj | 6 +++--- ...osoft.Extensions.Diagnostics.HealthChecks.netcoreapp.cs} | 0 .../Microsoft.Extensions.Diagnostics.HealthChecks.csproj | 4 ++-- ...crosoft.Extensions.Diagnostics.HealthChecks.Tests.csproj | 2 +- .../Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj | 6 +++--- ...p.netcoreapp3.0.cs => Microsoft.JSInterop.netcoreapp.cs} | 0 .../Microsoft.JSInterop/src/Microsoft.JSInterop.csproj | 4 ++-- .../test/Microsoft.JSInterop.Tests.csproj | 2 +- .../Microsoft.Extensions.Localization.Abstractions.csproj | 6 +++--- ...soft.Extensions.Localization.Abstractions.netcoreapp.cs} | 0 .../Microsoft.Extensions.Localization.Abstractions.csproj | 4 ++-- .../ref/Microsoft.Extensions.Localization.csproj | 6 +++--- ...0.cs => Microsoft.Extensions.Localization.netcoreapp.cs} | 0 .../src/Microsoft.Extensions.Localization.csproj | 4 ++-- .../test/Microsoft.Extensions.Localization.Tests.csproj | 2 +- .../test/Microsoft.Extensions.ObjectPool.Tests.csproj | 2 +- src/Shared/ActivatorUtilities/ActivatorUtilities.cs | 2 +- src/Shared/BenchmarkRunner/DefaultCoreConfig.cs | 6 +++++- src/Shared/test/Shared.Tests/DotNetMuxerTests.cs | 2 +- .../Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj | 2 +- .../BuildWebHostInvalidSignature.csproj | 2 +- .../BuildWebHostPatternTestSite.csproj | 2 +- .../CreateHostBuilderInvalidSignature.csproj | 2 +- .../CreateHostBuilderPatternTestSite.csproj | 2 +- .../CreateWebHostBuilderInvalidSignature.csproj | 2 +- .../CreateWebHostBuilderPatternTestSite.csproj | 2 +- .../test/testassets/MockHostTypes/MockHostTypes.csproj | 2 +- src/Testing/test/ConditionalFactTest.cs | 2 +- src/Testing/test/ConditionalTheoryTest.cs | 2 +- src/Testing/test/Microsoft.AspNetCore.Testing.Tests.csproj | 2 +- src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj | 6 +++--- ....0.cs => Microsoft.Extensions.WebEncoders.netcoreapp.cs} | 0 src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj | 4 ++-- .../test/Microsoft.Extensions.WebEncoders.Tests.csproj | 2 +- 48 files changed, 69 insertions(+), 65 deletions(-) rename src/Configuration.KeyPerFile/ref/{Microsoft.Extensions.Configuration.KeyPerFile.netcoreapp3.0.cs => Microsoft.Extensions.Configuration.KeyPerFile.netcoreapp.cs} (100%) rename src/FileProviders/Embedded/ref/{Microsoft.Extensions.FileProviders.Embedded.netcoreapp3.0.cs => Microsoft.Extensions.FileProviders.Embedded.netcoreapp.cs} (100%) rename src/FileProviders/Embedded/src/{Microsoft.Extensions.FileProviders.Embedded.netcoreapp3.0.nuspec => Microsoft.Extensions.FileProviders.Embedded.netcoreapp.nuspec} (94%) rename src/HealthChecks/Abstractions/ref/{Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netcoreapp3.0.cs => Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netcoreapp.cs} (100%) rename src/HealthChecks/HealthChecks/ref/{Microsoft.Extensions.Diagnostics.HealthChecks.netcoreapp3.0.cs => Microsoft.Extensions.Diagnostics.HealthChecks.netcoreapp.cs} (100%) rename src/JSInterop/Microsoft.JSInterop/ref/{Microsoft.JSInterop.netcoreapp3.0.cs => Microsoft.JSInterop.netcoreapp.cs} (100%) rename src/Localization/Abstractions/ref/{Microsoft.Extensions.Localization.Abstractions.netcoreapp3.0.cs => Microsoft.Extensions.Localization.Abstractions.netcoreapp.cs} (100%) rename src/Localization/Localization/ref/{Microsoft.Extensions.Localization.netcoreapp3.0.cs => Microsoft.Extensions.Localization.netcoreapp.cs} (100%) rename src/WebEncoders/ref/{Microsoft.Extensions.WebEncoders.netcoreapp3.0.cs => Microsoft.Extensions.WebEncoders.netcoreapp.cs} (100%) diff --git a/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj b/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj index 4e074ab493c9..5500c4ddc811 100644 --- a/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj +++ b/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj @@ -1,15 +1,15 @@ - netstandard2.0;netcoreapp3.0 + netstandard2.0;netcoreapp3.1 - - + + diff --git a/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.netcoreapp3.0.cs b/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.netcoreapp.cs similarity index 100% rename from src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.netcoreapp3.0.cs rename to src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.netcoreapp.cs diff --git a/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj b/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj index 10f41fa9f49d..7f9c5e7eb193 100644 --- a/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj +++ b/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj @@ -2,8 +2,8 @@ Configuration provider that uses files in a directory for Microsoft.Extensions.Configuration. - netstandard2.0;netcoreapp3.0 - netcoreapp3.0 + netstandard2.0;$(DefaultNetCoreTargetFramework) + $(DefaultNetCoreTargetFramework) true true diff --git a/src/Configuration.KeyPerFile/test/Microsoft.Extensions.Configuration.KeyPerFile.Tests.csproj b/src/Configuration.KeyPerFile/test/Microsoft.Extensions.Configuration.KeyPerFile.Tests.csproj index 95a73ae6cfdf..096cf2e2e0dc 100644 --- a/src/Configuration.KeyPerFile/test/Microsoft.Extensions.Configuration.KeyPerFile.Tests.csproj +++ b/src/Configuration.KeyPerFile/test/Microsoft.Extensions.Configuration.KeyPerFile.Tests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0;net472 + $(DefaultNetCoreTargetFramework);net472 diff --git a/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.csproj b/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.csproj index e9407f9164b0..355c5b738e56 100644 --- a/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.csproj +++ b/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.csproj @@ -1,14 +1,14 @@ - netstandard2.0;netcoreapp3.0 + netstandard2.0;netcoreapp3.1 - - + + diff --git a/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.netcoreapp3.0.cs b/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.netcoreapp.cs similarity index 100% rename from src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.netcoreapp3.0.cs rename to src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.netcoreapp.cs diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj index d36323e3b151..dc49b3fd75ca 100644 --- a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj @@ -3,10 +3,10 @@ Microsoft.Extensions.FileProviders File provider for files in embedded resources for Microsoft.Extensions.FileProviders. - netstandard2.0;netcoreapp3.0 + netstandard2.0;$(DefaultNetCoreTargetFramework) $(MSBuildProjectName).multitarget.nuspec - netcoreapp3.0 - $(MSBuildProjectName).netcoreapp3.0.nuspec + $(DefaultNetCoreTargetFramework) + $(MSBuildProjectName).netcoreapp.nuspec true true diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.multitarget.nuspec b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.multitarget.nuspec index 874c90c79dcc..59a0e89b7f77 100644 --- a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.multitarget.nuspec +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.multitarget.nuspec @@ -3,7 +3,7 @@ $CommonMetadataElements$ - + diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.netcoreapp3.0.nuspec b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.netcoreapp.nuspec similarity index 94% rename from src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.netcoreapp3.0.nuspec rename to src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.netcoreapp.nuspec index f98f0b4ad5aa..b0e9df487562 100644 --- a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.netcoreapp3.0.nuspec +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.netcoreapp.nuspec @@ -3,7 +3,7 @@ $CommonMetadataElements$ - + diff --git a/src/FileProviders/Embedded/test/Microsoft.Extensions.FileProviders.Embedded.Tests.csproj b/src/FileProviders/Embedded/test/Microsoft.Extensions.FileProviders.Embedded.Tests.csproj index 837774770263..a199e4383757 100644 --- a/src/FileProviders/Embedded/test/Microsoft.Extensions.FileProviders.Embedded.Tests.csproj +++ b/src/FileProviders/Embedded/test/Microsoft.Extensions.FileProviders.Embedded.Tests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0;net472 + $(DefaultNetCoreTargetFramework);net472 diff --git a/src/FileProviders/Manifest.MSBuildTask/test/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Tests.csproj b/src/FileProviders/Manifest.MSBuildTask/test/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Tests.csproj index ed68958fe871..06afd574e83a 100644 --- a/src/FileProviders/Manifest.MSBuildTask/test/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Tests.csproj +++ b/src/FileProviders/Manifest.MSBuildTask/test/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Tests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0 + $(DefaultNetCoreTargetFramework) diff --git a/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj index f7514489fa30..e09ee4398142 100644 --- a/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj +++ b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj @@ -1,14 +1,14 @@ - netstandard2.0;netcoreapp3.0 + netstandard2.0;netcoreapp3.1 - - + + diff --git a/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netcoreapp3.0.cs b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netcoreapp.cs similarity index 100% rename from src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netcoreapp3.0.cs rename to src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netcoreapp.cs diff --git a/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj b/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj index f41b257c6322..aeb85d3e7603 100644 --- a/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj +++ b/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj @@ -7,8 +7,8 @@ Commonly Used Types Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck Microsoft.Extensions.Diagnostics.HealthChecks - netstandard2.0;netcoreapp3.0 - netcoreapp3.0 + netstandard2.0;$(DefaultNetCoreTargetFramework) + $(DefaultNetCoreTargetFramework) $(NoWarn);CS1591 true diagnostics;healthchecks diff --git a/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj b/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj index 2286087bc786..a2080b28d3da 100644 --- a/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj +++ b/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj @@ -1,7 +1,7 @@ - netstandard2.0;netcoreapp3.0 + netstandard2.0;netcoreapp3.1 @@ -9,8 +9,8 @@ - - + + diff --git a/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netcoreapp3.0.cs b/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netcoreapp.cs similarity index 100% rename from src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netcoreapp3.0.cs rename to src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netcoreapp.cs diff --git a/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj b/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj index 40056850113e..35c789a691f2 100644 --- a/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj +++ b/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj @@ -6,8 +6,8 @@ Commonly Used Types: Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckService Microsoft.Extensions.Diagnostics.HealthChecks.IHealthChecksBuilder - netstandard2.0;netcoreapp3.0 - netcoreapp3.0 + netstandard2.0;$(DefaultNetCoreTargetFramework) + $(DefaultNetCoreTargetFramework) $(NoWarn);CS1591 true diagnostics;healthchecks diff --git a/src/HealthChecks/HealthChecks/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests.csproj b/src/HealthChecks/HealthChecks/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests.csproj index 21763e516510..b6fa1537da13 100644 --- a/src/HealthChecks/HealthChecks/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests.csproj +++ b/src/HealthChecks/HealthChecks/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests.csproj @@ -3,7 +3,7 @@ - netcoreapp3.0;net472 + $(DefaultNetCoreTargetFramework);net472 Microsoft.Extensions.Diagnostics.HealthChecks diff --git a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj index 61980aa92097..3c0752534723 100644 --- a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj +++ b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj @@ -1,14 +1,14 @@ - netstandard2.0;netcoreapp3.0 + netstandard2.0;netcoreapp3.1 - - + + diff --git a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netcoreapp3.0.cs b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netcoreapp.cs similarity index 100% rename from src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netcoreapp3.0.cs rename to src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netcoreapp.cs diff --git a/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj b/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj index 044c843b2815..a45484e0d3a7 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj +++ b/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj @@ -1,8 +1,8 @@  - netstandard2.0;netcoreapp3.0 - netcoreapp3.0 + netstandard2.0;$(DefaultNetCoreTargetFramework) + $(DefaultNetCoreTargetFramework) Abstractions and features for interop between .NET and JavaScript code. javascript;interop true diff --git a/src/JSInterop/Microsoft.JSInterop/test/Microsoft.JSInterop.Tests.csproj b/src/JSInterop/Microsoft.JSInterop/test/Microsoft.JSInterop.Tests.csproj index bff8fb3f99d2..ba3a91a94eb3 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/Microsoft.JSInterop.Tests.csproj +++ b/src/JSInterop/Microsoft.JSInterop/test/Microsoft.JSInterop.Tests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0;net472 + $(DefaultNetCoreTargetFramework);net472 diff --git a/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.csproj b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.csproj index 6404d5ae8ecb..c45066d3cbd7 100644 --- a/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.csproj +++ b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.csproj @@ -1,14 +1,14 @@ - netstandard2.0;netcoreapp3.0 + netstandard2.0;netcoreapp3.1 - - + + diff --git a/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netcoreapp3.0.cs b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netcoreapp.cs similarity index 100% rename from src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netcoreapp3.0.cs rename to src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netcoreapp.cs diff --git a/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj b/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj index 09b8bf65b79e..4b8816040148 100644 --- a/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj +++ b/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj @@ -6,8 +6,8 @@ Commonly used types: Microsoft.Extensions.Localization.IStringLocalizer Microsoft.Extensions.Localization.IStringLocalizer<T> - netstandard2.0;netcoreapp3.0 - netcoreapp3.0 + netstandard2.0;$(DefaultNetCoreTargetFramework) + $(DefaultNetCoreTargetFramework) $(NoWarn);CS1591 true localization diff --git a/src/Localization/Localization/ref/Microsoft.Extensions.Localization.csproj b/src/Localization/Localization/ref/Microsoft.Extensions.Localization.csproj index 735277e75418..b6df43f5db32 100644 --- a/src/Localization/Localization/ref/Microsoft.Extensions.Localization.csproj +++ b/src/Localization/Localization/ref/Microsoft.Extensions.Localization.csproj @@ -1,7 +1,7 @@ - netstandard2.0;netcoreapp3.0 + netstandard2.0;netcoreapp3.1 @@ -10,8 +10,8 @@ - - + + diff --git a/src/Localization/Localization/ref/Microsoft.Extensions.Localization.netcoreapp3.0.cs b/src/Localization/Localization/ref/Microsoft.Extensions.Localization.netcoreapp.cs similarity index 100% rename from src/Localization/Localization/ref/Microsoft.Extensions.Localization.netcoreapp3.0.cs rename to src/Localization/Localization/ref/Microsoft.Extensions.Localization.netcoreapp.cs diff --git a/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj b/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj index 86ebaf1970fc..b6b059ce827f 100644 --- a/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj +++ b/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj @@ -3,8 +3,8 @@ Microsoft .NET Extensions Application localization services and default implementation based on ResourceManager to load localized assembly resources. - netstandard2.0;netcoreapp3.0 - netcoreapp3.0 + netstandard2.0;$(DefaultNetCoreTargetFramework) + $(DefaultNetCoreTargetFramework) $(NoWarn);CS1591 true localization diff --git a/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests.csproj b/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests.csproj index 17254e211053..70e7a6de1a9d 100644 --- a/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests.csproj +++ b/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0;net472 + $(DefaultNetCoreTargetFramework);net472 diff --git a/src/ObjectPool/test/Microsoft.Extensions.ObjectPool.Tests.csproj b/src/ObjectPool/test/Microsoft.Extensions.ObjectPool.Tests.csproj index 1f2ad67664db..cc308fa8a009 100644 --- a/src/ObjectPool/test/Microsoft.Extensions.ObjectPool.Tests.csproj +++ b/src/ObjectPool/test/Microsoft.Extensions.ObjectPool.Tests.csproj @@ -1,7 +1,7 @@ - netcoreapp3.0;net472 + $(DefaultNetCoreTargetFramework);net472 diff --git a/src/Shared/ActivatorUtilities/ActivatorUtilities.cs b/src/Shared/ActivatorUtilities/ActivatorUtilities.cs index 3fd2b557ffe2..4d05ebf58967 100644 --- a/src/Shared/ActivatorUtilities/ActivatorUtilities.cs +++ b/src/Shared/ActivatorUtilities/ActivatorUtilities.cs @@ -402,7 +402,7 @@ public object CreateInstance(IServiceProvider provider) } } -#if NETCOREAPP3_0 +#if NETCOREAPP return _constructor.Invoke(BindingFlags.DoNotWrapExceptions, binder: null, parameters: _parameterValues, culture: null); #else try diff --git a/src/Shared/BenchmarkRunner/DefaultCoreConfig.cs b/src/Shared/BenchmarkRunner/DefaultCoreConfig.cs index a61833ab269d..832963501708 100644 --- a/src/Shared/BenchmarkRunner/DefaultCoreConfig.cs +++ b/src/Shared/BenchmarkRunner/DefaultCoreConfig.cs @@ -30,8 +30,12 @@ public DefaultCoreConfig() Add(Job.Core #if NETCOREAPP2_1 .With(CsProjCoreToolchain.From(NetCoreAppSettings.NetCoreApp21)) -#else +#elif NETCOREAPP3_0 .With(CsProjCoreToolchain.From(new NetCoreAppSettings("netcoreapp3.0", null, ".NET Core 3.0"))) +#elif NETCOREAPP3_1 + .With(CsProjCoreToolchain.From(new NetCoreAppSettings("netcoreapp3.1", null, ".NET Core 3.1"))) +#else +#error Target frameworks need to be updated. #endif .With(new GcMode { Server = true }) .With(RunStrategy.Throughput)); diff --git a/src/Shared/test/Shared.Tests/DotNetMuxerTests.cs b/src/Shared/test/Shared.Tests/DotNetMuxerTests.cs index 2f412e292ed8..8840d87bb84b 100644 --- a/src/Shared/test/Shared.Tests/DotNetMuxerTests.cs +++ b/src/Shared/test/Shared.Tests/DotNetMuxerTests.cs @@ -1,7 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -#if NETCOREAPP3_0 +#if NETCOREAPP using System.IO; using System.Runtime.InteropServices; using Xunit; diff --git a/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj b/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj index daf7eef7ea60..5d7cd465c1cb 100644 --- a/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj +++ b/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj @@ -1,7 +1,7 @@ - netcoreapp3.0;net472 + $(DefaultNetCoreTargetFramework);net472 true diff --git a/src/Shared/test/testassets/BuildWebHostInvalidSignature/BuildWebHostInvalidSignature.csproj b/src/Shared/test/testassets/BuildWebHostInvalidSignature/BuildWebHostInvalidSignature.csproj index 6368289f65e7..05ca293b6eae 100644 --- a/src/Shared/test/testassets/BuildWebHostInvalidSignature/BuildWebHostInvalidSignature.csproj +++ b/src/Shared/test/testassets/BuildWebHostInvalidSignature/BuildWebHostInvalidSignature.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0;net472 + $(DefaultNetCoreTargetFramework);net472 Exe diff --git a/src/Shared/test/testassets/BuildWebHostPatternTestSite/BuildWebHostPatternTestSite.csproj b/src/Shared/test/testassets/BuildWebHostPatternTestSite/BuildWebHostPatternTestSite.csproj index 6368289f65e7..05ca293b6eae 100644 --- a/src/Shared/test/testassets/BuildWebHostPatternTestSite/BuildWebHostPatternTestSite.csproj +++ b/src/Shared/test/testassets/BuildWebHostPatternTestSite/BuildWebHostPatternTestSite.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0;net472 + $(DefaultNetCoreTargetFramework);net472 Exe diff --git a/src/Shared/test/testassets/CreateHostBuilderInvalidSignature/CreateHostBuilderInvalidSignature.csproj b/src/Shared/test/testassets/CreateHostBuilderInvalidSignature/CreateHostBuilderInvalidSignature.csproj index 6368289f65e7..05ca293b6eae 100644 --- a/src/Shared/test/testassets/CreateHostBuilderInvalidSignature/CreateHostBuilderInvalidSignature.csproj +++ b/src/Shared/test/testassets/CreateHostBuilderInvalidSignature/CreateHostBuilderInvalidSignature.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0;net472 + $(DefaultNetCoreTargetFramework);net472 Exe diff --git a/src/Shared/test/testassets/CreateHostBuilderPatternTestSite/CreateHostBuilderPatternTestSite.csproj b/src/Shared/test/testassets/CreateHostBuilderPatternTestSite/CreateHostBuilderPatternTestSite.csproj index 6368289f65e7..05ca293b6eae 100644 --- a/src/Shared/test/testassets/CreateHostBuilderPatternTestSite/CreateHostBuilderPatternTestSite.csproj +++ b/src/Shared/test/testassets/CreateHostBuilderPatternTestSite/CreateHostBuilderPatternTestSite.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0;net472 + $(DefaultNetCoreTargetFramework);net472 Exe diff --git a/src/Shared/test/testassets/CreateWebHostBuilderInvalidSignature/CreateWebHostBuilderInvalidSignature.csproj b/src/Shared/test/testassets/CreateWebHostBuilderInvalidSignature/CreateWebHostBuilderInvalidSignature.csproj index 6368289f65e7..05ca293b6eae 100644 --- a/src/Shared/test/testassets/CreateWebHostBuilderInvalidSignature/CreateWebHostBuilderInvalidSignature.csproj +++ b/src/Shared/test/testassets/CreateWebHostBuilderInvalidSignature/CreateWebHostBuilderInvalidSignature.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0;net472 + $(DefaultNetCoreTargetFramework);net472 Exe diff --git a/src/Shared/test/testassets/CreateWebHostBuilderPatternTestSite/CreateWebHostBuilderPatternTestSite.csproj b/src/Shared/test/testassets/CreateWebHostBuilderPatternTestSite/CreateWebHostBuilderPatternTestSite.csproj index 6368289f65e7..05ca293b6eae 100644 --- a/src/Shared/test/testassets/CreateWebHostBuilderPatternTestSite/CreateWebHostBuilderPatternTestSite.csproj +++ b/src/Shared/test/testassets/CreateWebHostBuilderPatternTestSite/CreateWebHostBuilderPatternTestSite.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0;net472 + $(DefaultNetCoreTargetFramework);net472 Exe diff --git a/src/Shared/test/testassets/MockHostTypes/MockHostTypes.csproj b/src/Shared/test/testassets/MockHostTypes/MockHostTypes.csproj index 3272f8d93ae7..57b6e1ae58fd 100644 --- a/src/Shared/test/testassets/MockHostTypes/MockHostTypes.csproj +++ b/src/Shared/test/testassets/MockHostTypes/MockHostTypes.csproj @@ -1,7 +1,7 @@ - netcoreapp3.0;net472 + $(DefaultNetCoreTargetFramework);net472 diff --git a/src/Testing/test/ConditionalFactTest.cs b/src/Testing/test/ConditionalFactTest.cs index 0f1c6ada460c..fefe6c5a42d2 100644 --- a/src/Testing/test/ConditionalFactTest.cs +++ b/src/Testing/test/ConditionalFactTest.cs @@ -29,7 +29,7 @@ public void ConditionalFactSkip() Assert.True(false, "This test should always be skipped."); } -#if NETCOREAPP3_0 +#if NETCOREAPP [ConditionalFact] [FrameworkSkipCondition(RuntimeFrameworks.CLR)] public void ThisTestMustRunOnCoreCLR() diff --git a/src/Testing/test/ConditionalTheoryTest.cs b/src/Testing/test/ConditionalTheoryTest.cs index 12deaba4f9bd..e88a3334f29d 100644 --- a/src/Testing/test/ConditionalTheoryTest.cs +++ b/src/Testing/test/ConditionalTheoryTest.cs @@ -81,7 +81,7 @@ public void TestAlwaysRun() Assert.True(true); } -#if NETCOREAPP3_0 +#if NETCOREAPP [ConditionalTheory] [FrameworkSkipCondition(RuntimeFrameworks.CLR)] [MemberData(nameof(GetInts))] diff --git a/src/Testing/test/Microsoft.AspNetCore.Testing.Tests.csproj b/src/Testing/test/Microsoft.AspNetCore.Testing.Tests.csproj index e9d0dca4aebc..5a7366503d6f 100644 --- a/src/Testing/test/Microsoft.AspNetCore.Testing.Tests.csproj +++ b/src/Testing/test/Microsoft.AspNetCore.Testing.Tests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0;net472 + $(DefaultNetCoreTargetFramework);net472 $(NoWarn);xUnit1004 diff --git a/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj b/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj index 628691a220f9..283cb96e98cb 100644 --- a/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj +++ b/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj @@ -1,7 +1,7 @@ - netstandard2.0;netcoreapp3.0 + netstandard2.0;netcoreapp3.1 @@ -9,8 +9,8 @@ - - + + diff --git a/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.netcoreapp3.0.cs b/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.netcoreapp.cs similarity index 100% rename from src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.netcoreapp3.0.cs rename to src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.netcoreapp.cs diff --git a/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj b/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj index 2ed725eac7ea..364f4d3d8663 100644 --- a/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj +++ b/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj @@ -2,8 +2,8 @@ Contains registration and configuration APIs to add the core framework encoders to a dependency injection container. - netstandard2.0;netcoreapp3.0 - netcoreapp3.0 + netstandard2.0;$(DefaultNetCoreTargetFramework) + $(DefaultNetCoreTargetFramework) $(NoWarn);CS1591 true aspnetcore diff --git a/src/WebEncoders/test/Microsoft.Extensions.WebEncoders.Tests.csproj b/src/WebEncoders/test/Microsoft.Extensions.WebEncoders.Tests.csproj index 68ad589e3f64..0f93bf3fc67d 100755 --- a/src/WebEncoders/test/Microsoft.Extensions.WebEncoders.Tests.csproj +++ b/src/WebEncoders/test/Microsoft.Extensions.WebEncoders.Tests.csproj @@ -1,7 +1,7 @@ - netcoreapp3.0;net472 + $(DefaultNetCoreTargetFramework);net472 From 68f3765e6d677bd30383f97799133167045a9f22 Mon Sep 17 00:00:00 2001 From: John Luo Date: Mon, 16 Sep 2019 15:17:32 -0700 Subject: [PATCH 139/183] Fix packing on *nix systems (dotnet/extensions#2344) - Without this fix, the nupkg will be missing files if packed on *nix systems\n\nCommit migrated from https://github.com/dotnet/extensions/commit/51e8af72d5bb14b970dde12caa089d7357631614 --- .../src/Microsoft.Extensions.FileProviders.Embedded.csproj | 4 +--- ...oft.Extensions.FileProviders.Embedded.multitarget.nuspec | 6 +++--- ...soft.Extensions.FileProviders.Embedded.netcoreapp.nuspec | 6 +++--- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj index dc49b3fd75ca..3d5fe3cf54fa 100644 --- a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj @@ -27,9 +27,7 @@ - - - + diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.multitarget.nuspec b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.multitarget.nuspec index 59a0e89b7f77..a11c9c591eed 100644 --- a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.multitarget.nuspec +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.multitarget.nuspec @@ -13,9 +13,9 @@ - - - + + + diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.netcoreapp.nuspec b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.netcoreapp.nuspec index b0e9df487562..3033cff8c966 100644 --- a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.netcoreapp.nuspec +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.netcoreapp.nuspec @@ -10,9 +10,9 @@ - - - + + + From 6371d93d181b4d74d421b9ed4a18734d6e492960 Mon Sep 17 00:00:00 2001 From: John Luo Date: Tue, 17 Sep 2019 11:17:34 -0700 Subject: [PATCH 140/183] Update ref assembly generation to use DefaultNetCoreTargetFramework property (dotnet/extensions#2359) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/1f41bdc38668fcf1998fd29325e8267e4e034893 --- .../ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj | 4 ++-- .../ref/Microsoft.Extensions.FileProviders.Embedded.csproj | 4 ++-- ...ft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj | 4 ++-- .../ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj | 4 ++-- .../Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj | 4 ++-- .../ref/Microsoft.Extensions.Localization.Abstractions.csproj | 4 ++-- .../Localization/ref/Microsoft.Extensions.Localization.csproj | 4 ++-- src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj | 4 ++-- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj b/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj index 5500c4ddc811..8dbe6d3ba11c 100644 --- a/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj +++ b/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj @@ -1,14 +1,14 @@ - netstandard2.0;netcoreapp3.1 + netstandard2.0;$(DefaultNetCoreTargetFramework) - + diff --git a/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.csproj b/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.csproj index 355c5b738e56..89cba5ba5723 100644 --- a/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.csproj +++ b/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.csproj @@ -1,13 +1,13 @@ - netstandard2.0;netcoreapp3.1 + netstandard2.0;$(DefaultNetCoreTargetFramework) - + diff --git a/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj index e09ee4398142..6b67a0868621 100644 --- a/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj +++ b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj @@ -1,13 +1,13 @@ - netstandard2.0;netcoreapp3.1 + netstandard2.0;$(DefaultNetCoreTargetFramework) - + diff --git a/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj b/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj index a2080b28d3da..83dae521fbbf 100644 --- a/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj +++ b/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj @@ -1,7 +1,7 @@ - netstandard2.0;netcoreapp3.1 + netstandard2.0;$(DefaultNetCoreTargetFramework) @@ -9,7 +9,7 @@ - + diff --git a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj index 3c0752534723..ceb24636ab4d 100644 --- a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj +++ b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj @@ -1,13 +1,13 @@ - netstandard2.0;netcoreapp3.1 + netstandard2.0;$(DefaultNetCoreTargetFramework) - + diff --git a/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.csproj b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.csproj index c45066d3cbd7..af97cae5940d 100644 --- a/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.csproj +++ b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.csproj @@ -1,13 +1,13 @@ - netstandard2.0;netcoreapp3.1 + netstandard2.0;$(DefaultNetCoreTargetFramework) - + diff --git a/src/Localization/Localization/ref/Microsoft.Extensions.Localization.csproj b/src/Localization/Localization/ref/Microsoft.Extensions.Localization.csproj index b6df43f5db32..67e8f38f8949 100644 --- a/src/Localization/Localization/ref/Microsoft.Extensions.Localization.csproj +++ b/src/Localization/Localization/ref/Microsoft.Extensions.Localization.csproj @@ -1,7 +1,7 @@ - netstandard2.0;netcoreapp3.1 + netstandard2.0;$(DefaultNetCoreTargetFramework) @@ -10,7 +10,7 @@ - + diff --git a/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj b/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj index 283cb96e98cb..5beee97dd6fd 100644 --- a/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj +++ b/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj @@ -1,7 +1,7 @@ - netstandard2.0;netcoreapp3.1 + netstandard2.0;$(DefaultNetCoreTargetFramework) @@ -9,7 +9,7 @@ - + From 99bf7f0b56dbc30842a083b1d931f2fa9df234fe Mon Sep 17 00:00:00 2001 From: John Luo Date: Tue, 17 Sep 2019 14:35:45 -0700 Subject: [PATCH 141/183] Update ref assembly generation to use DefaultNetCoreTargetFramework property (dotnet/extensions#2362) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/d15c5687db29e4e1f31a302fe243226b0a3a17e3 --- .../Microsoft.Extensions.Configuration.KeyPerFile.csproj | 6 +++--- ...osoft.Extensions.Configuration.KeyPerFile.netcoreapp.cs} | 0 .../ref/Microsoft.Extensions.FileProviders.Embedded.csproj | 6 +++--- ...crosoft.Extensions.FileProviders.Embedded.netcoreapp.cs} | 0 ....Extensions.Diagnostics.HealthChecks.Abstractions.csproj | 6 +++--- ...ons.Diagnostics.HealthChecks.Abstractions.netcoreapp.cs} | 0 .../Microsoft.Extensions.Diagnostics.HealthChecks.csproj | 6 +++--- ...osoft.Extensions.Diagnostics.HealthChecks.netcoreapp.cs} | 0 .../Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj | 6 +++--- ...p.netcoreapp5.0.cs => Microsoft.JSInterop.netcoreapp.cs} | 0 .../Microsoft.Extensions.Localization.Abstractions.csproj | 6 +++--- ...soft.Extensions.Localization.Abstractions.netcoreapp.cs} | 0 .../ref/Microsoft.Extensions.Localization.csproj | 6 +++--- ...0.cs => Microsoft.Extensions.Localization.netcoreapp.cs} | 0 src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj | 6 +++--- ....0.cs => Microsoft.Extensions.WebEncoders.netcoreapp.cs} | 0 16 files changed, 24 insertions(+), 24 deletions(-) rename src/Configuration.KeyPerFile/ref/{Microsoft.Extensions.Configuration.KeyPerFile.netcoreapp5.0.cs => Microsoft.Extensions.Configuration.KeyPerFile.netcoreapp.cs} (100%) rename src/FileProviders/Embedded/ref/{Microsoft.Extensions.FileProviders.Embedded.netcoreapp5.0.cs => Microsoft.Extensions.FileProviders.Embedded.netcoreapp.cs} (100%) rename src/HealthChecks/Abstractions/ref/{Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netcoreapp5.0.cs => Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netcoreapp.cs} (100%) rename src/HealthChecks/HealthChecks/ref/{Microsoft.Extensions.Diagnostics.HealthChecks.netcoreapp5.0.cs => Microsoft.Extensions.Diagnostics.HealthChecks.netcoreapp.cs} (100%) rename src/JSInterop/Microsoft.JSInterop/ref/{Microsoft.JSInterop.netcoreapp5.0.cs => Microsoft.JSInterop.netcoreapp.cs} (100%) rename src/Localization/Abstractions/ref/{Microsoft.Extensions.Localization.Abstractions.netcoreapp5.0.cs => Microsoft.Extensions.Localization.Abstractions.netcoreapp.cs} (100%) rename src/Localization/Localization/ref/{Microsoft.Extensions.Localization.netcoreapp5.0.cs => Microsoft.Extensions.Localization.netcoreapp.cs} (100%) rename src/WebEncoders/ref/{Microsoft.Extensions.WebEncoders.netcoreapp5.0.cs => Microsoft.Extensions.WebEncoders.netcoreapp.cs} (100%) diff --git a/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj b/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj index 2ba44d34fa1b..8dbe6d3ba11c 100644 --- a/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj +++ b/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj @@ -1,15 +1,15 @@ - netstandard2.0;netcoreapp5.0 + netstandard2.0;$(DefaultNetCoreTargetFramework) - - + + diff --git a/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.netcoreapp5.0.cs b/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.netcoreapp.cs similarity index 100% rename from src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.netcoreapp5.0.cs rename to src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.netcoreapp.cs diff --git a/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.csproj b/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.csproj index 98d071201c4b..89cba5ba5723 100644 --- a/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.csproj +++ b/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.csproj @@ -1,14 +1,14 @@ - netstandard2.0;netcoreapp5.0 + netstandard2.0;$(DefaultNetCoreTargetFramework) - - + + diff --git a/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.netcoreapp5.0.cs b/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.netcoreapp.cs similarity index 100% rename from src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.netcoreapp5.0.cs rename to src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.netcoreapp.cs diff --git a/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj index d202fe2d713b..6b67a0868621 100644 --- a/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj +++ b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj @@ -1,14 +1,14 @@ - netstandard2.0;netcoreapp5.0 + netstandard2.0;$(DefaultNetCoreTargetFramework) - - + + diff --git a/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netcoreapp5.0.cs b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netcoreapp.cs similarity index 100% rename from src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netcoreapp5.0.cs rename to src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netcoreapp.cs diff --git a/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj b/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj index 07f63049eb7a..83dae521fbbf 100644 --- a/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj +++ b/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj @@ -1,7 +1,7 @@ - netstandard2.0;netcoreapp5.0 + netstandard2.0;$(DefaultNetCoreTargetFramework) @@ -9,8 +9,8 @@ - - + + diff --git a/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netcoreapp5.0.cs b/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netcoreapp.cs similarity index 100% rename from src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netcoreapp5.0.cs rename to src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netcoreapp.cs diff --git a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj index ae19552c6d53..ceb24636ab4d 100644 --- a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj +++ b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj @@ -1,14 +1,14 @@ - netstandard2.0;netcoreapp5.0 + netstandard2.0;$(DefaultNetCoreTargetFramework) - - + + diff --git a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netcoreapp5.0.cs b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netcoreapp.cs similarity index 100% rename from src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netcoreapp5.0.cs rename to src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netcoreapp.cs diff --git a/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.csproj b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.csproj index 66e71c05545e..af97cae5940d 100644 --- a/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.csproj +++ b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.csproj @@ -1,14 +1,14 @@ - netstandard2.0;netcoreapp5.0 + netstandard2.0;$(DefaultNetCoreTargetFramework) - - + + diff --git a/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netcoreapp5.0.cs b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netcoreapp.cs similarity index 100% rename from src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netcoreapp5.0.cs rename to src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netcoreapp.cs diff --git a/src/Localization/Localization/ref/Microsoft.Extensions.Localization.csproj b/src/Localization/Localization/ref/Microsoft.Extensions.Localization.csproj index 2a84077b0671..67e8f38f8949 100644 --- a/src/Localization/Localization/ref/Microsoft.Extensions.Localization.csproj +++ b/src/Localization/Localization/ref/Microsoft.Extensions.Localization.csproj @@ -1,7 +1,7 @@ - netstandard2.0;netcoreapp5.0 + netstandard2.0;$(DefaultNetCoreTargetFramework) @@ -10,8 +10,8 @@ - - + + diff --git a/src/Localization/Localization/ref/Microsoft.Extensions.Localization.netcoreapp5.0.cs b/src/Localization/Localization/ref/Microsoft.Extensions.Localization.netcoreapp.cs similarity index 100% rename from src/Localization/Localization/ref/Microsoft.Extensions.Localization.netcoreapp5.0.cs rename to src/Localization/Localization/ref/Microsoft.Extensions.Localization.netcoreapp.cs diff --git a/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj b/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj index 3fa06a72801d..5beee97dd6fd 100644 --- a/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj +++ b/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj @@ -1,7 +1,7 @@ - netstandard2.0;netcoreapp5.0 + netstandard2.0;$(DefaultNetCoreTargetFramework) @@ -9,8 +9,8 @@ - - + + diff --git a/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.netcoreapp5.0.cs b/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.netcoreapp.cs similarity index 100% rename from src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.netcoreapp5.0.cs rename to src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.netcoreapp.cs From 3ef03bf8dceb917248b59f5fb95ab9c2b70324b3 Mon Sep 17 00:00:00 2001 From: John Luo Date: Tue, 17 Sep 2019 20:52:34 -0700 Subject: [PATCH 142/183] Fix tests and tools \n\nCommit migrated from https://github.com/dotnet/extensions/commit/4bc42dae9c186b53f23c5291ca2ba5f4349a73a0 --- ...Microsoft.Extensions.Localization.RootNamespace.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Localization/Localization/test/Microsoft.Extensions.Localization.RootNamespace.Tests/Microsoft.Extensions.Localization.RootNamespace.Tests.csproj b/src/Localization/Localization/test/Microsoft.Extensions.Localization.RootNamespace.Tests/Microsoft.Extensions.Localization.RootNamespace.Tests.csproj index d7a45a84ab2a..3d9153ef330f 100644 --- a/src/Localization/Localization/test/Microsoft.Extensions.Localization.RootNamespace.Tests/Microsoft.Extensions.Localization.RootNamespace.Tests.csproj +++ b/src/Localization/Localization/test/Microsoft.Extensions.Localization.RootNamespace.Tests/Microsoft.Extensions.Localization.RootNamespace.Tests.csproj @@ -1,6 +1,6 @@ - netcoreapp3.0;net472 + $(DefaultNetCoreTargetFramework);net472 LocalizationTest.Abc From ef2dc5024feb965dbd1205c0904ca2114872420c Mon Sep 17 00:00:00 2001 From: Pranav K Date: Thu, 19 Sep 2019 10:48:09 -0700 Subject: [PATCH 143/183] Add support for JSInvokable methods on generic types (dotnet/extensions#2342) * Add support for JSInvokable methods on generic types Prior to this change, DotNetDispatcher cached the MethodInfo on the generic type definition. Using this would have required MethodInfo.MakeGenericMethod before the method was invoked. We could separately cache the result of this to avoid the reflection cost per invocation. Alternatively we could cache static and non-static MethodInfo instances separately which is what this change attempts to do. The big difference in the outcome is that this requires instance (non-static) JSInvokable methods to be only unique named within the type hierarchy as opposed to across all static and instance JSInvokable methods in an assembly. Fixes https://github.com/aspnet/Extensions/issues/1360 Fixes https://github.com/aspnet/AspNetCore/issues/9061 \n\nCommit migrated from https://github.com/dotnet/extensions/commit/659b604fb2e595d48a931a7ed831f6c38f035382 --- .../src/Infrastructure/DotNetDispatcher.cs | 83 ++++++++++++------ .../Infrastructure/DotNetDispatcherTest.cs | 87 ++++++++++++++++++- 2 files changed, 142 insertions(+), 28 deletions(-) diff --git a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs index 6a3a4f8d5fec..8d37905980e8 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs @@ -24,6 +24,9 @@ public static class DotNetDispatcher private static readonly ConcurrentDictionary> _cachedMethodsByAssembly = new ConcurrentDictionary>(); + private static readonly ConcurrentDictionary> _cachedMethodsByType + = new ConcurrentDictionary>(); + /// /// Receives a call from JS to .NET, locating and invoking the specified method. /// @@ -129,9 +132,12 @@ private static object InvokeSynchronously(JSRuntime jsRuntime, in DotNetInvocati var methodIdentifier = callInfo.MethodIdentifier; AssemblyKey assemblyKey; + MethodInfo methodInfo; + Type[] parameterTypes; if (objectReference is null) { assemblyKey = new AssemblyKey(assemblyName); + (methodInfo, parameterTypes) = GetCachedMethodInfo(assemblyKey, methodIdentifier); } else { @@ -147,11 +153,9 @@ private static object InvokeSynchronously(JSRuntime jsRuntime, in DotNetInvocati return default; } - assemblyKey = new AssemblyKey(objectReference.Value.GetType().Assembly); + (methodInfo, parameterTypes) = GetCachedMethodInfo(objectReference, methodIdentifier); } - var (methodInfo, parameterTypes) = GetCachedMethodInfo(assemblyKey, methodIdentifier); - var suppliedArgs = ParseArguments(jsRuntime, methodIdentifier, argsJson, parameterTypes); try @@ -301,7 +305,47 @@ private static (MethodInfo, Type[]) GetCachedMethodInfo(AssemblyKey assemblyKey, } else { - throw new ArgumentException($"The assembly '{assemblyKey.AssemblyName}' does not contain a public method with [{nameof(JSInvokableAttribute)}(\"{methodIdentifier}\")]."); + throw new ArgumentException($"The assembly '{assemblyKey.AssemblyName}' does not contain a public invokable method with [{nameof(JSInvokableAttribute)}(\"{methodIdentifier}\")]."); + } + } + + private static (MethodInfo methodInfo, Type[] parameterTypes) GetCachedMethodInfo(IDotNetObjectReference objectReference, string methodIdentifier) + { + var type = objectReference.Value.GetType(); + var assemblyMethods = _cachedMethodsByType.GetOrAdd(type, ScanTypeForCallableMethods); + if (assemblyMethods.TryGetValue(methodIdentifier, out var result)) + { + return result; + } + else + { + throw new ArgumentException($"The type '{type.Name}' does not contain a public invokable method with [{nameof(JSInvokableAttribute)}(\"{methodIdentifier}\")]."); + } + + static Dictionary ScanTypeForCallableMethods(Type type) + { + var result = new Dictionary(StringComparer.Ordinal); + var invokableMethods = type + .GetMethods(BindingFlags.Public | BindingFlags.Instance) + .Where(method => !method.ContainsGenericParameters && method.IsDefined(typeof(JSInvokableAttribute), inherit: false)); + + foreach (var method in invokableMethods) + { + var identifier = method.GetCustomAttribute(false).Identifier ?? method.Name; + var parameterTypes = method.GetParameters().Select(p => p.ParameterType).ToArray(); + + if (result.ContainsKey(identifier)) + { + throw new InvalidOperationException($"The type {type.Name} contains more than one " + + $"[JSInvokable] method with identifier '{identifier}'. All [JSInvokable] methods within the same " + + $"type must have different identifiers. You can pass a custom identifier as a parameter to " + + $"the [JSInvokable] attribute."); + } + + result.Add(identifier, (method, parameterTypes)); + } + + return result; } } @@ -312,35 +356,22 @@ private static (MethodInfo, Type[]) GetCachedMethodInfo(AssemblyKey assemblyKey, var result = new Dictionary(StringComparer.Ordinal); var invokableMethods = GetRequiredLoadedAssembly(assemblyKey) .GetExportedTypes() - .SelectMany(type => type.GetMethods( - BindingFlags.Public | - BindingFlags.DeclaredOnly | - BindingFlags.Instance | - BindingFlags.Static)) - .Where(method => method.IsDefined(typeof(JSInvokableAttribute), inherit: false)); + .SelectMany(type => type.GetMethods(BindingFlags.Public | BindingFlags.Static)) + .Where(method => !method.ContainsGenericParameters && method.IsDefined(typeof(JSInvokableAttribute), inherit: false)); foreach (var method in invokableMethods) { var identifier = method.GetCustomAttribute(false).Identifier ?? method.Name; var parameterTypes = method.GetParameters().Select(p => p.ParameterType).ToArray(); - try + if (result.ContainsKey(identifier)) { - result.Add(identifier, (method, parameterTypes)); - } - catch (ArgumentException) - { - if (result.ContainsKey(identifier)) - { - throw new InvalidOperationException($"The assembly '{assemblyKey.AssemblyName}' contains more than one " + - $"[JSInvokable] method with identifier '{identifier}'. All [JSInvokable] methods within the same " + - $"assembly must have different identifiers. You can pass a custom identifier as a parameter to " + - $"the [JSInvokable] attribute."); - } - else - { - throw; - } + throw new InvalidOperationException($"The assembly '{assemblyKey.AssemblyName}' contains more than one " + + $"[JSInvokable] method with identifier '{identifier}'. All [JSInvokable] methods within the same " + + $"assembly must have different identifiers. You can pass a custom identifier as a parameter to " + + $"the [JSInvokable] attribute."); } + + result.Add(identifier, (method, parameterTypes)); } return result; diff --git a/src/JSInterop/Microsoft.JSInterop/test/Infrastructure/DotNetDispatcherTest.cs b/src/JSInterop/Microsoft.JSInterop/test/Infrastructure/DotNetDispatcherTest.cs index 7e82a47a89eb..c85565d5f27e 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/Infrastructure/DotNetDispatcherTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/Infrastructure/DotNetDispatcherTest.cs @@ -3,7 +3,6 @@ using System; using System.Linq; -using System.Runtime.ExceptionServices; using System.Text.Json; using System.Threading; using System.Threading.Tasks; @@ -70,7 +69,7 @@ public void CannotInvokeUnsuitableMethods(string methodIdentifier) DotNetDispatcher.Invoke(new TestJSRuntime(), new DotNetInvocationInfo(thisAssemblyName, methodIdentifier, default, default), null); }); - Assert.Equal($"The assembly '{thisAssemblyName}' does not contain a public method with [JSInvokableAttribute(\"{methodIdentifier}\")].", ex.Message); + Assert.Equal($"The assembly '{thisAssemblyName}' does not contain a public invokable method with [JSInvokableAttribute(\"{methodIdentifier}\")].", ex.Message); } [Fact] @@ -355,6 +354,78 @@ public void CanInvokeInstanceMethodWithParams() Assert.Equal("MY STRING", resultDto.StringVal); } + [Fact] + public void CanInvokeNonGenericInstanceMethodOnGenericType() + { + var jsRuntime = new TestJSRuntime(); + var targetInstance = new GenericType(); + jsRuntime.Invoke("_setup", + DotNetObjectReference.Create(targetInstance)); + var argsJson = "[\"hello world\"]"; + + // Act + var resultJson = DotNetDispatcher.Invoke(jsRuntime, new DotNetInvocationInfo(null, nameof(GenericType.EchoStringParameter), 1, default), argsJson); + + // Assert + Assert.Equal("\"hello world\"", resultJson); + } + + [Fact] + public void CanInvokeMethodsThatAcceptGenericParametersOnGenericTypes() + { + var jsRuntime = new TestJSRuntime(); + var targetInstance = new GenericType(); + jsRuntime.Invoke("_setup", + DotNetObjectReference.Create(targetInstance)); + var argsJson = "[\"hello world\"]"; + + // Act + var resultJson = DotNetDispatcher.Invoke(jsRuntime, new DotNetInvocationInfo(null, nameof(GenericType.EchoParameter), 1, default), argsJson); + + // Assert + Assert.Equal("\"hello world\"", resultJson); + } + + [Fact] + public void CannotInvokeStaticOpenGenericMethods() + { + var methodIdentifier = "StaticGenericMethod"; + var jsRuntime = new TestJSRuntime(); + + // Act + var ex = Assert.Throws(() => DotNetDispatcher.Invoke(jsRuntime, new DotNetInvocationInfo(thisAssemblyName, methodIdentifier, 0, default), "[7]")); + Assert.Contains($"The assembly '{thisAssemblyName}' does not contain a public invokable method with [{nameof(JSInvokableAttribute)}(\"{methodIdentifier}\")].", ex.Message); + } + + [Fact] + public void CannotInvokeInstanceOpenGenericMethods() + { + var methodIdentifier = "InstanceGenericMethod"; + var targetInstance = new GenericType(); + var jsRuntime = new TestJSRuntime(); + jsRuntime.Invoke("_setup", + DotNetObjectReference.Create(targetInstance)); + var argsJson = "[\"hello world\"]"; + + // Act + var ex = Assert.Throws(() => DotNetDispatcher.Invoke(jsRuntime, new DotNetInvocationInfo(null, methodIdentifier, 1, default), argsJson)); + Assert.Contains($"The type 'GenericType`1' does not contain a public invokable method with [{nameof(JSInvokableAttribute)}(\"{methodIdentifier}\")].", ex.Message); + } + + [Fact] + public void CannotInvokeMethodsWithGenericParameters_IfTypesDoNotMatch() + { + var jsRuntime = new TestJSRuntime(); + var targetInstance = new GenericType(); + jsRuntime.Invoke("_setup", + DotNetObjectReference.Create(targetInstance)); + var argsJson = "[\"hello world\"]"; + + // Act & Assert + Assert.Throws(() => + DotNetDispatcher.Invoke(jsRuntime, new DotNetInvocationInfo(null, nameof(GenericType.EchoParameter), 1, default), argsJson)); + } + [Fact] public void CannotInvokeWithFewerNumberOfParameters() { @@ -790,6 +861,18 @@ public static async Task AsyncThrowingMethod() } } + public class GenericType + { + [JSInvokable] public string EchoStringParameter(string input) => input; + [JSInvokable] public TValue EchoParameter(TValue input) => input; + } + + public class GenericMethodClass + { + [JSInvokable("StaticGenericMethod")] public static string StaticGenericMethod(TValue input) => input.ToString(); + [JSInvokable("InstanceGenericMethod")] public string GenericMethod(TValue input) => input.ToString(); + } + public class TestJSRuntime : JSInProcessRuntime { private TaskCompletionSource _nextInvocationTcs = new TaskCompletionSource(); From ed97d344c534069ce978497243708b0419a89192 Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Fri, 6 Sep 2019 08:02:44 -0700 Subject: [PATCH 144/183] Rename file to make class \n\nCommit migrated from https://github.com/dotnet/extensions/commit/b6a290771fb6dd0a28e1a1cd91b8b0287dd812a8 --- src/Testing/src/xunit/FlakyAttribute.cs | 2 +- .../xunit/{FlakyTestDiscoverer.cs => FlakyTraitDiscoverer.cs} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/Testing/src/xunit/{FlakyTestDiscoverer.cs => FlakyTraitDiscoverer.cs} (95%) diff --git a/src/Testing/src/xunit/FlakyAttribute.cs b/src/Testing/src/xunit/FlakyAttribute.cs index 411435b74a01..daceb964fdf5 100644 --- a/src/Testing/src/xunit/FlakyAttribute.cs +++ b/src/Testing/src/xunit/FlakyAttribute.cs @@ -49,7 +49,7 @@ namespace Microsoft.AspNetCore.Testing /// to xunit.console.exe. Similarly, it can run only flaky tests using -trait "Flaky:AzP:OS:all=true" -trait "Flaky:AzP:OS:Darwin=true" /// /// - [TraitDiscoverer("Microsoft.AspNetCore.Testing." + nameof(FlakyTestDiscoverer), "Microsoft.AspNetCore.Testing")] + [TraitDiscoverer("Microsoft.AspNetCore.Testing." + nameof(FlakyTraitDiscoverer), "Microsoft.AspNetCore.Testing")] [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly)] public sealed class FlakyAttribute : Attribute, ITraitAttribute { diff --git a/src/Testing/src/xunit/FlakyTestDiscoverer.cs b/src/Testing/src/xunit/FlakyTraitDiscoverer.cs similarity index 95% rename from src/Testing/src/xunit/FlakyTestDiscoverer.cs rename to src/Testing/src/xunit/FlakyTraitDiscoverer.cs index 0c481e1c4198..4e6bc27b1bbe 100644 --- a/src/Testing/src/xunit/FlakyTestDiscoverer.cs +++ b/src/Testing/src/xunit/FlakyTraitDiscoverer.cs @@ -6,7 +6,7 @@ // Do not change this namespace without changing the usage in FlakyAttribute namespace Microsoft.AspNetCore.Testing { - public class FlakyTestDiscoverer : ITraitDiscoverer + public class FlakyTraitDiscoverer : ITraitDiscoverer { public IEnumerable> GetTraits(IAttributeInfo traitAttribute) { From b1987c75cbceb42c2da539d4df956a5204af07fd Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Wed, 11 Sep 2019 16:27:57 -0700 Subject: [PATCH 145/183] Add a workaround for xUnit bug We're currently experiencing a bug where conditional skips aren't working in VS. This is caused by https://github.com/xunit/xunit/issues/1782 \n\nCommit migrated from https://github.com/dotnet/extensions/commit/cbe90b849230d9b24f3152a44fa8d714c332b1e4 --- .../src/xunit/ConditionalTheoryDiscoverer.cs | 20 +++++ src/Testing/src/xunit/SkippedTestCase.cs | 7 +- .../WORKAROUND_SkippedDataRowTestCase.cs | 80 +++++++++++++++++++ 3 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 src/Testing/src/xunit/WORKAROUND_SkippedDataRowTestCase.cs diff --git a/src/Testing/src/xunit/ConditionalTheoryDiscoverer.cs b/src/Testing/src/xunit/ConditionalTheoryDiscoverer.cs index 764b613b16af..e7f655a5bec0 100644 --- a/src/Testing/src/xunit/ConditionalTheoryDiscoverer.cs +++ b/src/Testing/src/xunit/ConditionalTheoryDiscoverer.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Collections.Generic; using Xunit.Abstractions; using Xunit.Sdk; @@ -63,5 +64,24 @@ protected override IEnumerable CreateTestCasesForDataRow(ITestFr base.CreateTestCasesForSkippedDataRow(discoveryOptions, testMethod, theoryAttribute, dataRow, skipReason) : base.CreateTestCasesForDataRow(discoveryOptions, testMethod, theoryAttribute, dataRow); } + + protected override IEnumerable CreateTestCasesForSkippedDataRow( + ITestFrameworkDiscoveryOptions discoveryOptions, + ITestMethod testMethod, + IAttributeInfo theoryAttribute, + object[] dataRow, + string skipReason) + { + return new[] + { + new WORKAROUND_SkippedDataRowTestCase(DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), discoveryOptions.MethodDisplayOptionsOrDefault(), testMethod, skipReason, dataRow), + }; + } + + [Obsolete] + protected override IXunitTestCase CreateTestCaseForSkippedDataRow(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo theoryAttribute, object[] dataRow, string skipReason) + { + return new WORKAROUND_SkippedDataRowTestCase(DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), discoveryOptions.MethodDisplayOptionsOrDefault(), testMethod, skipReason, dataRow); + } } } diff --git a/src/Testing/src/xunit/SkippedTestCase.cs b/src/Testing/src/xunit/SkippedTestCase.cs index b514c572097d..0fdf166f2b5a 100644 --- a/src/Testing/src/xunit/SkippedTestCase.cs +++ b/src/Testing/src/xunit/SkippedTestCase.cs @@ -2,6 +2,8 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Threading; +using System.Threading.Tasks; using Xunit.Abstractions; using Xunit.Sdk; @@ -33,8 +35,11 @@ protected override string GetSkipReason(IAttributeInfo factAttribute) public override void Deserialize(IXunitSerializationInfo data) { - base.Deserialize(data); _skipReason = data.GetValue(nameof(_skipReason)); + + // We need to call base after reading our value, because Deserialize will call + // into GetSkipReason. + base.Deserialize(data); } public override void Serialize(IXunitSerializationInfo data) diff --git a/src/Testing/src/xunit/WORKAROUND_SkippedDataRowTestCase.cs b/src/Testing/src/xunit/WORKAROUND_SkippedDataRowTestCase.cs new file mode 100644 index 000000000000..a86f5645bf11 --- /dev/null +++ b/src/Testing/src/xunit/WORKAROUND_SkippedDataRowTestCase.cs @@ -0,0 +1,80 @@ +using System; +using System.ComponentModel; +using Xunit.Abstractions; +using Xunit.Sdk; + +namespace Microsoft.AspNetCore.Testing +{ + // This is a workaround for https://github.com/xunit/xunit/issues/1782 - as such, this code is a copy-paste + // from xUnit with the exception of fixing the bug. + // + // This will only work with [ConditionalTheory]. + internal class WORKAROUND_SkippedDataRowTestCase : XunitTestCase + { + string skipReason; + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + [Obsolete("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")] + public WORKAROUND_SkippedDataRowTestCase() { } + + /// + /// Initializes a new instance of the class. + /// + /// The message sink used to send diagnostic messages + /// Default method display to use (when not customized). + /// The test method this test case belongs to. + /// The reason that this test case will be skipped + /// The arguments for the test method. + [Obsolete("Please call the constructor which takes TestMethodDisplayOptions")] + public WORKAROUND_SkippedDataRowTestCase(IMessageSink diagnosticMessageSink, + TestMethodDisplay defaultMethodDisplay, + ITestMethod testMethod, + string skipReason, + object[] testMethodArguments = null) + : this(diagnosticMessageSink, defaultMethodDisplay, TestMethodDisplayOptions.None, testMethod, skipReason, testMethodArguments) { } + + /// + /// Initializes a new instance of the class. + /// + /// The message sink used to send diagnostic messages + /// Default method display to use (when not customized). + /// Default method display options to use (when not customized). + /// The test method this test case belongs to. + /// The reason that this test case will be skipped + /// The arguments for the test method. + public WORKAROUND_SkippedDataRowTestCase(IMessageSink diagnosticMessageSink, + TestMethodDisplay defaultMethodDisplay, + TestMethodDisplayOptions defaultMethodDisplayOptions, + ITestMethod testMethod, + string skipReason, + object[] testMethodArguments = null) + : base(diagnosticMessageSink, defaultMethodDisplay, defaultMethodDisplayOptions, testMethod, testMethodArguments) + { + this.skipReason = skipReason; + } + + /// + public override void Deserialize(IXunitSerializationInfo data) + { + // SkipReason has to be read before we call base.Deserialize, this is the workaround. + this.skipReason = data.GetValue("SkipReason"); + + base.Deserialize(data); + } + + /// + protected override string GetSkipReason(IAttributeInfo factAttribute) + { + return skipReason; + } + + /// + public override void Serialize(IXunitSerializationInfo data) + { + base.Serialize(data); + + data.AddValue("SkipReason", skipReason); + } + } +} From aadc979baf38b942fdf3f424e2b9a1ead5b8dffd Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Wed, 11 Sep 2019 16:31:38 -0700 Subject: [PATCH 146/183] Add AssemblyFixture to our test infra This is a feature that we're using in Templates and Blazor E2E tests to manage selenium. It's a general purpose kind of thing, so it makes sense to make it more general. This requires using the `[assembly: TestFramework()]`. Also fixed a bug here where this feature broke collection fixtures. \n\nCommit migrated from https://github.com/dotnet/extensions/commit/208d44a985fa4d8e13eb00515a44c5104c61b2b0 --- .../src/xunit/AspNetTestAssemblyRunner.cs | 83 +++++++++++++++++++ .../src/xunit/AspNetTestCollectionRunner.cs | 67 +++++++++++++++ src/Testing/src/xunit/AspNetTestFramework.cs | 20 +++++ .../src/xunit/AspNetTestFrameworkExecutor.cs | 26 ++++++ .../src/xunit/AssemblyFixtureAttribute.cs | 18 ++++ src/Testing/test/AssemblyFixtureTest.cs | 47 +++++++++++ src/Testing/test/Properties/AssemblyInfo.cs | 5 ++ src/Testing/test/TestAssemblyFixture.cs | 10 +++ src/Testing/test/TestCollectionFixture.cs | 10 +++ 9 files changed, 286 insertions(+) create mode 100644 src/Testing/src/xunit/AspNetTestAssemblyRunner.cs create mode 100644 src/Testing/src/xunit/AspNetTestCollectionRunner.cs create mode 100644 src/Testing/src/xunit/AspNetTestFramework.cs create mode 100644 src/Testing/src/xunit/AspNetTestFrameworkExecutor.cs create mode 100644 src/Testing/src/xunit/AssemblyFixtureAttribute.cs create mode 100644 src/Testing/test/AssemblyFixtureTest.cs create mode 100644 src/Testing/test/Properties/AssemblyInfo.cs create mode 100644 src/Testing/test/TestAssemblyFixture.cs create mode 100644 src/Testing/test/TestCollectionFixture.cs diff --git a/src/Testing/src/xunit/AspNetTestAssemblyRunner.cs b/src/Testing/src/xunit/AspNetTestAssemblyRunner.cs new file mode 100644 index 000000000000..48fbbbfa3bed --- /dev/null +++ b/src/Testing/src/xunit/AspNetTestAssemblyRunner.cs @@ -0,0 +1,83 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Xunit.Abstractions; +using Xunit.Sdk; + +namespace Microsoft.AspNetCore.Testing +{ + public class AspNetTestAssemblyRunner : XunitTestAssemblyRunner + { + private readonly Dictionary _assemblyFixtureMappings = new Dictionary(); + + public AspNetTestAssemblyRunner( + ITestAssembly testAssembly, + IEnumerable testCases, + IMessageSink diagnosticMessageSink, + IMessageSink executionMessageSink, + ITestFrameworkExecutionOptions executionOptions) + : base(testAssembly, testCases, diagnosticMessageSink, executionMessageSink, executionOptions) + { + } + + protected override async Task AfterTestAssemblyStartingAsync() + { + await base.AfterTestAssemblyStartingAsync(); + + // Find all the AssemblyFixtureAttributes on the test assembly + Aggregator.Run(() => + { + var fixturesAttributes = ((IReflectionAssemblyInfo)TestAssembly.Assembly) + .Assembly + .GetCustomAttributes(typeof(AssemblyFixtureAttribute), false) + .Cast() + .ToList(); + + // Instantiate all the fixtures + foreach (var fixtureAttribute in fixturesAttributes) + { + var ctorWithDiagnostics = fixtureAttribute.FixtureType.GetConstructor(new[] { typeof(IMessageSink) }); + if (ctorWithDiagnostics != null) + { + _assemblyFixtureMappings[fixtureAttribute.FixtureType] = Activator.CreateInstance(fixtureAttribute.FixtureType, DiagnosticMessageSink); + } + else + { + _assemblyFixtureMappings[fixtureAttribute.FixtureType] = Activator.CreateInstance(fixtureAttribute.FixtureType); + } + } + }); + } + + protected override Task BeforeTestAssemblyFinishedAsync() + { + // Dispose fixtures + foreach (var disposable in _assemblyFixtureMappings.Values.OfType()) + { + Aggregator.Run(disposable.Dispose); + } + + return base.BeforeTestAssemblyFinishedAsync(); + } + + protected override Task RunTestCollectionAsync( + IMessageBus messageBus, + ITestCollection testCollection, + IEnumerable testCases, + CancellationTokenSource cancellationTokenSource) + => new AspNetTestCollectionRunner( + _assemblyFixtureMappings, + testCollection, + testCases, + DiagnosticMessageSink, + messageBus, + TestCaseOrderer, + new ExceptionAggregator(Aggregator), + cancellationTokenSource).RunAsync(); + } +} diff --git a/src/Testing/src/xunit/AspNetTestCollectionRunner.cs b/src/Testing/src/xunit/AspNetTestCollectionRunner.cs new file mode 100644 index 000000000000..264cf769c63a --- /dev/null +++ b/src/Testing/src/xunit/AspNetTestCollectionRunner.cs @@ -0,0 +1,67 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Xunit.Abstractions; +using Xunit.Sdk; + +namespace Microsoft.AspNetCore.Testing +{ + public class AspNetTestCollectionRunner : XunitTestCollectionRunner + { + private readonly IDictionary _assemblyFixtureMappings; + private readonly IMessageSink _diagnosticMessageSink; + + public AspNetTestCollectionRunner( + Dictionary assemblyFixtureMappings, + ITestCollection testCollection, + IEnumerable testCases, + IMessageSink diagnosticMessageSink, + IMessageBus messageBus, + ITestCaseOrderer testCaseOrderer, + ExceptionAggregator aggregator, + CancellationTokenSource cancellationTokenSource) + : base(testCollection, testCases, diagnosticMessageSink, messageBus, testCaseOrderer, aggregator, cancellationTokenSource) + { + _assemblyFixtureMappings = assemblyFixtureMappings; + _diagnosticMessageSink = diagnosticMessageSink; + } + + protected override async Task AfterTestCollectionStartingAsync() + { + await base.AfterTestCollectionStartingAsync(); + + // note: We pass the assembly fixtures into the runner as ICollectionFixture<> - this seems to work OK without any + // drawbacks. It's reasonable that we could add IAssemblyFixture<> and related plumbing if it ever became required. + // + // The reason for assembly fixture is when we want to start/stop something as the project scope - tests can only be + // in one test collection at a time. + foreach (var mapping in _assemblyFixtureMappings) + { + CollectionFixtureMappings.Add(mapping.Key, mapping.Value); + } + } + + protected override Task BeforeTestCollectionFinishedAsync() + { + // We need to remove the assembly fixtures so they won't get disposed. + foreach (var mapping in _assemblyFixtureMappings) + { + CollectionFixtureMappings.Remove(mapping.Key); + } + + return base.BeforeTestCollectionFinishedAsync(); + } + + protected override Task RunTestClassAsync(ITestClass testClass, IReflectionTypeInfo @class, IEnumerable testCases) + { + var caste = testCases.ToArray(); + var type = caste.First().GetType(); + return base.RunTestClassAsync(testClass, @class, testCases); + } + } +} diff --git a/src/Testing/src/xunit/AspNetTestFramework.cs b/src/Testing/src/xunit/AspNetTestFramework.cs new file mode 100644 index 000000000000..0a2dc1b21fa3 --- /dev/null +++ b/src/Testing/src/xunit/AspNetTestFramework.cs @@ -0,0 +1,20 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Reflection; +using Xunit.Abstractions; +using Xunit.Sdk; + +namespace Microsoft.AspNetCore.Testing +{ + public class AspNetTestFramework : XunitTestFramework + { + public AspNetTestFramework(IMessageSink messageSink) + : base(messageSink) + { + } + + protected override ITestFrameworkExecutor CreateExecutor(AssemblyName assemblyName) + => new AspNetTestFrameworkExecutor(assemblyName, SourceInformationProvider, DiagnosticMessageSink); + } +} diff --git a/src/Testing/src/xunit/AspNetTestFrameworkExecutor.cs b/src/Testing/src/xunit/AspNetTestFrameworkExecutor.cs new file mode 100644 index 000000000000..b34f0b715e40 --- /dev/null +++ b/src/Testing/src/xunit/AspNetTestFrameworkExecutor.cs @@ -0,0 +1,26 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Reflection; +using Xunit.Abstractions; +using Xunit.Sdk; + +namespace Microsoft.AspNetCore.Testing +{ + public class AspNetTestFrameworkExecutor : XunitTestFrameworkExecutor + { + public AspNetTestFrameworkExecutor(AssemblyName assemblyName, ISourceInformationProvider sourceInformationProvider, IMessageSink diagnosticMessageSink) + : base(assemblyName, sourceInformationProvider, diagnosticMessageSink) + { + } + + protected override async void RunTestCases(IEnumerable testCases, IMessageSink executionMessageSink, ITestFrameworkExecutionOptions executionOptions) + { + using (var assemblyRunner = new AspNetTestAssemblyRunner(TestAssembly, testCases, DiagnosticMessageSink, executionMessageSink, executionOptions)) + { + await assemblyRunner.RunAsync(); + } + } + } +} diff --git a/src/Testing/src/xunit/AssemblyFixtureAttribute.cs b/src/Testing/src/xunit/AssemblyFixtureAttribute.cs new file mode 100644 index 000000000000..c3b9eba31d55 --- /dev/null +++ b/src/Testing/src/xunit/AssemblyFixtureAttribute.cs @@ -0,0 +1,18 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.AspNetCore.Testing +{ + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public class AssemblyFixtureAttribute : Attribute + { + public AssemblyFixtureAttribute(Type fixtureType) + { + FixtureType = fixtureType; + } + + public Type FixtureType { get; private set; } + } +} diff --git a/src/Testing/test/AssemblyFixtureTest.cs b/src/Testing/test/AssemblyFixtureTest.cs new file mode 100644 index 000000000000..a5fa73019a08 --- /dev/null +++ b/src/Testing/test/AssemblyFixtureTest.cs @@ -0,0 +1,47 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Xunit; + +namespace Microsoft.AspNetCore.Testing +{ + // We include a collection and assembly fixture to verify that they both still work. + [Collection("MyCollection")] + [TestCaseOrderer("Microsoft.AspNetCore.Testing.AlphabeticalOrderer", "Microsoft.AspNetCore.Testing.Tests")] + public class AssemblyFixtureTest + { + public AssemblyFixtureTest(TestAssemblyFixture assemblyFixture, TestCollectionFixture collectionFixture) + { + AssemblyFixture = assemblyFixture; + CollectionFixture = collectionFixture; + } + + public TestAssemblyFixture AssemblyFixture { get; } + public TestCollectionFixture CollectionFixture { get; } + + [Fact] + public void A() + { + Assert.NotNull(AssemblyFixture); + Assert.Equal(0, AssemblyFixture.Count); + + Assert.NotNull(CollectionFixture); + Assert.Equal(0, CollectionFixture.Count); + + AssemblyFixture.Count++; + CollectionFixture.Count++; + } + + [Fact] + public void B() + { + Assert.Equal(1, AssemblyFixture.Count); + Assert.Equal(1, CollectionFixture.Count); + } + } + + [CollectionDefinition("MyCollection", DisableParallelization = true)] + public class MyCollection : ICollectionFixture + { + } +} diff --git a/src/Testing/test/Properties/AssemblyInfo.cs b/src/Testing/test/Properties/AssemblyInfo.cs new file mode 100644 index 000000000000..4dba8d157ef7 --- /dev/null +++ b/src/Testing/test/Properties/AssemblyInfo.cs @@ -0,0 +1,5 @@ +using Microsoft.AspNetCore.Testing; +using Xunit; + +[assembly: AssemblyFixture(typeof(TestAssemblyFixture))] +[assembly: TestFramework("Microsoft.AspNetCore.Testing.AspNetTestFramework", "Microsoft.AspNetCore.Testing")] diff --git a/src/Testing/test/TestAssemblyFixture.cs b/src/Testing/test/TestAssemblyFixture.cs new file mode 100644 index 000000000000..44308160bd44 --- /dev/null +++ b/src/Testing/test/TestAssemblyFixture.cs @@ -0,0 +1,10 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNetCore.Testing +{ + public class TestAssemblyFixture + { + public int Count { get; set; } + } +} diff --git a/src/Testing/test/TestCollectionFixture.cs b/src/Testing/test/TestCollectionFixture.cs new file mode 100644 index 000000000000..b9aed01e4138 --- /dev/null +++ b/src/Testing/test/TestCollectionFixture.cs @@ -0,0 +1,10 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNetCore.Testing +{ + public class TestCollectionFixture + { + public int Count { get; set; } + } +} From ddde4faf4f43a2070353fee4d6f3d0c09aaa586d Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Mon, 16 Sep 2019 13:33:09 -0700 Subject: [PATCH 147/183] Refactor xUnit extensibility Adds our own hook for before/after logic that's more usable, called `ITestMethodLifecycle`. This provides access to a context object including the information about the test and the output helper. This can be implemented by attributes or by the class itself. The goal (and result) of this, is that we have a single *test executor* extensibility point that provides all of the features we need. We should use this everywhere we need features xUnit doesn't have. Adding a new extensibility point (`ITestMethodLifecycle`) allows us to do this without turning all of these features into a giant monolith. --- Also updated our existing extensibility to use this new hook. I did as much cleanup as a could to remove duplication from logging and keep it loosly coupled. I didn't want to tease this apart completely because the scope of this PR is already pretty large. \n\nCommit migrated from https://github.com/dotnet/extensions/commit/1b10284a47a4f58d69f3ee1feb232ecbf77168ee --- src/Testing/src/ITestMethodLifecycle.cs | 23 +++ src/Testing/src/RepeatAttribute.cs | 27 ++++ src/Testing/src/RepeatContext.cs | 27 ++++ src/Testing/src/ShortClassNameAttribute.cs | 17 +++ src/Testing/src/TestContext.cs | 44 ++++++ src/Testing/src/TestFileOutputContext.cs | 140 ++++++++++++++++++ .../src/TestOutputDirectoryAttribute.cs | 20 +++ src/Testing/src/xunit/AspNetTestCaseRunner.cs | 33 +++++ .../src/xunit/AspNetTestClassRunner.cs | 44 ++++++ .../src/xunit/AspNetTestCollectionRunner.cs | 14 +- src/Testing/src/xunit/AspNetTestInvoker.cs | 84 +++++++++++ .../src/xunit/AspNetTestMethodRunner.cs | 73 +++++++++ src/Testing/src/xunit/AspNetTestRunner.cs | 78 ++++++++++ .../src/xunit/AspNetTheoryTestCaseRunner.cs | 33 +++++ src/Testing/test/Properties/AssemblyInfo.cs | 1 + src/Testing/test/RepeatTest.cs | 43 ++++++ src/Testing/test/TestContextTest.cs | 83 +++++++++++ 17 files changed, 781 insertions(+), 3 deletions(-) create mode 100644 src/Testing/src/ITestMethodLifecycle.cs create mode 100644 src/Testing/src/RepeatAttribute.cs create mode 100644 src/Testing/src/RepeatContext.cs create mode 100644 src/Testing/src/ShortClassNameAttribute.cs create mode 100644 src/Testing/src/TestContext.cs create mode 100644 src/Testing/src/TestFileOutputContext.cs create mode 100644 src/Testing/src/TestOutputDirectoryAttribute.cs create mode 100644 src/Testing/src/xunit/AspNetTestCaseRunner.cs create mode 100644 src/Testing/src/xunit/AspNetTestClassRunner.cs create mode 100644 src/Testing/src/xunit/AspNetTestInvoker.cs create mode 100644 src/Testing/src/xunit/AspNetTestMethodRunner.cs create mode 100644 src/Testing/src/xunit/AspNetTestRunner.cs create mode 100644 src/Testing/src/xunit/AspNetTheoryTestCaseRunner.cs create mode 100644 src/Testing/test/RepeatTest.cs create mode 100644 src/Testing/test/TestContextTest.cs diff --git a/src/Testing/src/ITestMethodLifecycle.cs b/src/Testing/src/ITestMethodLifecycle.cs new file mode 100644 index 000000000000..d22779b6dd03 --- /dev/null +++ b/src/Testing/src/ITestMethodLifecycle.cs @@ -0,0 +1,23 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Testing +{ + /// + /// Defines a lifecycle for attributes or classes that want to know about tests starting + /// or ending. Implement this on a test class, or attribute at the method/class/assembly level. + /// + /// + /// Requires defining as the test framework. + /// + public interface ITestMethodLifecycle + { + Task OnTestStartAsync(TestContext context, CancellationToken cancellationToken); + + Task OnTestEndAsync(TestContext context, Exception exception, CancellationToken cancellationToken); + } +} diff --git a/src/Testing/src/RepeatAttribute.cs b/src/Testing/src/RepeatAttribute.cs new file mode 100644 index 000000000000..7bf307373480 --- /dev/null +++ b/src/Testing/src/RepeatAttribute.cs @@ -0,0 +1,27 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.ComponentModel; + +namespace Microsoft.AspNetCore.Testing +{ + /// + /// Runs a test multiple times to stress flaky tests that are believed to be fixed. + /// This can be used on an assembly, class, or method name. Requires using the AspNetCore test framework. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly, AllowMultiple = false)] + public class RepeatAttribute : Attribute + { + public RepeatAttribute(int runCount = 10) + { + RunCount = runCount; + } + + /// + /// The number of times to run a test. + /// + public int RunCount { get; } + } +} diff --git a/src/Testing/src/RepeatContext.cs b/src/Testing/src/RepeatContext.cs new file mode 100644 index 000000000000..d76a0f177e2a --- /dev/null +++ b/src/Testing/src/RepeatContext.cs @@ -0,0 +1,27 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Threading; + +namespace Microsoft.AspNetCore.Testing +{ + public class RepeatContext + { + private static AsyncLocal _current = new AsyncLocal(); + + public static RepeatContext Current + { + get => _current.Value; + internal set => _current.Value = value; + } + + public RepeatContext(int limit) + { + Limit = limit; + } + + public int Limit { get; } + + public int CurrentIteration { get; set; } + } +} diff --git a/src/Testing/src/ShortClassNameAttribute.cs b/src/Testing/src/ShortClassNameAttribute.cs new file mode 100644 index 000000000000..6a36575d70bf --- /dev/null +++ b/src/Testing/src/ShortClassNameAttribute.cs @@ -0,0 +1,17 @@ +// Copyright(c) .NET Foundation.All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.AspNetCore.Testing +{ + /// + /// Used to specify that should used the + /// unqualified class name. This is needed when a fully-qualified class name exceeds + /// max path for logging. + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Assembly, AllowMultiple = false)] + public class ShortClassNameAttribute : Attribute + { + } +} diff --git a/src/Testing/src/TestContext.cs b/src/Testing/src/TestContext.cs new file mode 100644 index 000000000000..a702d71ecf16 --- /dev/null +++ b/src/Testing/src/TestContext.cs @@ -0,0 +1,44 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Reflection; +using Xunit.Abstractions; + +namespace Microsoft.AspNetCore.Testing +{ + /// + /// Provides access to contextual information about the running tests. Get access by + /// implementing . + /// + /// + /// Requires defining as the test framework. + /// + public sealed class TestContext + { + private Lazy _files; + + public TestContext( + Type testClass, + object[] constructorArguments, + MethodInfo testMethod, + object[] methodArguments, + ITestOutputHelper output) + { + TestClass = testClass; + ConstructorArguments = constructorArguments; + TestMethod = testMethod; + MethodArguments = methodArguments; + Output = output; + + _files = new Lazy(() => new TestFileOutputContext(this)); + } + + public Type TestClass { get; } + public MethodInfo TestMethod { get; } + public object[] ConstructorArguments { get; } + public object[] MethodArguments { get; } + public ITestOutputHelper Output { get; } + public TestFileOutputContext FileOutput => _files.Value; + } +} diff --git a/src/Testing/src/TestFileOutputContext.cs b/src/Testing/src/TestFileOutputContext.cs new file mode 100644 index 000000000000..496a1379fb6c --- /dev/null +++ b/src/Testing/src/TestFileOutputContext.cs @@ -0,0 +1,140 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; + +namespace Microsoft.AspNetCore.Testing +{ + /// + /// Provides access to file storage for the running test. Get access by + /// implementing , and accessing . + /// + /// + /// Requires defining as the test framework. + /// + public sealed class TestFileOutputContext + { + private static char[] InvalidFileChars = new char[] + { + '\"', '<', '>', '|', '\0', + (char)1, (char)2, (char)3, (char)4, (char)5, (char)6, (char)7, (char)8, (char)9, (char)10, + (char)11, (char)12, (char)13, (char)14, (char)15, (char)16, (char)17, (char)18, (char)19, (char)20, + (char)21, (char)22, (char)23, (char)24, (char)25, (char)26, (char)27, (char)28, (char)29, (char)30, + (char)31, ':', '*', '?', '\\', '/', ' ', (char)127 + }; + + private readonly TestContext _parent; + + public TestFileOutputContext(TestContext parent) + { + _parent = parent; + + TestName = GetTestMethodName(parent.TestMethod, parent.MethodArguments); + TestClassName = GetTestClassName(parent.TestClass); + + AssemblyOutputDirectory = GetAssemblyBaseDirectory(_parent.TestClass.Assembly); + if (!string.IsNullOrEmpty(AssemblyOutputDirectory)) + { + TestClassOutputDirectory = Path.Combine(AssemblyOutputDirectory, TestClassName); + } + } + + public string TestName { get; } + + public string TestClassName { get; } + + public string AssemblyOutputDirectory { get; } + + public string TestClassOutputDirectory { get; } + + public string GetUniqueFileName(string prefix, string extension) + { + if (prefix == null) + { + throw new ArgumentNullException(nameof(prefix)); + } + + if (extension != null && !extension.StartsWith(".", StringComparison.Ordinal)) + { + throw new ArgumentException("The extension must start with '.' if one is provided.", nameof(extension)); + } + + var path = Path.Combine(TestClassOutputDirectory, $"{prefix}{extension}"); + + var i = 1; + while (File.Exists(path)) + { + path = Path.Combine(TestClassOutputDirectory, $"{prefix}{i++}{extension}"); + } + + return path; + } + + // Gets the output directory without appending the TFM or assembly name. + public static string GetOutputDirectory(Assembly assembly) + { + var attribute = assembly.GetCustomAttributes().OfType().FirstOrDefault(); + return attribute?.BaseDirectory; + } + + public static string GetAssemblyBaseDirectory(Assembly assembly, string baseDirectory = null) + { + var attribute = assembly.GetCustomAttributes().OfType().FirstOrDefault(); + baseDirectory = baseDirectory ?? attribute?.BaseDirectory; + if (string.IsNullOrEmpty(baseDirectory)) + { + return string.Empty; + } + + return Path.Combine(baseDirectory, assembly.GetName().Name, attribute.TargetFramework); + } + + public static string GetTestClassName(Type type) + { + var shortNameAttribute = + type.GetCustomAttribute() ?? + type.Assembly.GetCustomAttribute(); + var name = shortNameAttribute == null ? type.FullName : type.Name; + + // Try to shorten the class name using the assembly name + var assemblyName = type.Assembly.GetName().Name; + if (name.StartsWith(assemblyName + ".")) + { + name = name.Substring(assemblyName.Length + 1); + } + + return name; + } + + public static string GetTestMethodName(MethodInfo method, object[] arguments) + { + var name = arguments.Aggregate(method.Name, (a, b) => $"{a}-{(b ?? "null")}"); + return RemoveIllegalFileChars(name); + } + + public static string RemoveIllegalFileChars(string s) + { + var sb = new StringBuilder(); + + foreach (var c in s) + { + if (InvalidFileChars.Contains(c)) + { + if (sb.Length > 0 && sb[sb.Length - 1] != '_') + { + sb.Append('_'); + } + } + else + { + sb.Append(c); + } + } + return sb.ToString(); + } + } +} diff --git a/src/Testing/src/TestOutputDirectoryAttribute.cs b/src/Testing/src/TestOutputDirectoryAttribute.cs new file mode 100644 index 000000000000..4ae8cea05457 --- /dev/null +++ b/src/Testing/src/TestOutputDirectoryAttribute.cs @@ -0,0 +1,20 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.AspNetCore.Testing +{ + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false, Inherited = true)] + public class TestOutputDirectoryAttribute : Attribute + { + public TestOutputDirectoryAttribute(string targetFramework, string baseDirectory = null) + { + TargetFramework = targetFramework; + BaseDirectory = baseDirectory; + } + + public string BaseDirectory { get; } + public string TargetFramework { get; } + } +} diff --git a/src/Testing/src/xunit/AspNetTestCaseRunner.cs b/src/Testing/src/xunit/AspNetTestCaseRunner.cs new file mode 100644 index 000000000000..42773db21274 --- /dev/null +++ b/src/Testing/src/xunit/AspNetTestCaseRunner.cs @@ -0,0 +1,33 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Threading; +using Xunit.Abstractions; +using Xunit.Sdk; + +namespace Microsoft.AspNetCore.Testing +{ + internal class AspNetTestCaseRunner : XunitTestCaseRunner + { + public AspNetTestCaseRunner( + IXunitTestCase testCase, + string displayName, + string skipReason, + object[] constructorArguments, + object[] testMethodArguments, + IMessageBus messageBus, + ExceptionAggregator aggregator, + CancellationTokenSource cancellationTokenSource) + : base(testCase, displayName, skipReason, constructorArguments, testMethodArguments, messageBus, aggregator, cancellationTokenSource) + { + } + + protected override XunitTestRunner CreateTestRunner(ITest test, IMessageBus messageBus, Type testClass, object[] constructorArguments, MethodInfo testMethod, object[] testMethodArguments, string skipReason, IReadOnlyList beforeAfterAttributes, ExceptionAggregator aggregator, CancellationTokenSource cancellationTokenSource) + { + return new AspNetTestRunner(test, messageBus, testClass, constructorArguments, testMethod, testMethodArguments, skipReason, beforeAfterAttributes, aggregator, cancellationTokenSource); + } + } +} diff --git a/src/Testing/src/xunit/AspNetTestClassRunner.cs b/src/Testing/src/xunit/AspNetTestClassRunner.cs new file mode 100644 index 000000000000..bbefa37427bf --- /dev/null +++ b/src/Testing/src/xunit/AspNetTestClassRunner.cs @@ -0,0 +1,44 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Xunit.Abstractions; +using Xunit.Sdk; + +namespace Microsoft.AspNetCore.Testing +{ + internal class AspNetTestClassRunner : XunitTestClassRunner + { + public AspNetTestClassRunner( + ITestClass testClass, + IReflectionTypeInfo @class, + IEnumerable testCases, + IMessageSink diagnosticMessageSink, + IMessageBus messageBus, + ITestCaseOrderer testCaseOrderer, + ExceptionAggregator aggregator, + CancellationTokenSource cancellationTokenSource, + IDictionary collectionFixtureMappings) + : base(testClass, @class, testCases, diagnosticMessageSink, messageBus, testCaseOrderer, aggregator, cancellationTokenSource, collectionFixtureMappings) + { + } + + protected override Task RunTestMethodAsync(ITestMethod testMethod, IReflectionMethodInfo method, IEnumerable testCases, object[] constructorArguments) + { + var runner = new AspNetTestMethodRunner( + testMethod, + Class, + method, + testCases, + DiagnosticMessageSink, + MessageBus, + new ExceptionAggregator(Aggregator), + CancellationTokenSource, + constructorArguments); + return runner.RunAsync(); + } + } +} diff --git a/src/Testing/src/xunit/AspNetTestCollectionRunner.cs b/src/Testing/src/xunit/AspNetTestCollectionRunner.cs index 264cf769c63a..522cbd4624ca 100644 --- a/src/Testing/src/xunit/AspNetTestCollectionRunner.cs +++ b/src/Testing/src/xunit/AspNetTestCollectionRunner.cs @@ -59,9 +59,17 @@ protected override Task BeforeTestCollectionFinishedAsync() protected override Task RunTestClassAsync(ITestClass testClass, IReflectionTypeInfo @class, IEnumerable testCases) { - var caste = testCases.ToArray(); - var type = caste.First().GetType(); - return base.RunTestClassAsync(testClass, @class, testCases); + var runner = new AspNetTestClassRunner( + testClass, + @class, + testCases, + DiagnosticMessageSink, + MessageBus, + TestCaseOrderer, + new ExceptionAggregator(Aggregator), + CancellationTokenSource, + CollectionFixtureMappings); + return runner.RunAsync(); } } } diff --git a/src/Testing/src/xunit/AspNetTestInvoker.cs b/src/Testing/src/xunit/AspNetTestInvoker.cs new file mode 100644 index 000000000000..a764db6622d0 --- /dev/null +++ b/src/Testing/src/xunit/AspNetTestInvoker.cs @@ -0,0 +1,84 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; +using Xunit.Abstractions; +using Xunit.Sdk; + +namespace Microsoft.AspNetCore.Testing +{ + internal class AspNetTestInvoker : XunitTestInvoker + { + public AspNetTestInvoker( + ITest test, + IMessageBus messageBus, + Type testClass, + object[] constructorArguments, + MethodInfo testMethod, + object[] testMethodArguments, + IReadOnlyList beforeAfterAttributes, + ExceptionAggregator aggregator, + CancellationTokenSource cancellationTokenSource) + : base(test, messageBus, testClass, constructorArguments, testMethod, testMethodArguments, beforeAfterAttributes, aggregator, cancellationTokenSource) + { + } + + protected override async Task InvokeTestMethodAsync(object testClassInstance) + { + var output = new TestOutputHelper(); + output.Initialize(MessageBus, Test); + + var context = new TestContext(TestClass, ConstructorArguments, TestMethod, TestMethodArguments, output); + var lifecycleHooks = GetLifecycleHooks(testClassInstance, TestClass, TestMethod); + + await Aggregator.RunAsync(async () => + { + foreach (var lifecycleHook in lifecycleHooks) + { + await lifecycleHook.OnTestStartAsync(context, CancellationTokenSource.Token); + } + }); + + var time = await base.InvokeTestMethodAsync(testClassInstance); + + await Aggregator.RunAsync(async () => + { + var exception = Aggregator.HasExceptions ? Aggregator.ToException() : null; + foreach (var lifecycleHook in lifecycleHooks) + { + await lifecycleHook.OnTestEndAsync(context, exception, CancellationTokenSource.Token); + } + }); + + return time; + } + + private static IEnumerable GetLifecycleHooks(object testClassInstance, Type testClass, MethodInfo testMethod) + { + foreach (var attribute in testMethod.GetCustomAttributes(inherit: true).OfType()) + { + yield return attribute; + } + + if (testClassInstance is ITestMethodLifecycle instance) + { + yield return instance; + } + + foreach (var attribute in testClass.GetCustomAttributes(inherit: true).OfType()) + { + yield return attribute; + } + + foreach (var attribute in testClass.Assembly.GetCustomAttributes(inherit: true).OfType()) + { + yield return attribute; + } + } + } +} diff --git a/src/Testing/src/xunit/AspNetTestMethodRunner.cs b/src/Testing/src/xunit/AspNetTestMethodRunner.cs new file mode 100644 index 000000000000..e238d0769d81 --- /dev/null +++ b/src/Testing/src/xunit/AspNetTestMethodRunner.cs @@ -0,0 +1,73 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Testing.xunit; +using Xunit.Abstractions; +using Xunit.Sdk; + +namespace Microsoft.AspNetCore.Testing +{ + internal class AspNetTestMethodRunner : XunitTestMethodRunner + { + private readonly object[] _constructorArguments; + private readonly IMessageSink _diagnosticMessageSink; + + public AspNetTestMethodRunner( + ITestMethod testMethod, + IReflectionTypeInfo @class, + IReflectionMethodInfo method, + IEnumerable testCases, + IMessageSink diagnosticMessageSink, + IMessageBus messageBus, + ExceptionAggregator aggregator, + CancellationTokenSource cancellationTokenSource, + object[] constructorArguments) + : base(testMethod, @class, method, testCases, diagnosticMessageSink, messageBus, aggregator, cancellationTokenSource, constructorArguments) + { + _diagnosticMessageSink = diagnosticMessageSink; + _constructorArguments = constructorArguments; + } + + protected override Task RunTestCaseAsync(IXunitTestCase testCase) + { + if (testCase.GetType() == typeof(XunitTestCase)) + { + // If we get here this is a 'regular' test case, not something that represents a skipped test. + // + // We can take control of it's invocation thusly. + var runner = new AspNetTestCaseRunner( + testCase, + testCase.DisplayName, + testCase.SkipReason, + _constructorArguments, + testCase.TestMethodArguments, + MessageBus, + new ExceptionAggregator(Aggregator), + CancellationTokenSource); + return runner.RunAsync(); + } + + if (testCase.GetType() == typeof(XunitTheoryTestCase)) + { + // If we get here this is a 'regular' theory test case, not something that represents a skipped test. + // + // We can take control of it's invocation thusly. + var runner = new AspNetTheoryTestCaseRunner( + testCase, + testCase.DisplayName, + testCase.SkipReason, + _constructorArguments, + _diagnosticMessageSink, + MessageBus, + new ExceptionAggregator(Aggregator), + CancellationTokenSource); + return runner.RunAsync(); + } + + return base.RunTestCaseAsync(testCase); + } + } +} diff --git a/src/Testing/src/xunit/AspNetTestRunner.cs b/src/Testing/src/xunit/AspNetTestRunner.cs new file mode 100644 index 000000000000..2786a866b417 --- /dev/null +++ b/src/Testing/src/xunit/AspNetTestRunner.cs @@ -0,0 +1,78 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; +using Xunit.Abstractions; +using Xunit.Sdk; + +namespace Microsoft.AspNetCore.Testing +{ + internal class AspNetTestRunner : XunitTestRunner + { + public AspNetTestRunner( + ITest test, + IMessageBus messageBus, + Type testClass, + object[] constructorArguments, + MethodInfo testMethod, + object[] testMethodArguments, + string skipReason, + IReadOnlyList beforeAfterAttributes, + ExceptionAggregator aggregator, + CancellationTokenSource cancellationTokenSource) + : base(test, messageBus, testClass, constructorArguments, testMethod, testMethodArguments, skipReason, beforeAfterAttributes, aggregator, cancellationTokenSource) + { + } + + protected override async Task InvokeTestMethodAsync(ExceptionAggregator aggregator) + { + var repeatAttribute = GetRepeatAttribute(TestMethod); + if (repeatAttribute == null) + { + return await InvokeTestMethodCoreAsync(aggregator); + } + + var repeatContext = new RepeatContext(repeatAttribute.RunCount); + RepeatContext.Current = repeatContext; + + var timeTaken = 0.0M; + for (repeatContext.CurrentIteration = 0; repeatContext.CurrentIteration < repeatContext.Limit; repeatContext.CurrentIteration++) + { + timeTaken = await InvokeTestMethodCoreAsync(aggregator); + if (aggregator.HasExceptions) + { + return timeTaken; + } + } + + return timeTaken; + } + + private Task InvokeTestMethodCoreAsync(ExceptionAggregator aggregator) + { + var invoker = new AspNetTestInvoker(Test, MessageBus, TestClass, ConstructorArguments, TestMethod, TestMethodArguments, BeforeAfterAttributes, aggregator, CancellationTokenSource); + return invoker.RunAsync(); + } + + private RepeatAttribute GetRepeatAttribute(MethodInfo methodInfo) + { + var attributeCandidate = methodInfo.GetCustomAttribute(); + if (attributeCandidate != null) + { + return attributeCandidate; + } + + attributeCandidate = methodInfo.DeclaringType.GetCustomAttribute(); + if (attributeCandidate != null) + { + return attributeCandidate; + } + + return methodInfo.DeclaringType.Assembly.GetCustomAttribute(); + } + } +} diff --git a/src/Testing/src/xunit/AspNetTheoryTestCaseRunner.cs b/src/Testing/src/xunit/AspNetTheoryTestCaseRunner.cs new file mode 100644 index 000000000000..a09a17cf69f3 --- /dev/null +++ b/src/Testing/src/xunit/AspNetTheoryTestCaseRunner.cs @@ -0,0 +1,33 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Threading; +using Xunit.Abstractions; +using Xunit.Sdk; + +namespace Microsoft.AspNetCore.Testing.xunit +{ + internal class AspNetTheoryTestCaseRunner : XunitTheoryTestCaseRunner + { + public AspNetTheoryTestCaseRunner( + IXunitTestCase testCase, + string displayName, + string skipReason, + object[] constructorArguments, + IMessageSink diagnosticMessageSink, + IMessageBus messageBus, + ExceptionAggregator aggregator, + CancellationTokenSource cancellationTokenSource) + : base(testCase, displayName, skipReason, constructorArguments, diagnosticMessageSink, messageBus, aggregator, cancellationTokenSource) + { + } + + protected override XunitTestRunner CreateTestRunner(ITest test, IMessageBus messageBus, Type testClass, object[] constructorArguments, MethodInfo testMethod, object[] testMethodArguments, string skipReason, IReadOnlyList beforeAfterAttributes, ExceptionAggregator aggregator, CancellationTokenSource cancellationTokenSource) + { + return new AspNetTestRunner(test, messageBus, testClass, constructorArguments, testMethod, testMethodArguments, skipReason, beforeAfterAttributes, aggregator, cancellationTokenSource); + } + } +} diff --git a/src/Testing/test/Properties/AssemblyInfo.cs b/src/Testing/test/Properties/AssemblyInfo.cs index 4dba8d157ef7..d585b5ed95b8 100644 --- a/src/Testing/test/Properties/AssemblyInfo.cs +++ b/src/Testing/test/Properties/AssemblyInfo.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Testing; using Xunit; +[assembly: Repeat(1)] [assembly: AssemblyFixture(typeof(TestAssemblyFixture))] [assembly: TestFramework("Microsoft.AspNetCore.Testing.AspNetTestFramework", "Microsoft.AspNetCore.Testing")] diff --git a/src/Testing/test/RepeatTest.cs b/src/Testing/test/RepeatTest.cs new file mode 100644 index 000000000000..0d995fad5902 --- /dev/null +++ b/src/Testing/test/RepeatTest.cs @@ -0,0 +1,43 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Xunit; + +namespace Microsoft.AspNetCore.Testing +{ + [Repeat] + public class RepeatTest + { + public static int _runCount = 0; + + [Fact] + [Repeat(5)] + public void RepeatLimitIsSetCorrectly() + { + Assert.Equal(5, RepeatContext.Current.Limit); + } + + [Fact] + [Repeat(5)] + public void RepeatRunsTestSpecifiedNumberOfTimes() + { + Assert.Equal(RepeatContext.Current.CurrentIteration, _runCount); + _runCount++; + } + + [Fact] + public void RepeatCanBeSetOnClass() + { + Assert.Equal(10, RepeatContext.Current.Limit); + } + } + + public class LoggedTestXunitRepeatAssemblyTests + { + [Fact] + public void RepeatCanBeSetOnAssembly() + { + Assert.Equal(1, RepeatContext.Current.Limit); + } + } +} diff --git a/src/Testing/test/TestContextTest.cs b/src/Testing/test/TestContextTest.cs new file mode 100644 index 000000000000..944d706477d6 --- /dev/null +++ b/src/Testing/test/TestContextTest.cs @@ -0,0 +1,83 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace Microsoft.AspNetCore.Testing +{ + public class TestContextTest : ITestMethodLifecycle + { + public TestContext Context { get; private set; } + + [Fact] + public void FullName_IsUsed_ByDefault() + { + Assert.Equal(GetType().FullName, Context.FileOutput.TestClassName); + } + + Task ITestMethodLifecycle.OnTestStartAsync(TestContext context, CancellationToken cancellationToken) + { + Context = context; + return Task.CompletedTask; + } + + Task ITestMethodLifecycle.OnTestEndAsync(TestContext context, Exception exception, CancellationToken cancellationToken) + { + return Task.CompletedTask; + } + } +} + +namespace Microsoft.AspNetCore.Testing.Tests +{ + public class TestContextNameShorteningTest : ITestMethodLifecycle + { + public TestContext Context { get; private set; } + + [Fact] + public void NameIsShortenedWhenAssemblyNameIsAPrefix() + { + Assert.Equal(GetType().Name, Context.FileOutput.TestClassName); + } + + Task ITestMethodLifecycle.OnTestStartAsync(TestContext context, CancellationToken cancellationToken) + { + Context = context; + return Task.CompletedTask; + } + + Task ITestMethodLifecycle.OnTestEndAsync(TestContext context, Exception exception, CancellationToken cancellationToken) + { + return Task.CompletedTask; + } + } +} + +namespace Microsoft.AspNetCore.Testing +{ + [ShortClassName] + public class TestContextTestClassShortNameAttributeTest : ITestMethodLifecycle + { + public TestContext Context { get; private set; } + + [Fact] + public void ShortClassNameUsedWhenShortClassNameAttributeSpecified() + { + Assert.Equal(GetType().Name, Context.FileOutput.TestClassName); + } + + Task ITestMethodLifecycle.OnTestStartAsync(TestContext context, CancellationToken cancellationToken) + { + Context = context; + return Task.CompletedTask; + } + + Task ITestMethodLifecycle.OnTestEndAsync(TestContext context, Exception exception, CancellationToken cancellationToken) + { + return Task.CompletedTask; + } + } +} From 5114f2f1a25a712112b4e23e2dae99dfa9b91dac Mon Sep 17 00:00:00 2001 From: Pranav K Date: Mon, 23 Sep 2019 09:42:48 -0700 Subject: [PATCH 148/183] Add null check for DotNetObjectReference (dotnet/extensions#2372) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/45776ac25c750bc98522f7d8bdbba61bcc7540d4 --- .../Microsoft.JSInterop/src/DotNetObjectReference.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReference.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReference.cs index 989d8062bb63..53217936d67b 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReference.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReference.cs @@ -1,6 +1,8 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; + namespace Microsoft.JSInterop { /// @@ -15,6 +17,11 @@ public static class DotNetObjectReference /// An instance of . public static DotNetObjectReference Create(TValue value) where TValue : class { + if (value is null) + { + throw new ArgumentNullException(nameof(value)); + } + return new DotNetObjectReference(value); } } From b67cfeab493a1b1ee774d5f4ebbca89eb0261f5b Mon Sep 17 00:00:00 2001 From: wtgodbe Date: Mon, 23 Sep 2019 14:55:45 -0700 Subject: [PATCH 149/183] Switch to embedded PackageIcon \n\nCommit migrated from https://github.com/dotnet/extensions/commit/6acbcb1e6de598049e4549092f59edf3c30dc3af --- .../src/Microsoft.Extensions.FileProviders.Embedded.csproj | 1 + ...icrosoft.Extensions.FileProviders.Embedded.multitarget.nuspec | 1 + ...rosoft.Extensions.FileProviders.Embedded.netcoreapp3.0.nuspec | 1 + 3 files changed, 3 insertions(+) diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj index d36323e3b151..4858cae97e60 100644 --- a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj @@ -32,5 +32,6 @@ + diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.multitarget.nuspec b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.multitarget.nuspec index 874c90c79dcc..f958f4550a09 100644 --- a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.multitarget.nuspec +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.multitarget.nuspec @@ -18,6 +18,7 @@ + diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.netcoreapp3.0.nuspec b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.netcoreapp3.0.nuspec index f98f0b4ad5aa..4c586dedc45a 100644 --- a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.netcoreapp3.0.nuspec +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.netcoreapp3.0.nuspec @@ -15,6 +15,7 @@ + From afa62186c6f8bb59be0b22b83ce0a613d99285db Mon Sep 17 00:00:00 2001 From: wtgodbe Date: Tue, 24 Sep 2019 09:48:33 -0700 Subject: [PATCH 150/183] Add icon metadata to manually generated .nuspecs \n\nCommit migrated from https://github.com/dotnet/extensions/commit/224e1104acb518c672d1b97e2bc5279a2869b3f9 --- ...icrosoft.Extensions.FileProviders.Embedded.multitarget.nuspec | 1 + ...rosoft.Extensions.FileProviders.Embedded.netcoreapp3.0.nuspec | 1 + 2 files changed, 2 insertions(+) diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.multitarget.nuspec b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.multitarget.nuspec index f958f4550a09..c9ab1ba847ea 100644 --- a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.multitarget.nuspec +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.multitarget.nuspec @@ -10,6 +10,7 @@ + packageIcon.png diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.netcoreapp3.0.nuspec b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.netcoreapp3.0.nuspec index 4c586dedc45a..b42e51174f83 100644 --- a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.netcoreapp3.0.nuspec +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.netcoreapp3.0.nuspec @@ -7,6 +7,7 @@ + packageIcon.png From f31f20e70a453dca68e72cfe9a3131942808eaee Mon Sep 17 00:00:00 2001 From: Chris Ross Date: Thu, 3 Oct 2019 21:03:01 -0700 Subject: [PATCH 151/183] Add OSMinVersionAttribute (dotnet/extensions#2449) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/ddc00c0f2f8d5430646012950544cf137712f15b --- .../src/xunit/OSMinVersionAttribute.cs | 81 +++++++++++++++++++ src/Testing/src/xunit/WindowsVersions.cs | 18 +++++ src/Testing/test/OSMinVersionAttributeTest.cs | 77 ++++++++++++++++++ src/Testing/test/OSMinVersionTest.cs | 73 +++++++++++++++++ 4 files changed, 249 insertions(+) create mode 100644 src/Testing/src/xunit/OSMinVersionAttribute.cs create mode 100644 src/Testing/test/OSMinVersionAttributeTest.cs create mode 100644 src/Testing/test/OSMinVersionTest.cs diff --git a/src/Testing/src/xunit/OSMinVersionAttribute.cs b/src/Testing/src/xunit/OSMinVersionAttribute.cs new file mode 100644 index 000000000000..3bef606ee676 --- /dev/null +++ b/src/Testing/src/xunit/OSMinVersionAttribute.cs @@ -0,0 +1,81 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Runtime.InteropServices; + +namespace Microsoft.AspNetCore.Testing +{ + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly, AllowMultiple = true)] + public class OSMinVersionAttribute : Attribute, ITestCondition + { + private readonly OperatingSystems _targetOS; + private readonly Version _minVersion; + private readonly OperatingSystems _currentOS; + private readonly Version _currentVersion; + private readonly bool _skip; + + /// + /// Used to indicate the minimum version a test can run on for the given operating system. + /// Also add to skip other operating systems. + /// + /// The OS to check for a version. Only Windows is currently supported. + /// The minimum OS version NOT to skip. + public OSMinVersionAttribute(OperatingSystems targetOS, string minVersion) : + this(targetOS, Version.Parse(minVersion), GetCurrentOS(), GetCurrentOSVersion()) + { + } + + // to enable unit testing + internal OSMinVersionAttribute(OperatingSystems targetOS, Version minVersion, OperatingSystems currentOS, Version currentVersion) + { + if (targetOS != OperatingSystems.Windows) + { + throw new NotImplementedException(targetOS.ToString()); + } + + _targetOS = targetOS; + _minVersion = minVersion; + _currentOS = currentOS; + _currentVersion = currentVersion; + + _skip = _targetOS == _currentOS && _minVersion > _currentVersion; + SkipReason = $"The test cannot run on this operating system version '{currentVersion}'."; + } + + // Since a test would be excuted only if 'IsMet' is true, return false if we want to skip + public bool IsMet => !_skip; + + public string SkipReason { get; set; } + + static private OperatingSystems GetCurrentOS() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return OperatingSystems.Windows; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + return OperatingSystems.Linux; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + return OperatingSystems.MacOSX; + } + throw new PlatformNotSupportedException(); + } + + static private Version GetCurrentOSVersion() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return Environment.OSVersion.Version; + } + else + { + // Not implmeneted, but this will still be called before the OS check happens so don't throw. + return new Version(0, 0); + } + } + } +} diff --git a/src/Testing/src/xunit/WindowsVersions.cs b/src/Testing/src/xunit/WindowsVersions.cs index d89da44de38f..d0ef86d1a840 100644 --- a/src/Testing/src/xunit/WindowsVersions.cs +++ b/src/Testing/src/xunit/WindowsVersions.cs @@ -3,6 +3,9 @@ namespace Microsoft.AspNetCore.Testing { + /// + /// https://en.wikipedia.org/wiki/Windows_10_version_history + /// public static class WindowsVersions { public const string Win7 = "6.1"; @@ -14,5 +17,20 @@ public static class WindowsVersions public const string Win81 = "6.3"; public const string Win10 = "10.0"; + + /// + /// 1803, RS4, 17134 + /// + public const string Win10_RS4 = "10.0.17134"; + + /// + /// 1909, 19H2, 18363 + /// + public const string Win10_19H2 = "10.0.18363"; + + /// + /// _, 20H2, 18990 + /// + public const string Win10_20H1 = "10.0.18990"; } } diff --git a/src/Testing/test/OSMinVersionAttributeTest.cs b/src/Testing/test/OSMinVersionAttributeTest.cs new file mode 100644 index 000000000000..f156607e8b0e --- /dev/null +++ b/src/Testing/test/OSMinVersionAttributeTest.cs @@ -0,0 +1,77 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Xunit; + +namespace Microsoft.AspNetCore.Testing +{ + public class OSMinVersionAttributeTest + { + [Fact] + public void Linux_ThrowsNotImplemeneted() + { + Assert.Throws(() => new OSMinVersionAttribute(OperatingSystems.Linux, "2.5")); + } + + [Fact] + public void Mac_ThrowsNotImplemeneted() + { + Assert.Throws(() => new OSMinVersionAttribute(OperatingSystems.MacOSX, "2.5")); + } + + [Fact] + public void WindowsOrLinux_ThrowsNotImplemeneted() + { + Assert.Throws(() => new OSMinVersionAttribute(OperatingSystems.Linux | OperatingSystems.Windows, "2.5")); + } + + [Fact] + public void DoesNotSkip_LaterVersions() + { + var osSkipAttribute = new OSMinVersionAttribute( + OperatingSystems.Windows, + new Version("2.0"), + OperatingSystems.Windows, + new Version("2.5")); + + Assert.True(osSkipAttribute.IsMet); + } + + [Fact] + public void DoesNotSkip_SameVersion() + { + var osSkipAttribute = new OSMinVersionAttribute( + OperatingSystems.Windows, + new Version("2.5"), + OperatingSystems.Windows, + new Version("2.5")); + + Assert.True(osSkipAttribute.IsMet); + } + + [Fact] + public void Skip_EarlierVersion() + { + var osSkipAttribute = new OSMinVersionAttribute( + OperatingSystems.Windows, + new Version("3.0"), + OperatingSystems.Windows, + new Version("2.5")); + + Assert.False(osSkipAttribute.IsMet); + } + + [Fact] + public void DoesNotSkip_WhenOnlyVersionsMatch() + { + var osSkipAttribute = new OSMinVersionAttribute( + OperatingSystems.Windows, + new Version("2.5"), + OperatingSystems.Linux, + new Version("2.5")); + + Assert.True(osSkipAttribute.IsMet); + } + } +} diff --git a/src/Testing/test/OSMinVersionTest.cs b/src/Testing/test/OSMinVersionTest.cs new file mode 100644 index 000000000000..30233823e236 --- /dev/null +++ b/src/Testing/test/OSMinVersionTest.cs @@ -0,0 +1,73 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Runtime.InteropServices; +using Microsoft.Win32; +using Xunit; + +namespace Microsoft.AspNetCore.Testing +{ + public class OSMinVersionTest + { + [ConditionalFact] + [OSMinVersion(OperatingSystems.Windows, WindowsVersions.Win8)] + public void RunTest_Win8DoesNotRunOnWin7() + { + Assert.False( + RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && + Environment.OSVersion.Version.ToString().StartsWith("6.1"), + "Test should not be running on Win7 or Win2008R2."); + } + + [ConditionalTheory] + [OSMinVersion(OperatingSystems.Windows, WindowsVersions.Win8)] + [InlineData(1)] + public void RunTheory_Win8DoesNotRunOnWin7(int arg) + { + Assert.False( + RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && + Environment.OSVersion.Version.ToString().StartsWith("6.1"), + "Test should not be running on Win7 or Win2008R2."); + } + + [ConditionalFact] + [OSMinVersion(OperatingSystems.Windows, WindowsVersions.Win10_RS4)] + [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)] + public void RunTest_Win10_RS4() + { + Assert.True(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)); + var versionKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion"); + Assert.NotNull(versionKey); + var currentVersion = (string)versionKey.GetValue("CurrentBuildNumber"); + Assert.NotNull(currentVersion); + Assert.True(17134 <= int.Parse(currentVersion)); + } + + [ConditionalFact] + [OSMinVersion(OperatingSystems.Windows, WindowsVersions.Win10_19H2)] + [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)] + public void RunTest_Win10_19H2() + { + Assert.True(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)); + var versionKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion"); + Assert.NotNull(versionKey); + var currentVersion = (string)versionKey.GetValue("CurrentBuildNumber"); + Assert.NotNull(currentVersion); + Assert.True(18363 <= int.Parse(currentVersion)); + } + } + + [OSMinVersion(OperatingSystems.Windows, WindowsVersions.Win8)] + public class OSMinVersionClassTest + { + [ConditionalFact] + public void TestSkipClass_Win8DoesNotRunOnWin7() + { + Assert.False( + RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && + Environment.OSVersion.Version.ToString().StartsWith("6.1"), + "Test should not be running on Win7 or Win2008R2."); + } + } +} From aae216ade2db82e880616c648a42d2ee14fa8f3f Mon Sep 17 00:00:00 2001 From: Chris Ross Date: Wed, 9 Oct 2019 17:59:08 -0700 Subject: [PATCH 152/183] Unify OS version xunit attributes (dotnet/extensions#2481) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/65e70f58df999e69f53ddac3709410fd8fcc0ae6 --- .../src/xunit/MinimumOsVersionAttribute.cs | 68 +++++----------- .../src/xunit/OSMinVersionAttribute.cs | 81 ------------------- ...st.cs => MinimumOSVersionAttributeTest.cs} | 16 ++-- ...VersionTest.cs => MinimumOSVersionTest.cs} | 12 +-- 4 files changed, 32 insertions(+), 145 deletions(-) delete mode 100644 src/Testing/src/xunit/OSMinVersionAttribute.cs rename src/Testing/test/{OSMinVersionAttributeTest.cs => MinimumOSVersionAttributeTest.cs} (70%) rename src/Testing/test/{OSMinVersionTest.cs => MinimumOSVersionTest.cs} (86%) diff --git a/src/Testing/src/xunit/MinimumOsVersionAttribute.cs b/src/Testing/src/xunit/MinimumOsVersionAttribute.cs index df4985d338bc..4d016a07e784 100644 --- a/src/Testing/src/xunit/MinimumOsVersionAttribute.cs +++ b/src/Testing/src/xunit/MinimumOsVersionAttribute.cs @@ -3,7 +3,6 @@ using System; using System.Runtime.InteropServices; -using Microsoft.Win32; namespace Microsoft.AspNetCore.Testing { @@ -14,49 +13,36 @@ namespace Microsoft.AspNetCore.Testing [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly, AllowMultiple = true)] public class MinimumOSVersionAttribute : Attribute, ITestCondition { - private readonly OperatingSystems _excludedOperatingSystem; + private readonly OperatingSystems _targetOS; private readonly Version _minVersion; - private readonly OperatingSystems _osPlatform; - private readonly Version _osVersion; + private readonly OperatingSystems _currentOS; + private readonly Version _currentVersion; + private readonly bool _skip; public MinimumOSVersionAttribute(OperatingSystems operatingSystem, string minVersion) : - this( - operatingSystem, - GetCurrentOS(), - GetCurrentOSVersion(), - Version.Parse(minVersion)) + this(operatingSystem, Version.Parse(minVersion), GetCurrentOS(), GetCurrentOSVersion()) { } // to enable unit testing - internal MinimumOSVersionAttribute( - OperatingSystems operatingSystem, OperatingSystems osPlatform, Version osVersion, Version minVersion) + internal MinimumOSVersionAttribute(OperatingSystems targetOS, Version minVersion, OperatingSystems currentOS, Version currentVersion) { - if (operatingSystem != OperatingSystems.Windows) + if (targetOS != OperatingSystems.Windows) { throw new NotImplementedException("Min version support is only implemented for Windows."); } - _excludedOperatingSystem = operatingSystem; + _targetOS = targetOS; _minVersion = minVersion; - _osPlatform = osPlatform; - _osVersion = osVersion; + _currentOS = currentOS; + _currentVersion = currentVersion; - SkipReason = $"This test requires {_excludedOperatingSystem} {_minVersion} or later."; + // Do not skip other OS's, Use OSSkipConditionAttribute or a separate MinimumOSVersionAttribute for that. + _skip = _targetOS == _currentOS && _minVersion > _currentVersion; + SkipReason = $"This test requires {_targetOS} {_minVersion} or later."; } - public bool IsMet - { - get - { - // Do not skip other OS's, Use OSSkipConditionAttribute or a separate MinimumOSVersionAttribute for that. - if (_osPlatform != _excludedOperatingSystem) - { - return true; - } - - return _osVersion >= _minVersion; - } - } + // Since a test would be executed only if 'IsMet' is true, return false if we want to skip + public bool IsMet => !_skip; public string SkipReason { get; set; } @@ -77,34 +63,16 @@ private static OperatingSystems GetCurrentOS() throw new PlatformNotSupportedException(); } - private static Version GetCurrentOSVersion() + static private Version GetCurrentOSVersion() { - // currently not used on other OS's if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - // Win10+ - var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion"); - var major = key.GetValue("CurrentMajorVersionNumber") as int?; - var minor = key.GetValue("CurrentMinorVersionNumber") as int?; - - if (major.HasValue && minor.HasValue) - { - return new Version(major.Value, minor.Value); - } - - // CurrentVersion doesn't work past Win8.1 - var current = key.GetValue("CurrentVersion") as string; - if (!string.IsNullOrEmpty(current) && Version.TryParse(current, out var currentVersion)) - { - return currentVersion; - } - - // Environment.OSVersion doesn't work past Win8. return Environment.OSVersion.Version; } else { - return new Version(); + // Not implemented, but this will still be called before the OS check happens so don't throw. + return new Version(0, 0); } } } diff --git a/src/Testing/src/xunit/OSMinVersionAttribute.cs b/src/Testing/src/xunit/OSMinVersionAttribute.cs deleted file mode 100644 index 3bef606ee676..000000000000 --- a/src/Testing/src/xunit/OSMinVersionAttribute.cs +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Runtime.InteropServices; - -namespace Microsoft.AspNetCore.Testing -{ - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly, AllowMultiple = true)] - public class OSMinVersionAttribute : Attribute, ITestCondition - { - private readonly OperatingSystems _targetOS; - private readonly Version _minVersion; - private readonly OperatingSystems _currentOS; - private readonly Version _currentVersion; - private readonly bool _skip; - - /// - /// Used to indicate the minimum version a test can run on for the given operating system. - /// Also add to skip other operating systems. - /// - /// The OS to check for a version. Only Windows is currently supported. - /// The minimum OS version NOT to skip. - public OSMinVersionAttribute(OperatingSystems targetOS, string minVersion) : - this(targetOS, Version.Parse(minVersion), GetCurrentOS(), GetCurrentOSVersion()) - { - } - - // to enable unit testing - internal OSMinVersionAttribute(OperatingSystems targetOS, Version minVersion, OperatingSystems currentOS, Version currentVersion) - { - if (targetOS != OperatingSystems.Windows) - { - throw new NotImplementedException(targetOS.ToString()); - } - - _targetOS = targetOS; - _minVersion = minVersion; - _currentOS = currentOS; - _currentVersion = currentVersion; - - _skip = _targetOS == _currentOS && _minVersion > _currentVersion; - SkipReason = $"The test cannot run on this operating system version '{currentVersion}'."; - } - - // Since a test would be excuted only if 'IsMet' is true, return false if we want to skip - public bool IsMet => !_skip; - - public string SkipReason { get; set; } - - static private OperatingSystems GetCurrentOS() - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - return OperatingSystems.Windows; - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - return OperatingSystems.Linux; - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - return OperatingSystems.MacOSX; - } - throw new PlatformNotSupportedException(); - } - - static private Version GetCurrentOSVersion() - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - return Environment.OSVersion.Version; - } - else - { - // Not implmeneted, but this will still be called before the OS check happens so don't throw. - return new Version(0, 0); - } - } - } -} diff --git a/src/Testing/test/OSMinVersionAttributeTest.cs b/src/Testing/test/MinimumOSVersionAttributeTest.cs similarity index 70% rename from src/Testing/test/OSMinVersionAttributeTest.cs rename to src/Testing/test/MinimumOSVersionAttributeTest.cs index f156607e8b0e..a0a6e84d7df8 100644 --- a/src/Testing/test/OSMinVersionAttributeTest.cs +++ b/src/Testing/test/MinimumOSVersionAttributeTest.cs @@ -6,30 +6,30 @@ namespace Microsoft.AspNetCore.Testing { - public class OSMinVersionAttributeTest + public class MinimumOSVersionAttributeTest { [Fact] public void Linux_ThrowsNotImplemeneted() { - Assert.Throws(() => new OSMinVersionAttribute(OperatingSystems.Linux, "2.5")); + Assert.Throws(() => new MinimumOSVersionAttribute(OperatingSystems.Linux, "2.5")); } [Fact] public void Mac_ThrowsNotImplemeneted() { - Assert.Throws(() => new OSMinVersionAttribute(OperatingSystems.MacOSX, "2.5")); + Assert.Throws(() => new MinimumOSVersionAttribute(OperatingSystems.MacOSX, "2.5")); } [Fact] public void WindowsOrLinux_ThrowsNotImplemeneted() { - Assert.Throws(() => new OSMinVersionAttribute(OperatingSystems.Linux | OperatingSystems.Windows, "2.5")); + Assert.Throws(() => new MinimumOSVersionAttribute(OperatingSystems.Linux | OperatingSystems.Windows, "2.5")); } [Fact] public void DoesNotSkip_LaterVersions() { - var osSkipAttribute = new OSMinVersionAttribute( + var osSkipAttribute = new MinimumOSVersionAttribute( OperatingSystems.Windows, new Version("2.0"), OperatingSystems.Windows, @@ -41,7 +41,7 @@ public void DoesNotSkip_LaterVersions() [Fact] public void DoesNotSkip_SameVersion() { - var osSkipAttribute = new OSMinVersionAttribute( + var osSkipAttribute = new MinimumOSVersionAttribute( OperatingSystems.Windows, new Version("2.5"), OperatingSystems.Windows, @@ -53,7 +53,7 @@ public void DoesNotSkip_SameVersion() [Fact] public void Skip_EarlierVersion() { - var osSkipAttribute = new OSMinVersionAttribute( + var osSkipAttribute = new MinimumOSVersionAttribute( OperatingSystems.Windows, new Version("3.0"), OperatingSystems.Windows, @@ -65,7 +65,7 @@ public void Skip_EarlierVersion() [Fact] public void DoesNotSkip_WhenOnlyVersionsMatch() { - var osSkipAttribute = new OSMinVersionAttribute( + var osSkipAttribute = new MinimumOSVersionAttribute( OperatingSystems.Windows, new Version("2.5"), OperatingSystems.Linux, diff --git a/src/Testing/test/OSMinVersionTest.cs b/src/Testing/test/MinimumOSVersionTest.cs similarity index 86% rename from src/Testing/test/OSMinVersionTest.cs rename to src/Testing/test/MinimumOSVersionTest.cs index 30233823e236..b218cd1ec5ef 100644 --- a/src/Testing/test/OSMinVersionTest.cs +++ b/src/Testing/test/MinimumOSVersionTest.cs @@ -8,10 +8,10 @@ namespace Microsoft.AspNetCore.Testing { - public class OSMinVersionTest + public class MinimumOSVersionTest { [ConditionalFact] - [OSMinVersion(OperatingSystems.Windows, WindowsVersions.Win8)] + [MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win8)] public void RunTest_Win8DoesNotRunOnWin7() { Assert.False( @@ -21,7 +21,7 @@ public void RunTest_Win8DoesNotRunOnWin7() } [ConditionalTheory] - [OSMinVersion(OperatingSystems.Windows, WindowsVersions.Win8)] + [MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win8)] [InlineData(1)] public void RunTheory_Win8DoesNotRunOnWin7(int arg) { @@ -32,7 +32,7 @@ public void RunTheory_Win8DoesNotRunOnWin7(int arg) } [ConditionalFact] - [OSMinVersion(OperatingSystems.Windows, WindowsVersions.Win10_RS4)] + [MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10_RS4)] [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)] public void RunTest_Win10_RS4() { @@ -45,7 +45,7 @@ public void RunTest_Win10_RS4() } [ConditionalFact] - [OSMinVersion(OperatingSystems.Windows, WindowsVersions.Win10_19H2)] + [MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10_19H2)] [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)] public void RunTest_Win10_19H2() { @@ -58,7 +58,7 @@ public void RunTest_Win10_19H2() } } - [OSMinVersion(OperatingSystems.Windows, WindowsVersions.Win8)] + [MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win8)] public class OSMinVersionClassTest { [ConditionalFact] From e6cc17044799680f22a345e14b461484640b556b Mon Sep 17 00:00:00 2001 From: John Luo Date: Fri, 11 Oct 2019 03:41:09 -0700 Subject: [PATCH 153/183] Compile using ref assemblies (dotnet/extensions#2483) * Compile using ref assemblies * Use updated ResolveReferences.target from AspNetCore * Generate ref assemblies for .NET framework\n\nCommit migrated from https://github.com/dotnet/extensions/commit/e771c4205fd0f04e6cbebabc8f16ad9b8d905bf1 --- .../ref/Microsoft.AspNetCore.Testing.csproj | 32 +++ .../ref/Microsoft.AspNetCore.Testing.net46.cs | 269 ++++++++++++++++++ ...osoft.AspNetCore.Testing.netstandard2.0.cs | 269 ++++++++++++++++++ .../src/Microsoft.AspNetCore.Testing.csproj | 2 + 4 files changed, 572 insertions(+) create mode 100644 src/Testing/ref/Microsoft.AspNetCore.Testing.csproj create mode 100644 src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs create mode 100644 src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs diff --git a/src/Testing/ref/Microsoft.AspNetCore.Testing.csproj b/src/Testing/ref/Microsoft.AspNetCore.Testing.csproj new file mode 100644 index 000000000000..4606a5411106 --- /dev/null +++ b/src/Testing/ref/Microsoft.AspNetCore.Testing.csproj @@ -0,0 +1,32 @@ + + + + netstandard2.0;net46 + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs b/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs new file mode 100644 index 000000000000..c90ffb3a0f28 --- /dev/null +++ b/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs @@ -0,0 +1,269 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNetCore.Testing +{ + [System.AttributeUsageAttribute(System.AttributeTargets.Method, AllowMultiple=false)] + [Xunit.Sdk.XunitTestCaseDiscovererAttribute("Microsoft.AspNetCore.Testing.ConditionalFactDiscoverer", "Microsoft.AspNetCore.Testing")] + public partial class ConditionalFactAttribute : Xunit.FactAttribute + { + public ConditionalFactAttribute() { } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Method, AllowMultiple=false)] + [Xunit.Sdk.XunitTestCaseDiscovererAttribute("Microsoft.AspNetCore.Testing.ConditionalTheoryDiscoverer", "Microsoft.AspNetCore.Testing")] + public partial class ConditionalTheoryAttribute : Xunit.TheoryAttribute + { + public ConditionalTheoryAttribute() { } + } + public partial class CultureReplacer : System.IDisposable + { + public CultureReplacer(System.Globalization.CultureInfo culture, System.Globalization.CultureInfo uiCulture) { } + public CultureReplacer(string culture = "en-GB", string uiCulture = "en-US") { } + public static System.Globalization.CultureInfo DefaultCulture { get { throw null; } } + public static string DefaultCultureName { get { throw null; } } + public static string DefaultUICultureName { get { throw null; } } + public void Dispose() { } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Method, Inherited=true, AllowMultiple=false)] + public sealed partial class DockerOnlyAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition + { + public DockerOnlyAttribute() { } + public bool IsMet { get { throw null; } } + public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=true)] + public partial class EnvironmentVariableSkipConditionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition + { + public EnvironmentVariableSkipConditionAttribute(string variableName, params string[] values) { } + public bool IsMet { get { throw null; } } + public bool RunOnMatch { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public string SkipReason { get { throw null; } } + } + public static partial class ExceptionAssert + { + public static System.ArgumentException ThrowsArgument(System.Action testCode, string paramName, string exceptionMessage) { throw null; } + public static System.Threading.Tasks.Task ThrowsArgumentAsync(System.Func testCode, string paramName, string exceptionMessage) { throw null; } + public static System.ArgumentNullException ThrowsArgumentNull(System.Action testCode, string paramName) { throw null; } + public static System.ArgumentException ThrowsArgumentNullOrEmpty(System.Action testCode, string paramName) { throw null; } + public static System.Threading.Tasks.Task ThrowsArgumentNullOrEmptyAsync(System.Func testCode, string paramName) { throw null; } + public static System.ArgumentException ThrowsArgumentNullOrEmptyString(System.Action testCode, string paramName) { throw null; } + public static System.Threading.Tasks.Task ThrowsArgumentNullOrEmptyStringAsync(System.Func testCode, string paramName) { throw null; } + public static System.ArgumentOutOfRangeException ThrowsArgumentOutOfRange(System.Action testCode, string paramName, string exceptionMessage, object actualValue = null) { throw null; } + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.Task ThrowsAsync(System.Func testCode, string exceptionMessage) where TException : System.Exception { throw null; } + public static TException Throws(System.Action testCode) where TException : System.Exception { throw null; } + public static TException Throws(System.Action testCode, string exceptionMessage) where TException : System.Exception { throw null; } + public static TException Throws(System.Func testCode, string exceptionMessage) where TException : System.Exception { throw null; } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method)] + [Xunit.Sdk.TraitDiscovererAttribute("Microsoft.AspNetCore.Testing.FlakyTestDiscoverer", "Microsoft.AspNetCore.Testing")] + public sealed partial class FlakyAttribute : System.Attribute, Xunit.Sdk.ITraitAttribute + { + public FlakyAttribute(string gitHubIssueUrl, string firstFilter, params string[] additionalFilters) { } + public System.Collections.Generic.IReadOnlyList Filters { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string GitHubIssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } + public static partial class FlakyOn + { + public const string All = "All"; + public static partial class AzP + { + public const string All = "AzP:All"; + public const string Linux = "AzP:OS:Linux"; + public const string macOS = "AzP:OS:Darwin"; + public const string Windows = "AzP:OS:Windows_NT"; + } + public static partial class Helix + { + public const string All = "Helix:Queue:All"; + public const string Centos7Amd64 = "Helix:Queue:Centos.7.Amd64.Open"; + public const string Debian8Amd64 = "Helix:Queue:Debian.8.Amd64.Open"; + public const string Debian9Amd64 = "Helix:Queue:Debian.9.Amd64.Open"; + public const string Fedora27Amd64 = "Helix:Queue:Fedora.27.Amd64.Open"; + public const string Fedora28Amd64 = "Helix:Queue:Fedora.28.Amd64.Open"; + public const string macOS1012Amd64 = "Helix:Queue:OSX.1012.Amd64.Open"; + public const string Redhat7Amd64 = "Helix:Queue:Redhat.7.Amd64.Open"; + public const string Ubuntu1604Amd64 = "Helix:Queue:Ubuntu.1604.Amd64.Open"; + public const string Ubuntu1810Amd64 = "Helix:Queue:Ubuntu.1810.Amd64.Open"; + public const string Windows10Amd64 = "Helix:Queue:Windows.10.Amd64.ClientRS4.VS2017.Open"; + } + } + public partial class FlakyTestDiscoverer : Xunit.Sdk.ITraitDiscoverer + { + public FlakyTestDiscoverer() { } + public System.Collections.Generic.IEnumerable> GetTraits(Xunit.Abstractions.IAttributeInfo traitAttribute) { throw null; } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Method, AllowMultiple=false)] + public partial class FrameworkSkipConditionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition + { + public FrameworkSkipConditionAttribute(Microsoft.AspNetCore.Testing.RuntimeFrameworks excludedFrameworks) { } + public bool IsMet { get { throw null; } } + public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + } + public static partial class HelixQueues + { + public const string Centos7Amd64 = "Centos.7.Amd64.Open"; + public const string Debian8Amd64 = "Debian.8.Amd64.Open"; + public const string Debian9Amd64 = "Debian.9.Amd64.Open"; + public const string Fedora27Amd64 = "Fedora.27.Amd64.Open"; + public const string Fedora28Amd64 = "Fedora.28.Amd64.Open"; + public const string macOS1012Amd64 = "OSX.1012.Amd64.Open"; + public const string Redhat7Amd64 = "Redhat.7.Amd64.Open"; + public const string Ubuntu1604Amd64 = "Ubuntu.1604.Amd64.Open"; + public const string Ubuntu1810Amd64 = "Ubuntu.1810.Amd64.Open"; + public const string Windows10Amd64 = "Windows.10.Amd64.ClientRS4.VS2017.Open"; + } + public static partial class HttpClientSlim + { + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.Task GetSocket(System.Uri requestUri) { throw null; } + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.Task GetStringAsync(string requestUri, bool validateCertificate = true) { throw null; } + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.Task GetStringAsync(System.Uri requestUri, bool validateCertificate = true) { throw null; } + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.Task PostAsync(string requestUri, System.Net.Http.HttpContent content, bool validateCertificate = true) { throw null; } + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.Task PostAsync(System.Uri requestUri, System.Net.Http.HttpContent content, bool validateCertificate = true) { throw null; } + } + public partial interface ITestCondition + { + bool IsMet { get; } + string SkipReason { get; } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=true)] + public partial class MinimumOSVersionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition + { + public MinimumOSVersionAttribute(Microsoft.AspNetCore.Testing.OperatingSystems operatingSystem, string minVersion) { } + public bool IsMet { get { throw null; } } + public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + } + [System.FlagsAttribute] + public enum OperatingSystems + { + Linux = 1, + MacOSX = 2, + Windows = 4, + } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=true)] + public partial class OSSkipConditionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition + { + public OSSkipConditionAttribute(Microsoft.AspNetCore.Testing.OperatingSystems operatingSystem, params string[] versions) { } + public bool IsMet { get { throw null; } } + public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Method)] + public partial class ReplaceCultureAttribute : Xunit.Sdk.BeforeAfterTestAttribute + { + public ReplaceCultureAttribute() { } + public ReplaceCultureAttribute(string currentCulture, string currentUICulture) { } + public System.Globalization.CultureInfo Culture { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Globalization.CultureInfo UICulture { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public override void After(System.Reflection.MethodInfo methodUnderTest) { } + public override void Before(System.Reflection.MethodInfo methodUnderTest) { } + } + [System.FlagsAttribute] + public enum RuntimeFrameworks + { + None = 0, + Mono = 1, + CLR = 2, + CoreCLR = 4, + } + [System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=false)] + public partial class SkipOnCIAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition + { + public SkipOnCIAttribute(string issueUrl = "") { } + public bool IsMet { get { throw null; } } + public string IssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string SkipReason { get { throw null; } } + public static string GetIfOnAzdo() { throw null; } + public static string GetTargetHelixQueue() { throw null; } + public static bool OnAzdo() { throw null; } + public static bool OnCI() { throw null; } + public static bool OnHelix() { throw null; } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=false)] + public partial class SkipOnHelixAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition + { + public SkipOnHelixAttribute(string issueUrl) { } + public bool IsMet { get { throw null; } } + public string IssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string Queues { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public string SkipReason { get { throw null; } } + public static string GetTargetHelixQueue() { throw null; } + public static bool OnHelix() { throw null; } + } + public partial class SkippedTestCase : Xunit.Sdk.XunitTestCase + { + [System.ObsoleteAttribute("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")] + public SkippedTestCase() { } + public SkippedTestCase(string skipReason, Xunit.Abstractions.IMessageSink diagnosticMessageSink, Xunit.Sdk.TestMethodDisplay defaultMethodDisplay, Xunit.Sdk.TestMethodDisplayOptions defaultMethodDisplayOptions, Xunit.Abstractions.ITestMethod testMethod, object[] testMethodArguments = null) { } + public override void Deserialize(Xunit.Abstractions.IXunitSerializationInfo data) { } + protected override string GetSkipReason(Xunit.Abstractions.IAttributeInfo factAttribute) { throw null; } + public override void Serialize(Xunit.Abstractions.IXunitSerializationInfo data) { } + } + public static partial class TaskExtensions + { + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.Task TimeoutAfter(this System.Threading.Tasks.Task task, System.TimeSpan timeout, [System.Runtime.CompilerServices.CallerFilePathAttribute]string filePath = null, [System.Runtime.CompilerServices.CallerLineNumberAttribute]int lineNumber = 0) { throw null; } + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.Task TimeoutAfter(this System.Threading.Tasks.Task task, System.TimeSpan timeout, [System.Runtime.CompilerServices.CallerFilePathAttribute]string filePath = null, [System.Runtime.CompilerServices.CallerLineNumberAttribute]int lineNumber = 0) { throw null; } + } + public static partial class TestMethodExtensions + { + public static string EvaluateSkipConditions(this Xunit.Abstractions.ITestMethod testMethod) { throw null; } + } + [System.ObsoleteAttribute("This API is obsolete and the pattern its usage encouraged should not be used anymore. See https://github.com/aspnet/Extensions/issues/1697 for details.")] + public partial class TestPathUtilities + { + public TestPathUtilities() { } + public static string GetRepoRootDirectory() { throw null; } + public static string GetSolutionRootDirectory(string solution) { throw null; } + } + public static partial class TestPlatformHelper + { + public static bool IsLinux { get { throw null; } } + public static bool IsMac { get { throw null; } } + public static bool IsMono { get { throw null; } } + public static bool IsWindows { get { throw null; } } + } + public static partial class WindowsVersions + { + public const string Win10 = "10.0"; + public const string Win2008R2 = "6.1"; + public const string Win7 = "6.1"; + public const string Win8 = "6.2"; + public const string Win81 = "6.3"; + } +} +namespace Microsoft.AspNetCore.Testing.Tracing +{ + public partial class CollectingEventListener : System.Diagnostics.Tracing.EventListener + { + public CollectingEventListener() { } + public void CollectFrom(System.Diagnostics.Tracing.EventSource eventSource) { } + public void CollectFrom(string eventSourceName) { } + public System.Collections.Generic.IReadOnlyList GetEventsWritten() { throw null; } + protected override void OnEventSourceCreated(System.Diagnostics.Tracing.EventSource eventSource) { } + protected override void OnEventWritten(System.Diagnostics.Tracing.EventWrittenEventArgs eventData) { } + } + public partial class EventAssert + { + public EventAssert(int expectedId, string expectedName, System.Diagnostics.Tracing.EventLevel expectedLevel) { } + public static void Collection(System.Collections.Generic.IEnumerable events, params Microsoft.AspNetCore.Testing.Tracing.EventAssert[] asserts) { } + public static Microsoft.AspNetCore.Testing.Tracing.EventAssert Event(int id, string name, System.Diagnostics.Tracing.EventLevel level) { throw null; } + public Microsoft.AspNetCore.Testing.Tracing.EventAssert Payload(string name, System.Action asserter) { throw null; } + public Microsoft.AspNetCore.Testing.Tracing.EventAssert Payload(string name, object expectedValue) { throw null; } + } + [Xunit.CollectionAttribute("Microsoft.AspNetCore.Testing.Tracing.EventSourceTestCollection")] + public abstract partial class EventSourceTestBase : System.IDisposable + { + public const string CollectionName = "Microsoft.AspNetCore.Testing.Tracing.EventSourceTestCollection"; + public EventSourceTestBase() { } + protected void CollectFrom(System.Diagnostics.Tracing.EventSource eventSource) { } + protected void CollectFrom(string eventSourceName) { } + public void Dispose() { } + protected System.Collections.Generic.IReadOnlyList GetEvents() { throw null; } + } +} diff --git a/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs b/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs new file mode 100644 index 000000000000..c90ffb3a0f28 --- /dev/null +++ b/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs @@ -0,0 +1,269 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNetCore.Testing +{ + [System.AttributeUsageAttribute(System.AttributeTargets.Method, AllowMultiple=false)] + [Xunit.Sdk.XunitTestCaseDiscovererAttribute("Microsoft.AspNetCore.Testing.ConditionalFactDiscoverer", "Microsoft.AspNetCore.Testing")] + public partial class ConditionalFactAttribute : Xunit.FactAttribute + { + public ConditionalFactAttribute() { } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Method, AllowMultiple=false)] + [Xunit.Sdk.XunitTestCaseDiscovererAttribute("Microsoft.AspNetCore.Testing.ConditionalTheoryDiscoverer", "Microsoft.AspNetCore.Testing")] + public partial class ConditionalTheoryAttribute : Xunit.TheoryAttribute + { + public ConditionalTheoryAttribute() { } + } + public partial class CultureReplacer : System.IDisposable + { + public CultureReplacer(System.Globalization.CultureInfo culture, System.Globalization.CultureInfo uiCulture) { } + public CultureReplacer(string culture = "en-GB", string uiCulture = "en-US") { } + public static System.Globalization.CultureInfo DefaultCulture { get { throw null; } } + public static string DefaultCultureName { get { throw null; } } + public static string DefaultUICultureName { get { throw null; } } + public void Dispose() { } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Method, Inherited=true, AllowMultiple=false)] + public sealed partial class DockerOnlyAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition + { + public DockerOnlyAttribute() { } + public bool IsMet { get { throw null; } } + public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=true)] + public partial class EnvironmentVariableSkipConditionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition + { + public EnvironmentVariableSkipConditionAttribute(string variableName, params string[] values) { } + public bool IsMet { get { throw null; } } + public bool RunOnMatch { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public string SkipReason { get { throw null; } } + } + public static partial class ExceptionAssert + { + public static System.ArgumentException ThrowsArgument(System.Action testCode, string paramName, string exceptionMessage) { throw null; } + public static System.Threading.Tasks.Task ThrowsArgumentAsync(System.Func testCode, string paramName, string exceptionMessage) { throw null; } + public static System.ArgumentNullException ThrowsArgumentNull(System.Action testCode, string paramName) { throw null; } + public static System.ArgumentException ThrowsArgumentNullOrEmpty(System.Action testCode, string paramName) { throw null; } + public static System.Threading.Tasks.Task ThrowsArgumentNullOrEmptyAsync(System.Func testCode, string paramName) { throw null; } + public static System.ArgumentException ThrowsArgumentNullOrEmptyString(System.Action testCode, string paramName) { throw null; } + public static System.Threading.Tasks.Task ThrowsArgumentNullOrEmptyStringAsync(System.Func testCode, string paramName) { throw null; } + public static System.ArgumentOutOfRangeException ThrowsArgumentOutOfRange(System.Action testCode, string paramName, string exceptionMessage, object actualValue = null) { throw null; } + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.Task ThrowsAsync(System.Func testCode, string exceptionMessage) where TException : System.Exception { throw null; } + public static TException Throws(System.Action testCode) where TException : System.Exception { throw null; } + public static TException Throws(System.Action testCode, string exceptionMessage) where TException : System.Exception { throw null; } + public static TException Throws(System.Func testCode, string exceptionMessage) where TException : System.Exception { throw null; } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method)] + [Xunit.Sdk.TraitDiscovererAttribute("Microsoft.AspNetCore.Testing.FlakyTestDiscoverer", "Microsoft.AspNetCore.Testing")] + public sealed partial class FlakyAttribute : System.Attribute, Xunit.Sdk.ITraitAttribute + { + public FlakyAttribute(string gitHubIssueUrl, string firstFilter, params string[] additionalFilters) { } + public System.Collections.Generic.IReadOnlyList Filters { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string GitHubIssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } + public static partial class FlakyOn + { + public const string All = "All"; + public static partial class AzP + { + public const string All = "AzP:All"; + public const string Linux = "AzP:OS:Linux"; + public const string macOS = "AzP:OS:Darwin"; + public const string Windows = "AzP:OS:Windows_NT"; + } + public static partial class Helix + { + public const string All = "Helix:Queue:All"; + public const string Centos7Amd64 = "Helix:Queue:Centos.7.Amd64.Open"; + public const string Debian8Amd64 = "Helix:Queue:Debian.8.Amd64.Open"; + public const string Debian9Amd64 = "Helix:Queue:Debian.9.Amd64.Open"; + public const string Fedora27Amd64 = "Helix:Queue:Fedora.27.Amd64.Open"; + public const string Fedora28Amd64 = "Helix:Queue:Fedora.28.Amd64.Open"; + public const string macOS1012Amd64 = "Helix:Queue:OSX.1012.Amd64.Open"; + public const string Redhat7Amd64 = "Helix:Queue:Redhat.7.Amd64.Open"; + public const string Ubuntu1604Amd64 = "Helix:Queue:Ubuntu.1604.Amd64.Open"; + public const string Ubuntu1810Amd64 = "Helix:Queue:Ubuntu.1810.Amd64.Open"; + public const string Windows10Amd64 = "Helix:Queue:Windows.10.Amd64.ClientRS4.VS2017.Open"; + } + } + public partial class FlakyTestDiscoverer : Xunit.Sdk.ITraitDiscoverer + { + public FlakyTestDiscoverer() { } + public System.Collections.Generic.IEnumerable> GetTraits(Xunit.Abstractions.IAttributeInfo traitAttribute) { throw null; } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Method, AllowMultiple=false)] + public partial class FrameworkSkipConditionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition + { + public FrameworkSkipConditionAttribute(Microsoft.AspNetCore.Testing.RuntimeFrameworks excludedFrameworks) { } + public bool IsMet { get { throw null; } } + public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + } + public static partial class HelixQueues + { + public const string Centos7Amd64 = "Centos.7.Amd64.Open"; + public const string Debian8Amd64 = "Debian.8.Amd64.Open"; + public const string Debian9Amd64 = "Debian.9.Amd64.Open"; + public const string Fedora27Amd64 = "Fedora.27.Amd64.Open"; + public const string Fedora28Amd64 = "Fedora.28.Amd64.Open"; + public const string macOS1012Amd64 = "OSX.1012.Amd64.Open"; + public const string Redhat7Amd64 = "Redhat.7.Amd64.Open"; + public const string Ubuntu1604Amd64 = "Ubuntu.1604.Amd64.Open"; + public const string Ubuntu1810Amd64 = "Ubuntu.1810.Amd64.Open"; + public const string Windows10Amd64 = "Windows.10.Amd64.ClientRS4.VS2017.Open"; + } + public static partial class HttpClientSlim + { + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.Task GetSocket(System.Uri requestUri) { throw null; } + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.Task GetStringAsync(string requestUri, bool validateCertificate = true) { throw null; } + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.Task GetStringAsync(System.Uri requestUri, bool validateCertificate = true) { throw null; } + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.Task PostAsync(string requestUri, System.Net.Http.HttpContent content, bool validateCertificate = true) { throw null; } + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.Task PostAsync(System.Uri requestUri, System.Net.Http.HttpContent content, bool validateCertificate = true) { throw null; } + } + public partial interface ITestCondition + { + bool IsMet { get; } + string SkipReason { get; } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=true)] + public partial class MinimumOSVersionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition + { + public MinimumOSVersionAttribute(Microsoft.AspNetCore.Testing.OperatingSystems operatingSystem, string minVersion) { } + public bool IsMet { get { throw null; } } + public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + } + [System.FlagsAttribute] + public enum OperatingSystems + { + Linux = 1, + MacOSX = 2, + Windows = 4, + } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=true)] + public partial class OSSkipConditionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition + { + public OSSkipConditionAttribute(Microsoft.AspNetCore.Testing.OperatingSystems operatingSystem, params string[] versions) { } + public bool IsMet { get { throw null; } } + public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Method)] + public partial class ReplaceCultureAttribute : Xunit.Sdk.BeforeAfterTestAttribute + { + public ReplaceCultureAttribute() { } + public ReplaceCultureAttribute(string currentCulture, string currentUICulture) { } + public System.Globalization.CultureInfo Culture { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Globalization.CultureInfo UICulture { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public override void After(System.Reflection.MethodInfo methodUnderTest) { } + public override void Before(System.Reflection.MethodInfo methodUnderTest) { } + } + [System.FlagsAttribute] + public enum RuntimeFrameworks + { + None = 0, + Mono = 1, + CLR = 2, + CoreCLR = 4, + } + [System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=false)] + public partial class SkipOnCIAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition + { + public SkipOnCIAttribute(string issueUrl = "") { } + public bool IsMet { get { throw null; } } + public string IssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string SkipReason { get { throw null; } } + public static string GetIfOnAzdo() { throw null; } + public static string GetTargetHelixQueue() { throw null; } + public static bool OnAzdo() { throw null; } + public static bool OnCI() { throw null; } + public static bool OnHelix() { throw null; } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=false)] + public partial class SkipOnHelixAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition + { + public SkipOnHelixAttribute(string issueUrl) { } + public bool IsMet { get { throw null; } } + public string IssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string Queues { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public string SkipReason { get { throw null; } } + public static string GetTargetHelixQueue() { throw null; } + public static bool OnHelix() { throw null; } + } + public partial class SkippedTestCase : Xunit.Sdk.XunitTestCase + { + [System.ObsoleteAttribute("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")] + public SkippedTestCase() { } + public SkippedTestCase(string skipReason, Xunit.Abstractions.IMessageSink diagnosticMessageSink, Xunit.Sdk.TestMethodDisplay defaultMethodDisplay, Xunit.Sdk.TestMethodDisplayOptions defaultMethodDisplayOptions, Xunit.Abstractions.ITestMethod testMethod, object[] testMethodArguments = null) { } + public override void Deserialize(Xunit.Abstractions.IXunitSerializationInfo data) { } + protected override string GetSkipReason(Xunit.Abstractions.IAttributeInfo factAttribute) { throw null; } + public override void Serialize(Xunit.Abstractions.IXunitSerializationInfo data) { } + } + public static partial class TaskExtensions + { + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.Task TimeoutAfter(this System.Threading.Tasks.Task task, System.TimeSpan timeout, [System.Runtime.CompilerServices.CallerFilePathAttribute]string filePath = null, [System.Runtime.CompilerServices.CallerLineNumberAttribute]int lineNumber = 0) { throw null; } + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.Task TimeoutAfter(this System.Threading.Tasks.Task task, System.TimeSpan timeout, [System.Runtime.CompilerServices.CallerFilePathAttribute]string filePath = null, [System.Runtime.CompilerServices.CallerLineNumberAttribute]int lineNumber = 0) { throw null; } + } + public static partial class TestMethodExtensions + { + public static string EvaluateSkipConditions(this Xunit.Abstractions.ITestMethod testMethod) { throw null; } + } + [System.ObsoleteAttribute("This API is obsolete and the pattern its usage encouraged should not be used anymore. See https://github.com/aspnet/Extensions/issues/1697 for details.")] + public partial class TestPathUtilities + { + public TestPathUtilities() { } + public static string GetRepoRootDirectory() { throw null; } + public static string GetSolutionRootDirectory(string solution) { throw null; } + } + public static partial class TestPlatformHelper + { + public static bool IsLinux { get { throw null; } } + public static bool IsMac { get { throw null; } } + public static bool IsMono { get { throw null; } } + public static bool IsWindows { get { throw null; } } + } + public static partial class WindowsVersions + { + public const string Win10 = "10.0"; + public const string Win2008R2 = "6.1"; + public const string Win7 = "6.1"; + public const string Win8 = "6.2"; + public const string Win81 = "6.3"; + } +} +namespace Microsoft.AspNetCore.Testing.Tracing +{ + public partial class CollectingEventListener : System.Diagnostics.Tracing.EventListener + { + public CollectingEventListener() { } + public void CollectFrom(System.Diagnostics.Tracing.EventSource eventSource) { } + public void CollectFrom(string eventSourceName) { } + public System.Collections.Generic.IReadOnlyList GetEventsWritten() { throw null; } + protected override void OnEventSourceCreated(System.Diagnostics.Tracing.EventSource eventSource) { } + protected override void OnEventWritten(System.Diagnostics.Tracing.EventWrittenEventArgs eventData) { } + } + public partial class EventAssert + { + public EventAssert(int expectedId, string expectedName, System.Diagnostics.Tracing.EventLevel expectedLevel) { } + public static void Collection(System.Collections.Generic.IEnumerable events, params Microsoft.AspNetCore.Testing.Tracing.EventAssert[] asserts) { } + public static Microsoft.AspNetCore.Testing.Tracing.EventAssert Event(int id, string name, System.Diagnostics.Tracing.EventLevel level) { throw null; } + public Microsoft.AspNetCore.Testing.Tracing.EventAssert Payload(string name, System.Action asserter) { throw null; } + public Microsoft.AspNetCore.Testing.Tracing.EventAssert Payload(string name, object expectedValue) { throw null; } + } + [Xunit.CollectionAttribute("Microsoft.AspNetCore.Testing.Tracing.EventSourceTestCollection")] + public abstract partial class EventSourceTestBase : System.IDisposable + { + public const string CollectionName = "Microsoft.AspNetCore.Testing.Tracing.EventSourceTestCollection"; + public EventSourceTestBase() { } + protected void CollectFrom(System.Diagnostics.Tracing.EventSource eventSource) { } + protected void CollectFrom(string eventSourceName) { } + public void Dispose() { } + protected System.Collections.Generic.IReadOnlyList GetEvents() { throw null; } + } +} diff --git a/src/Testing/src/Microsoft.AspNetCore.Testing.csproj b/src/Testing/src/Microsoft.AspNetCore.Testing.csproj index 9da267c4198a..70ac622461ac 100644 --- a/src/Testing/src/Microsoft.AspNetCore.Testing.csproj +++ b/src/Testing/src/Microsoft.AspNetCore.Testing.csproj @@ -10,6 +10,8 @@ false true false + true + true true From 7364cfcc56892f27d4563c326c22d3fc04712909 Mon Sep 17 00:00:00 2001 From: Doug Bunting <6431421+dougbu@users.noreply.github.com> Date: Sun, 13 Oct 2019 21:15:18 -0700 Subject: [PATCH 154/183] Regen Microsoft.AspNetCore.Testing ref/ code - account for xUnit refactoring in 'release/3.1' \n\nCommit migrated from https://github.com/dotnet/extensions/commit/c6cacd71619f0e81403e34218964b256da42bbcc --- .../ref/Microsoft.AspNetCore.Testing.net46.cs | 97 ++++++++++++++++++- ...osoft.AspNetCore.Testing.netstandard2.0.cs | 97 ++++++++++++++++++- 2 files changed, 188 insertions(+), 6 deletions(-) diff --git a/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs b/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs index c90ffb3a0f28..a7b9c07a9968 100644 --- a/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs +++ b/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs @@ -3,6 +3,39 @@ namespace Microsoft.AspNetCore.Testing { + public partial class AspNetTestAssemblyRunner : Xunit.Sdk.XunitTestAssemblyRunner + { + public AspNetTestAssemblyRunner(Xunit.Abstractions.ITestAssembly testAssembly, System.Collections.Generic.IEnumerable testCases, Xunit.Abstractions.IMessageSink diagnosticMessageSink, Xunit.Abstractions.IMessageSink executionMessageSink, Xunit.Abstractions.ITestFrameworkExecutionOptions executionOptions) : base (default(Xunit.Abstractions.ITestAssembly), default(System.Collections.Generic.IEnumerable), default(Xunit.Abstractions.IMessageSink), default(Xunit.Abstractions.IMessageSink), default(Xunit.Abstractions.ITestFrameworkExecutionOptions)) { } + [System.Diagnostics.DebuggerStepThroughAttribute] + protected override System.Threading.Tasks.Task AfterTestAssemblyStartingAsync() { throw null; } + protected override System.Threading.Tasks.Task BeforeTestAssemblyFinishedAsync() { throw null; } + protected override System.Threading.Tasks.Task RunTestCollectionAsync(Xunit.Sdk.IMessageBus messageBus, Xunit.Abstractions.ITestCollection testCollection, System.Collections.Generic.IEnumerable testCases, System.Threading.CancellationTokenSource cancellationTokenSource) { throw null; } + } + public partial class AspNetTestCollectionRunner : Xunit.Sdk.XunitTestCollectionRunner + { + public AspNetTestCollectionRunner(System.Collections.Generic.Dictionary assemblyFixtureMappings, Xunit.Abstractions.ITestCollection testCollection, System.Collections.Generic.IEnumerable testCases, Xunit.Abstractions.IMessageSink diagnosticMessageSink, Xunit.Sdk.IMessageBus messageBus, Xunit.Sdk.ITestCaseOrderer testCaseOrderer, Xunit.Sdk.ExceptionAggregator aggregator, System.Threading.CancellationTokenSource cancellationTokenSource) : base (default(Xunit.Abstractions.ITestCollection), default(System.Collections.Generic.IEnumerable), default(Xunit.Abstractions.IMessageSink), default(Xunit.Sdk.IMessageBus), default(Xunit.Sdk.ITestCaseOrderer), default(Xunit.Sdk.ExceptionAggregator), default(System.Threading.CancellationTokenSource)) { } + [System.Diagnostics.DebuggerStepThroughAttribute] + protected override System.Threading.Tasks.Task AfterTestCollectionStartingAsync() { throw null; } + protected override System.Threading.Tasks.Task BeforeTestCollectionFinishedAsync() { throw null; } + protected override System.Threading.Tasks.Task RunTestClassAsync(Xunit.Abstractions.ITestClass testClass, Xunit.Abstractions.IReflectionTypeInfo @class, System.Collections.Generic.IEnumerable testCases) { throw null; } + } + public partial class AspNetTestFramework : Xunit.Sdk.XunitTestFramework + { + public AspNetTestFramework(Xunit.Abstractions.IMessageSink messageSink) : base (default(Xunit.Abstractions.IMessageSink)) { } + protected override Xunit.Abstractions.ITestFrameworkExecutor CreateExecutor(System.Reflection.AssemblyName assemblyName) { throw null; } + } + public partial class AspNetTestFrameworkExecutor : Xunit.Sdk.XunitTestFrameworkExecutor + { + public AspNetTestFrameworkExecutor(System.Reflection.AssemblyName assemblyName, Xunit.Abstractions.ISourceInformationProvider sourceInformationProvider, Xunit.Abstractions.IMessageSink diagnosticMessageSink) : base (default(System.Reflection.AssemblyName), default(Xunit.Abstractions.ISourceInformationProvider), default(Xunit.Abstractions.IMessageSink)) { } + [System.Diagnostics.DebuggerStepThroughAttribute] + protected override void RunTestCases(System.Collections.Generic.IEnumerable testCases, Xunit.Abstractions.IMessageSink executionMessageSink, Xunit.Abstractions.ITestFrameworkExecutionOptions executionOptions) { } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly, AllowMultiple=true)] + public partial class AssemblyFixtureAttribute : System.Attribute + { + public AssemblyFixtureAttribute(System.Type fixtureType) { } + public System.Type FixtureType { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } [System.AttributeUsageAttribute(System.AttributeTargets.Method, AllowMultiple=false)] [Xunit.Sdk.XunitTestCaseDiscovererAttribute("Microsoft.AspNetCore.Testing.ConditionalFactDiscoverer", "Microsoft.AspNetCore.Testing")] public partial class ConditionalFactAttribute : Xunit.FactAttribute @@ -56,7 +89,7 @@ public static partial class ExceptionAssert public static TException Throws(System.Func testCode, string exceptionMessage) where TException : System.Exception { throw null; } } [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method)] - [Xunit.Sdk.TraitDiscovererAttribute("Microsoft.AspNetCore.Testing.FlakyTestDiscoverer", "Microsoft.AspNetCore.Testing")] + [Xunit.Sdk.TraitDiscovererAttribute("Microsoft.AspNetCore.Testing.FlakyTraitDiscoverer", "Microsoft.AspNetCore.Testing")] public sealed partial class FlakyAttribute : System.Attribute, Xunit.Sdk.ITraitAttribute { public FlakyAttribute(string gitHubIssueUrl, string firstFilter, params string[] additionalFilters) { } @@ -88,9 +121,9 @@ public static partial class Helix public const string Windows10Amd64 = "Helix:Queue:Windows.10.Amd64.ClientRS4.VS2017.Open"; } } - public partial class FlakyTestDiscoverer : Xunit.Sdk.ITraitDiscoverer + public partial class FlakyTraitDiscoverer : Xunit.Sdk.ITraitDiscoverer { - public FlakyTestDiscoverer() { } + public FlakyTraitDiscoverer() { } public System.Collections.Generic.IEnumerable> GetTraits(Xunit.Abstractions.IAttributeInfo traitAttribute) { throw null; } } [System.AttributeUsageAttribute(System.AttributeTargets.Method, AllowMultiple=false)] @@ -131,6 +164,11 @@ public partial interface ITestCondition bool IsMet { get; } string SkipReason { get; } } + public partial interface ITestMethodLifecycle + { + System.Threading.Tasks.Task OnTestEndAsync(Microsoft.AspNetCore.Testing.TestContext context, System.Exception exception, System.Threading.CancellationToken cancellationToken); + System.Threading.Tasks.Task OnTestStartAsync(Microsoft.AspNetCore.Testing.TestContext context, System.Threading.CancellationToken cancellationToken); + } [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=true)] public partial class MinimumOSVersionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition { @@ -152,6 +190,20 @@ public OSSkipConditionAttribute(Microsoft.AspNetCore.Testing.OperatingSystems op public bool IsMet { get { throw null; } } public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=false)] + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public partial class RepeatAttribute : System.Attribute + { + public RepeatAttribute(int runCount = 10) { } + public int RunCount { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } + public partial class RepeatContext + { + public RepeatContext(int limit) { } + public static Microsoft.AspNetCore.Testing.RepeatContext Current { get { throw null; } } + public int CurrentIteration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public int Limit { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } [System.AttributeUsageAttribute(System.AttributeTargets.Method)] public partial class ReplaceCultureAttribute : Xunit.Sdk.BeforeAfterTestAttribute { @@ -170,6 +222,11 @@ public enum RuntimeFrameworks CLR = 2, CoreCLR = 4, } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class, AllowMultiple=false)] + public partial class ShortClassNameAttribute : System.Attribute + { + public ShortClassNameAttribute() { } + } [System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=false)] public partial class SkipOnCIAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition { @@ -210,10 +267,41 @@ public static partial class TaskExtensions [System.Diagnostics.DebuggerStepThroughAttribute] public static System.Threading.Tasks.Task TimeoutAfter(this System.Threading.Tasks.Task task, System.TimeSpan timeout, [System.Runtime.CompilerServices.CallerFilePathAttribute]string filePath = null, [System.Runtime.CompilerServices.CallerLineNumberAttribute]int lineNumber = 0) { throw null; } } + public sealed partial class TestContext + { + public TestContext(System.Type testClass, object[] constructorArguments, System.Reflection.MethodInfo testMethod, object[] methodArguments, Xunit.Abstractions.ITestOutputHelper output) { } + public object[] ConstructorArguments { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public Microsoft.AspNetCore.Testing.TestFileOutputContext FileOutput { get { throw null; } } + public object[] MethodArguments { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public Xunit.Abstractions.ITestOutputHelper Output { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Type TestClass { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Reflection.MethodInfo TestMethod { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } + public sealed partial class TestFileOutputContext + { + public TestFileOutputContext(Microsoft.AspNetCore.Testing.TestContext parent) { } + public string AssemblyOutputDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string TestClassName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string TestClassOutputDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string TestName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public static string GetAssemblyBaseDirectory(System.Reflection.Assembly assembly, string baseDirectory = null) { throw null; } + public static string GetOutputDirectory(System.Reflection.Assembly assembly) { throw null; } + public static string GetTestClassName(System.Type type) { throw null; } + public static string GetTestMethodName(System.Reflection.MethodInfo method, object[] arguments) { throw null; } + public string GetUniqueFileName(string prefix, string extension) { throw null; } + public static string RemoveIllegalFileChars(string s) { throw null; } + } public static partial class TestMethodExtensions { public static string EvaluateSkipConditions(this Xunit.Abstractions.ITestMethod testMethod) { throw null; } } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly, AllowMultiple=false, Inherited=true)] + public partial class TestOutputDirectoryAttribute : System.Attribute + { + public TestOutputDirectoryAttribute(string targetFramework, string baseDirectory = null) { } + public string BaseDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string TargetFramework { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } [System.ObsoleteAttribute("This API is obsolete and the pattern its usage encouraged should not be used anymore. See https://github.com/aspnet/Extensions/issues/1697 for details.")] public partial class TestPathUtilities { @@ -231,6 +319,9 @@ public static partial class TestPlatformHelper public static partial class WindowsVersions { public const string Win10 = "10.0"; + public const string Win10_19H2 = "10.0.18363"; + public const string Win10_20H1 = "10.0.18990"; + public const string Win10_RS4 = "10.0.17134"; public const string Win2008R2 = "6.1"; public const string Win7 = "6.1"; public const string Win8 = "6.2"; diff --git a/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs b/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs index c90ffb3a0f28..a7b9c07a9968 100644 --- a/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs +++ b/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs @@ -3,6 +3,39 @@ namespace Microsoft.AspNetCore.Testing { + public partial class AspNetTestAssemblyRunner : Xunit.Sdk.XunitTestAssemblyRunner + { + public AspNetTestAssemblyRunner(Xunit.Abstractions.ITestAssembly testAssembly, System.Collections.Generic.IEnumerable testCases, Xunit.Abstractions.IMessageSink diagnosticMessageSink, Xunit.Abstractions.IMessageSink executionMessageSink, Xunit.Abstractions.ITestFrameworkExecutionOptions executionOptions) : base (default(Xunit.Abstractions.ITestAssembly), default(System.Collections.Generic.IEnumerable), default(Xunit.Abstractions.IMessageSink), default(Xunit.Abstractions.IMessageSink), default(Xunit.Abstractions.ITestFrameworkExecutionOptions)) { } + [System.Diagnostics.DebuggerStepThroughAttribute] + protected override System.Threading.Tasks.Task AfterTestAssemblyStartingAsync() { throw null; } + protected override System.Threading.Tasks.Task BeforeTestAssemblyFinishedAsync() { throw null; } + protected override System.Threading.Tasks.Task RunTestCollectionAsync(Xunit.Sdk.IMessageBus messageBus, Xunit.Abstractions.ITestCollection testCollection, System.Collections.Generic.IEnumerable testCases, System.Threading.CancellationTokenSource cancellationTokenSource) { throw null; } + } + public partial class AspNetTestCollectionRunner : Xunit.Sdk.XunitTestCollectionRunner + { + public AspNetTestCollectionRunner(System.Collections.Generic.Dictionary assemblyFixtureMappings, Xunit.Abstractions.ITestCollection testCollection, System.Collections.Generic.IEnumerable testCases, Xunit.Abstractions.IMessageSink diagnosticMessageSink, Xunit.Sdk.IMessageBus messageBus, Xunit.Sdk.ITestCaseOrderer testCaseOrderer, Xunit.Sdk.ExceptionAggregator aggregator, System.Threading.CancellationTokenSource cancellationTokenSource) : base (default(Xunit.Abstractions.ITestCollection), default(System.Collections.Generic.IEnumerable), default(Xunit.Abstractions.IMessageSink), default(Xunit.Sdk.IMessageBus), default(Xunit.Sdk.ITestCaseOrderer), default(Xunit.Sdk.ExceptionAggregator), default(System.Threading.CancellationTokenSource)) { } + [System.Diagnostics.DebuggerStepThroughAttribute] + protected override System.Threading.Tasks.Task AfterTestCollectionStartingAsync() { throw null; } + protected override System.Threading.Tasks.Task BeforeTestCollectionFinishedAsync() { throw null; } + protected override System.Threading.Tasks.Task RunTestClassAsync(Xunit.Abstractions.ITestClass testClass, Xunit.Abstractions.IReflectionTypeInfo @class, System.Collections.Generic.IEnumerable testCases) { throw null; } + } + public partial class AspNetTestFramework : Xunit.Sdk.XunitTestFramework + { + public AspNetTestFramework(Xunit.Abstractions.IMessageSink messageSink) : base (default(Xunit.Abstractions.IMessageSink)) { } + protected override Xunit.Abstractions.ITestFrameworkExecutor CreateExecutor(System.Reflection.AssemblyName assemblyName) { throw null; } + } + public partial class AspNetTestFrameworkExecutor : Xunit.Sdk.XunitTestFrameworkExecutor + { + public AspNetTestFrameworkExecutor(System.Reflection.AssemblyName assemblyName, Xunit.Abstractions.ISourceInformationProvider sourceInformationProvider, Xunit.Abstractions.IMessageSink diagnosticMessageSink) : base (default(System.Reflection.AssemblyName), default(Xunit.Abstractions.ISourceInformationProvider), default(Xunit.Abstractions.IMessageSink)) { } + [System.Diagnostics.DebuggerStepThroughAttribute] + protected override void RunTestCases(System.Collections.Generic.IEnumerable testCases, Xunit.Abstractions.IMessageSink executionMessageSink, Xunit.Abstractions.ITestFrameworkExecutionOptions executionOptions) { } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly, AllowMultiple=true)] + public partial class AssemblyFixtureAttribute : System.Attribute + { + public AssemblyFixtureAttribute(System.Type fixtureType) { } + public System.Type FixtureType { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } [System.AttributeUsageAttribute(System.AttributeTargets.Method, AllowMultiple=false)] [Xunit.Sdk.XunitTestCaseDiscovererAttribute("Microsoft.AspNetCore.Testing.ConditionalFactDiscoverer", "Microsoft.AspNetCore.Testing")] public partial class ConditionalFactAttribute : Xunit.FactAttribute @@ -56,7 +89,7 @@ public static partial class ExceptionAssert public static TException Throws(System.Func testCode, string exceptionMessage) where TException : System.Exception { throw null; } } [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method)] - [Xunit.Sdk.TraitDiscovererAttribute("Microsoft.AspNetCore.Testing.FlakyTestDiscoverer", "Microsoft.AspNetCore.Testing")] + [Xunit.Sdk.TraitDiscovererAttribute("Microsoft.AspNetCore.Testing.FlakyTraitDiscoverer", "Microsoft.AspNetCore.Testing")] public sealed partial class FlakyAttribute : System.Attribute, Xunit.Sdk.ITraitAttribute { public FlakyAttribute(string gitHubIssueUrl, string firstFilter, params string[] additionalFilters) { } @@ -88,9 +121,9 @@ public static partial class Helix public const string Windows10Amd64 = "Helix:Queue:Windows.10.Amd64.ClientRS4.VS2017.Open"; } } - public partial class FlakyTestDiscoverer : Xunit.Sdk.ITraitDiscoverer + public partial class FlakyTraitDiscoverer : Xunit.Sdk.ITraitDiscoverer { - public FlakyTestDiscoverer() { } + public FlakyTraitDiscoverer() { } public System.Collections.Generic.IEnumerable> GetTraits(Xunit.Abstractions.IAttributeInfo traitAttribute) { throw null; } } [System.AttributeUsageAttribute(System.AttributeTargets.Method, AllowMultiple=false)] @@ -131,6 +164,11 @@ public partial interface ITestCondition bool IsMet { get; } string SkipReason { get; } } + public partial interface ITestMethodLifecycle + { + System.Threading.Tasks.Task OnTestEndAsync(Microsoft.AspNetCore.Testing.TestContext context, System.Exception exception, System.Threading.CancellationToken cancellationToken); + System.Threading.Tasks.Task OnTestStartAsync(Microsoft.AspNetCore.Testing.TestContext context, System.Threading.CancellationToken cancellationToken); + } [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=true)] public partial class MinimumOSVersionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition { @@ -152,6 +190,20 @@ public OSSkipConditionAttribute(Microsoft.AspNetCore.Testing.OperatingSystems op public bool IsMet { get { throw null; } } public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=false)] + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public partial class RepeatAttribute : System.Attribute + { + public RepeatAttribute(int runCount = 10) { } + public int RunCount { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } + public partial class RepeatContext + { + public RepeatContext(int limit) { } + public static Microsoft.AspNetCore.Testing.RepeatContext Current { get { throw null; } } + public int CurrentIteration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public int Limit { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } [System.AttributeUsageAttribute(System.AttributeTargets.Method)] public partial class ReplaceCultureAttribute : Xunit.Sdk.BeforeAfterTestAttribute { @@ -170,6 +222,11 @@ public enum RuntimeFrameworks CLR = 2, CoreCLR = 4, } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class, AllowMultiple=false)] + public partial class ShortClassNameAttribute : System.Attribute + { + public ShortClassNameAttribute() { } + } [System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=false)] public partial class SkipOnCIAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition { @@ -210,10 +267,41 @@ public static partial class TaskExtensions [System.Diagnostics.DebuggerStepThroughAttribute] public static System.Threading.Tasks.Task TimeoutAfter(this System.Threading.Tasks.Task task, System.TimeSpan timeout, [System.Runtime.CompilerServices.CallerFilePathAttribute]string filePath = null, [System.Runtime.CompilerServices.CallerLineNumberAttribute]int lineNumber = 0) { throw null; } } + public sealed partial class TestContext + { + public TestContext(System.Type testClass, object[] constructorArguments, System.Reflection.MethodInfo testMethod, object[] methodArguments, Xunit.Abstractions.ITestOutputHelper output) { } + public object[] ConstructorArguments { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public Microsoft.AspNetCore.Testing.TestFileOutputContext FileOutput { get { throw null; } } + public object[] MethodArguments { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public Xunit.Abstractions.ITestOutputHelper Output { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Type TestClass { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Reflection.MethodInfo TestMethod { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } + public sealed partial class TestFileOutputContext + { + public TestFileOutputContext(Microsoft.AspNetCore.Testing.TestContext parent) { } + public string AssemblyOutputDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string TestClassName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string TestClassOutputDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string TestName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public static string GetAssemblyBaseDirectory(System.Reflection.Assembly assembly, string baseDirectory = null) { throw null; } + public static string GetOutputDirectory(System.Reflection.Assembly assembly) { throw null; } + public static string GetTestClassName(System.Type type) { throw null; } + public static string GetTestMethodName(System.Reflection.MethodInfo method, object[] arguments) { throw null; } + public string GetUniqueFileName(string prefix, string extension) { throw null; } + public static string RemoveIllegalFileChars(string s) { throw null; } + } public static partial class TestMethodExtensions { public static string EvaluateSkipConditions(this Xunit.Abstractions.ITestMethod testMethod) { throw null; } } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly, AllowMultiple=false, Inherited=true)] + public partial class TestOutputDirectoryAttribute : System.Attribute + { + public TestOutputDirectoryAttribute(string targetFramework, string baseDirectory = null) { } + public string BaseDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string TargetFramework { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } [System.ObsoleteAttribute("This API is obsolete and the pattern its usage encouraged should not be used anymore. See https://github.com/aspnet/Extensions/issues/1697 for details.")] public partial class TestPathUtilities { @@ -231,6 +319,9 @@ public static partial class TestPlatformHelper public static partial class WindowsVersions { public const string Win10 = "10.0"; + public const string Win10_19H2 = "10.0.18363"; + public const string Win10_20H1 = "10.0.18990"; + public const string Win10_RS4 = "10.0.17134"; public const string Win2008R2 = "6.1"; public const string Win7 = "6.1"; public const string Win8 = "6.2"; From fe782681b489443cfb52d9f04daf173008f495de Mon Sep 17 00:00:00 2001 From: Pranav K Date: Mon, 14 Oct 2019 15:36:50 -0700 Subject: [PATCH 155/183] Make WASM packages stay preview releases in 3.1. (dotnet/extensions#2503) Fixes https://github.com/aspnet/AspNetCore/issues/14623\n\nCommit migrated from https://github.com/dotnet/extensions/commit/b195e40af561c7505fb086e511d33ba8314a8340 --- .../Mono.WebAssembly.Interop/Directory.Build.props | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/JSInterop/Mono.WebAssembly.Interop/Directory.Build.props diff --git a/src/JSInterop/Mono.WebAssembly.Interop/Directory.Build.props b/src/JSInterop/Mono.WebAssembly.Interop/Directory.Build.props new file mode 100644 index 000000000000..ce32dcd999e5 --- /dev/null +++ b/src/JSInterop/Mono.WebAssembly.Interop/Directory.Build.props @@ -0,0 +1,9 @@ + + + + + + $(BlazorWasmPreReleaseVersionLabel) + + + From 29d2fd4576675f4595937c7d98ca001a906ccffb Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Thu, 31 Oct 2019 22:47:08 +0000 Subject: [PATCH 156/183] [master] Update dependencies from dotnet/core-setup dotnet/arcade (dotnet/extensions#2559) * Update dependencies from https://github.com/dotnet/arcade build 20191023.3 - Microsoft.DotNet.Arcade.Sdk - 5.0.0-beta.19523.3 - Microsoft.DotNet.GenAPI - 5.0.0-beta.19523.3 - Microsoft.DotNet.Helix.Sdk - 5.0.0-beta.19523.3 * Updated ref asssemblies * Update dependencies from https://github.com/dotnet/core-setup build 20191024.4 - Microsoft.NETCore.App.Ref - 5.0.0-alpha1.19524.4 - NETStandard.Library.Ref - 2.1.0-alpha1.19524.4 - Microsoft.NETCore.App.Runtime.win-x64 - 5.0.0-alpha1.19524.4 Dependency coherency updates - Microsoft.Win32.Registry - 5.0.0-alpha.1.19524.14 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.ComponentModel.Annotations - 5.0.0-alpha.1.19524.14 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Diagnostics.DiagnosticSource - 5.0.0-alpha.1.19524.14 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Diagnostics.EventLog - 5.0.0-alpha.1.19524.14 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.IO.Pipelines - 5.0.0-alpha.1.19524.14 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Reflection.Metadata - 5.0.0-alpha.1.19524.14 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Runtime.CompilerServices.Unsafe - 5.0.0-alpha.1.19524.14 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Security.Cryptography.Cng - 5.0.0-alpha.1.19524.14 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Security.Cryptography.Xml - 5.0.0-alpha.1.19524.14 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.ServiceProcess.ServiceController - 5.0.0-alpha.1.19524.14 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Text.Encodings.Web - 5.0.0-alpha.1.19524.14 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Text.Json - 5.0.0-alpha.1.19524.14 (parent: Microsoft.NETCore.App.Runtime.win-x64) - Microsoft.NETCore.Platforms - 5.0.0-alpha.1.19524.14 (parent: Microsoft.NETCore.App.Runtime.win-x64) * Update dependencies from https://github.com/dotnet/core-setup build 20191025.8 - Microsoft.NETCore.App.Ref - 5.0.0-alpha1.19525.8 - NETStandard.Library.Ref - 2.1.0-alpha1.19525.8 - Microsoft.NETCore.App.Runtime.win-x64 - 5.0.0-alpha1.19525.8 Dependency coherency updates - Microsoft.Win32.Registry - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.ComponentModel.Annotations - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Diagnostics.DiagnosticSource - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Diagnostics.EventLog - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.IO.Pipelines - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Reflection.Metadata - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Runtime.CompilerServices.Unsafe - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Security.Cryptography.Cng - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Security.Cryptography.Xml - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.ServiceProcess.ServiceController - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Text.Encodings.Web - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Text.Json - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - Microsoft.NETCore.Platforms - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) * Update dependencies from https://github.com/dotnet/core-setup build 20191026.2 - Microsoft.NETCore.App.Ref - 5.0.0-alpha1.19526.2 - NETStandard.Library.Ref - 2.1.0-alpha1.19526.2 - Microsoft.NETCore.App.Runtime.win-x64 - 5.0.0-alpha1.19526.2 Dependency coherency updates - Microsoft.Win32.Registry - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.ComponentModel.Annotations - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Diagnostics.DiagnosticSource - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Diagnostics.EventLog - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.IO.Pipelines - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Reflection.Metadata - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Runtime.CompilerServices.Unsafe - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Security.Cryptography.Cng - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Security.Cryptography.Xml - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.ServiceProcess.ServiceController - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Text.Encodings.Web - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Text.Json - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - Microsoft.NETCore.Platforms - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) * Update dependencies from https://github.com/dotnet/arcade build 20191027.3 - Microsoft.DotNet.Arcade.Sdk - 5.0.0-beta.19527.3 - Microsoft.DotNet.GenAPI - 5.0.0-beta.19527.3 - Microsoft.DotNet.Helix.Sdk - 5.0.0-beta.19527.3 * Update dependencies from https://github.com/dotnet/core-setup build 20191027.2 - Microsoft.NETCore.App.Ref - 5.0.0-alpha1.19527.2 - NETStandard.Library.Ref - 2.1.0-alpha1.19527.2 - Microsoft.NETCore.App.Runtime.win-x64 - 5.0.0-alpha1.19527.2 Dependency coherency updates - Microsoft.Win32.Registry - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.ComponentModel.Annotations - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Diagnostics.DiagnosticSource - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Diagnostics.EventLog - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.IO.Pipelines - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Reflection.Metadata - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Runtime.CompilerServices.Unsafe - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Security.Cryptography.Cng - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Security.Cryptography.Xml - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.ServiceProcess.ServiceController - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Text.Encodings.Web - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Text.Json - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - Microsoft.NETCore.Platforms - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) * Update dependencies from https://github.com/dotnet/core-setup build 20191028.5 - Microsoft.NETCore.App.Ref - 5.0.0-alpha.1.19528.5 - NETStandard.Library.Ref - 2.1.0-alpha.1.19528.5 - Microsoft.NETCore.App.Runtime.win-x64 - 5.0.0-alpha.1.19528.5 Dependency coherency updates - Microsoft.Win32.Registry - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.ComponentModel.Annotations - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Diagnostics.DiagnosticSource - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Diagnostics.EventLog - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.IO.Pipelines - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Reflection.Metadata - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Runtime.CompilerServices.Unsafe - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Security.Cryptography.Cng - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Security.Cryptography.Xml - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.ServiceProcess.ServiceController - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Text.Encodings.Web - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Text.Json - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) - Microsoft.NETCore.Platforms - 5.0.0-alpha.1.19525.3 (parent: Microsoft.NETCore.App.Runtime.win-x64) * Update dependencies from https://github.com/dotnet/core-setup build 20191030.10 - Microsoft.NETCore.App.Ref - 5.0.0-alpha.1.19530.10 - NETStandard.Library.Ref - 2.1.0-alpha.1.19530.10 - Microsoft.NETCore.App.Runtime.win-x64 - 5.0.0-alpha.1.19530.10 Dependency coherency updates - Microsoft.Win32.Registry - 5.0.0-alpha.1.19530.13 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.ComponentModel.Annotations - 5.0.0-alpha.1.19530.13 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Diagnostics.DiagnosticSource - 5.0.0-alpha.1.19530.13 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Diagnostics.EventLog - 5.0.0-alpha.1.19530.13 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.IO.Pipelines - 5.0.0-alpha.1.19530.13 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Reflection.Metadata - 5.0.0-alpha.1.19530.13 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Runtime.CompilerServices.Unsafe - 5.0.0-alpha.1.19530.13 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Security.Cryptography.Cng - 5.0.0-alpha.1.19530.13 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Security.Cryptography.Xml - 5.0.0-alpha.1.19530.13 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.ServiceProcess.ServiceController - 5.0.0-alpha.1.19530.13 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Text.Encodings.Web - 5.0.0-alpha.1.19530.13 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Text.Json - 5.0.0-alpha.1.19530.13 (parent: Microsoft.NETCore.App.Runtime.win-x64) - Microsoft.NETCore.Platforms - 5.0.0-alpha.1.19530.13 (parent: Microsoft.NETCore.App.Runtime.win-x64) * Update dependencies from https://github.com/dotnet/arcade build 20191031.8 - Microsoft.DotNet.Arcade.Sdk - 5.0.0-beta.19531.8 - Microsoft.DotNet.GenAPI - 5.0.0-beta.19531.8 - Microsoft.DotNet.Helix.Sdk - 5.0.0-beta.19531.8 \n\nCommit migrated from https://github.com/dotnet/extensions/commit/fc2a7eadfedd88f951eaecd45ecac6989b0f897f --- ...ons.Configuration.KeyPerFile.netcoreapp.cs | 8 +-- ...Configuration.KeyPerFile.netstandard2.0.cs | 8 +-- ...sions.FileProviders.Embedded.netcoreapp.cs | 6 +- ...s.FileProviders.Embedded.netstandard2.0.cs | 6 +- ...cs.HealthChecks.Abstractions.netcoreapp.cs | 32 +++++----- ...ealthChecks.Abstractions.netstandard2.0.cs | 32 +++++----- ...ons.Diagnostics.HealthChecks.netcoreapp.cs | 6 +- ...Diagnostics.HealthChecks.netstandard2.0.cs | 6 +- .../ref/Microsoft.JSInterop.netcoreapp.cs | 22 +++---- .../ref/Microsoft.JSInterop.netstandard2.0.cs | 22 +++---- ...ns.Localization.Abstractions.netcoreapp.cs | 8 +-- ...ocalization.Abstractions.netstandard2.0.cs | 8 +-- ...soft.Extensions.Localization.netcoreapp.cs | 8 +-- ....Extensions.Localization.netstandard2.0.cs | 8 +-- ...ft.Extensions.ObjectPool.netstandard2.0.cs | 6 +- .../ref/Microsoft.AspNetCore.Testing.net46.cs | 58 +++++++++---------- ...osoft.AspNetCore.Testing.netstandard2.0.cs | 58 +++++++++---------- ...osoft.Extensions.WebEncoders.netcoreapp.cs | 2 +- ...t.Extensions.WebEncoders.netstandard2.0.cs | 2 +- 19 files changed, 153 insertions(+), 153 deletions(-) diff --git a/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.netcoreapp.cs b/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.netcoreapp.cs index e26ca1909d18..37cefae6cb66 100644 --- a/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.netcoreapp.cs +++ b/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.netcoreapp.cs @@ -20,10 +20,10 @@ public override void Load() { } public partial class KeyPerFileConfigurationSource : Microsoft.Extensions.Configuration.IConfigurationSource { public KeyPerFileConfigurationSource() { } - public Microsoft.Extensions.FileProviders.IFileProvider FileProvider { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } - public System.Func IgnoreCondition { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } - public string IgnorePrefix { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } - public bool Optional { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public Microsoft.Extensions.FileProviders.IFileProvider FileProvider { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } + public System.Func IgnoreCondition { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } + public string IgnorePrefix { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } + public bool Optional { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } public Microsoft.Extensions.Configuration.IConfigurationProvider Build(Microsoft.Extensions.Configuration.IConfigurationBuilder builder) { throw null; } } } diff --git a/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.netstandard2.0.cs b/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.netstandard2.0.cs index e26ca1909d18..37cefae6cb66 100644 --- a/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.netstandard2.0.cs +++ b/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.netstandard2.0.cs @@ -20,10 +20,10 @@ public override void Load() { } public partial class KeyPerFileConfigurationSource : Microsoft.Extensions.Configuration.IConfigurationSource { public KeyPerFileConfigurationSource() { } - public Microsoft.Extensions.FileProviders.IFileProvider FileProvider { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } - public System.Func IgnoreCondition { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } - public string IgnorePrefix { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } - public bool Optional { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public Microsoft.Extensions.FileProviders.IFileProvider FileProvider { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } + public System.Func IgnoreCondition { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } + public string IgnorePrefix { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } + public bool Optional { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } public Microsoft.Extensions.Configuration.IConfigurationProvider Build(Microsoft.Extensions.Configuration.IConfigurationBuilder builder) { throw null; } } } diff --git a/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.netcoreapp.cs b/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.netcoreapp.cs index 1596f191fdd4..033174efc01e 100644 --- a/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.netcoreapp.cs +++ b/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.netcoreapp.cs @@ -17,7 +17,7 @@ public ManifestEmbeddedFileProvider(System.Reflection.Assembly assembly) { } public ManifestEmbeddedFileProvider(System.Reflection.Assembly assembly, string root) { } public ManifestEmbeddedFileProvider(System.Reflection.Assembly assembly, string root, System.DateTimeOffset lastModified) { } public ManifestEmbeddedFileProvider(System.Reflection.Assembly assembly, string root, string manifestName, System.DateTimeOffset lastModified) { } - public System.Reflection.Assembly Assembly { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Reflection.Assembly Assembly { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } public Microsoft.Extensions.FileProviders.IDirectoryContents GetDirectoryContents(string subpath) { throw null; } public Microsoft.Extensions.FileProviders.IFileInfo GetFileInfo(string subpath) { throw null; } public Microsoft.Extensions.Primitives.IChangeToken Watch(string filter) { throw null; } @@ -30,9 +30,9 @@ public partial class EmbeddedResourceFileInfo : Microsoft.Extensions.FileProvide public EmbeddedResourceFileInfo(System.Reflection.Assembly assembly, string resourcePath, string name, System.DateTimeOffset lastModified) { } public bool Exists { get { throw null; } } public bool IsDirectory { get { throw null; } } - public System.DateTimeOffset LastModified { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.DateTimeOffset LastModified { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } public long Length { get { throw null; } } - public string Name { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string Name { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } public string PhysicalPath { get { throw null; } } public System.IO.Stream CreateReadStream() { throw null; } } diff --git a/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.netstandard2.0.cs b/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.netstandard2.0.cs index 1596f191fdd4..033174efc01e 100644 --- a/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.netstandard2.0.cs +++ b/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.netstandard2.0.cs @@ -17,7 +17,7 @@ public ManifestEmbeddedFileProvider(System.Reflection.Assembly assembly) { } public ManifestEmbeddedFileProvider(System.Reflection.Assembly assembly, string root) { } public ManifestEmbeddedFileProvider(System.Reflection.Assembly assembly, string root, System.DateTimeOffset lastModified) { } public ManifestEmbeddedFileProvider(System.Reflection.Assembly assembly, string root, string manifestName, System.DateTimeOffset lastModified) { } - public System.Reflection.Assembly Assembly { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Reflection.Assembly Assembly { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } public Microsoft.Extensions.FileProviders.IDirectoryContents GetDirectoryContents(string subpath) { throw null; } public Microsoft.Extensions.FileProviders.IFileInfo GetFileInfo(string subpath) { throw null; } public Microsoft.Extensions.Primitives.IChangeToken Watch(string filter) { throw null; } @@ -30,9 +30,9 @@ public partial class EmbeddedResourceFileInfo : Microsoft.Extensions.FileProvide public EmbeddedResourceFileInfo(System.Reflection.Assembly assembly, string resourcePath, string name, System.DateTimeOffset lastModified) { } public bool Exists { get { throw null; } } public bool IsDirectory { get { throw null; } } - public System.DateTimeOffset LastModified { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.DateTimeOffset LastModified { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } public long Length { get { throw null; } } - public string Name { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string Name { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } public string PhysicalPath { get { throw null; } } public System.IO.Stream CreateReadStream() { throw null; } } diff --git a/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netcoreapp.cs b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netcoreapp.cs index 8c53adc27598..616636274594 100644 --- a/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netcoreapp.cs +++ b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netcoreapp.cs @@ -6,7 +6,7 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks public sealed partial class HealthCheckContext { public HealthCheckContext() { } - public Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckRegistration Registration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckRegistration Registration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } } public sealed partial class HealthCheckRegistration { @@ -15,9 +15,9 @@ public HealthCheckRegistration(string name, Microsoft.Extensions.Diagnostics.Hea public HealthCheckRegistration(string name, System.Func factory, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable tags) { } public HealthCheckRegistration(string name, System.Func factory, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable tags, System.TimeSpan? timeout) { } public System.Func Factory { get { throw null; } set { } } - public Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus FailureStatus { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus FailureStatus { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } public string Name { get { throw null; } set { } } - public System.Collections.Generic.ISet Tags { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Collections.Generic.ISet Tags { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } public System.TimeSpan Timeout { get { throw null; } set { } } } [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] @@ -26,10 +26,10 @@ public partial struct HealthCheckResult private object _dummy; private int _dummyPrimitive; public HealthCheckResult(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus status, string description = null, System.Exception exception = null, System.Collections.Generic.IReadOnlyDictionary data = null) { throw null; } - public System.Collections.Generic.IReadOnlyDictionary Data { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string Description { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public System.Exception Exception { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus Status { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Collections.Generic.IReadOnlyDictionary Data { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public string Description { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public System.Exception Exception { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus Status { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } public static Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult Degraded(string description = null, System.Exception exception = null, System.Collections.Generic.IReadOnlyDictionary data = null) { throw null; } public static Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult Healthy(string description = null, System.Collections.Generic.IReadOnlyDictionary data = null) { throw null; } public static Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult Unhealthy(string description = null, System.Exception exception = null, System.Collections.Generic.IReadOnlyDictionary data = null) { throw null; } @@ -37,9 +37,9 @@ public partial struct HealthCheckResult public sealed partial class HealthReport { public HealthReport(System.Collections.Generic.IReadOnlyDictionary entries, System.TimeSpan totalDuration) { } - public System.Collections.Generic.IReadOnlyDictionary Entries { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus Status { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public System.TimeSpan TotalDuration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Collections.Generic.IReadOnlyDictionary Entries { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus Status { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public System.TimeSpan TotalDuration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } } [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct HealthReportEntry @@ -48,12 +48,12 @@ public partial struct HealthReportEntry private int _dummyPrimitive; public HealthReportEntry(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus status, string description, System.TimeSpan duration, System.Exception exception, System.Collections.Generic.IReadOnlyDictionary data) { throw null; } public HealthReportEntry(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus status, string description, System.TimeSpan duration, System.Exception exception, System.Collections.Generic.IReadOnlyDictionary data, System.Collections.Generic.IEnumerable tags = null) { throw null; } - public System.Collections.Generic.IReadOnlyDictionary Data { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string Description { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public System.TimeSpan Duration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public System.Exception Exception { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus Status { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public System.Collections.Generic.IEnumerable Tags { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Collections.Generic.IReadOnlyDictionary Data { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public string Description { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public System.TimeSpan Duration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public System.Exception Exception { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus Status { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public System.Collections.Generic.IEnumerable Tags { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } } public enum HealthStatus { diff --git a/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netstandard2.0.cs b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netstandard2.0.cs index 8c53adc27598..616636274594 100644 --- a/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netstandard2.0.cs +++ b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netstandard2.0.cs @@ -6,7 +6,7 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks public sealed partial class HealthCheckContext { public HealthCheckContext() { } - public Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckRegistration Registration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckRegistration Registration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } } public sealed partial class HealthCheckRegistration { @@ -15,9 +15,9 @@ public HealthCheckRegistration(string name, Microsoft.Extensions.Diagnostics.Hea public HealthCheckRegistration(string name, System.Func factory, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable tags) { } public HealthCheckRegistration(string name, System.Func factory, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable tags, System.TimeSpan? timeout) { } public System.Func Factory { get { throw null; } set { } } - public Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus FailureStatus { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus FailureStatus { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } public string Name { get { throw null; } set { } } - public System.Collections.Generic.ISet Tags { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Collections.Generic.ISet Tags { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } public System.TimeSpan Timeout { get { throw null; } set { } } } [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] @@ -26,10 +26,10 @@ public partial struct HealthCheckResult private object _dummy; private int _dummyPrimitive; public HealthCheckResult(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus status, string description = null, System.Exception exception = null, System.Collections.Generic.IReadOnlyDictionary data = null) { throw null; } - public System.Collections.Generic.IReadOnlyDictionary Data { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string Description { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public System.Exception Exception { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus Status { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Collections.Generic.IReadOnlyDictionary Data { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public string Description { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public System.Exception Exception { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus Status { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } public static Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult Degraded(string description = null, System.Exception exception = null, System.Collections.Generic.IReadOnlyDictionary data = null) { throw null; } public static Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult Healthy(string description = null, System.Collections.Generic.IReadOnlyDictionary data = null) { throw null; } public static Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult Unhealthy(string description = null, System.Exception exception = null, System.Collections.Generic.IReadOnlyDictionary data = null) { throw null; } @@ -37,9 +37,9 @@ public partial struct HealthCheckResult public sealed partial class HealthReport { public HealthReport(System.Collections.Generic.IReadOnlyDictionary entries, System.TimeSpan totalDuration) { } - public System.Collections.Generic.IReadOnlyDictionary Entries { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus Status { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public System.TimeSpan TotalDuration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Collections.Generic.IReadOnlyDictionary Entries { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus Status { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public System.TimeSpan TotalDuration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } } [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct HealthReportEntry @@ -48,12 +48,12 @@ public partial struct HealthReportEntry private int _dummyPrimitive; public HealthReportEntry(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus status, string description, System.TimeSpan duration, System.Exception exception, System.Collections.Generic.IReadOnlyDictionary data) { throw null; } public HealthReportEntry(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus status, string description, System.TimeSpan duration, System.Exception exception, System.Collections.Generic.IReadOnlyDictionary data, System.Collections.Generic.IEnumerable tags = null) { throw null; } - public System.Collections.Generic.IReadOnlyDictionary Data { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string Description { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public System.TimeSpan Duration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public System.Exception Exception { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus Status { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public System.Collections.Generic.IEnumerable Tags { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Collections.Generic.IReadOnlyDictionary Data { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public string Description { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public System.TimeSpan Duration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public System.Exception Exception { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus Status { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public System.Collections.Generic.IEnumerable Tags { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } } public enum HealthStatus { diff --git a/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netcoreapp.cs b/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netcoreapp.cs index a23961efdd26..cee2d1420e1a 100644 --- a/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netcoreapp.cs +++ b/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netcoreapp.cs @@ -42,8 +42,8 @@ public sealed partial class HealthCheckPublisherOptions public HealthCheckPublisherOptions() { } public System.TimeSpan Delay { get { throw null; } set { } } public System.TimeSpan Period { get { throw null; } set { } } - public System.Func Predicate { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } - public System.TimeSpan Timeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public System.Func Predicate { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } + public System.TimeSpan Timeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } } public abstract partial class HealthCheckService { @@ -54,6 +54,6 @@ protected HealthCheckService() { } public sealed partial class HealthCheckServiceOptions { public HealthCheckServiceOptions() { } - public System.Collections.Generic.ICollection Registrations { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Collections.Generic.ICollection Registrations { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } } } diff --git a/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netstandard2.0.cs b/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netstandard2.0.cs index a23961efdd26..cee2d1420e1a 100644 --- a/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netstandard2.0.cs +++ b/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netstandard2.0.cs @@ -42,8 +42,8 @@ public sealed partial class HealthCheckPublisherOptions public HealthCheckPublisherOptions() { } public System.TimeSpan Delay { get { throw null; } set { } } public System.TimeSpan Period { get { throw null; } set { } } - public System.Func Predicate { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } - public System.TimeSpan Timeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public System.Func Predicate { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } + public System.TimeSpan Timeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } } public abstract partial class HealthCheckService { @@ -54,6 +54,6 @@ protected HealthCheckService() { } public sealed partial class HealthCheckServiceOptions { public HealthCheckServiceOptions() { } - public System.Collections.Generic.ICollection Registrations { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Collections.Generic.ICollection Registrations { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } } } diff --git a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netcoreapp.cs b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netcoreapp.cs index 2d8c51caaf8e..3abe22eea07e 100644 --- a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netcoreapp.cs +++ b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netcoreapp.cs @@ -42,13 +42,13 @@ public sealed partial class JSInvokableAttribute : System.Attribute { public JSInvokableAttribute() { } public JSInvokableAttribute(string identifier) { } - public string Identifier { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string Identifier { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } } public abstract partial class JSRuntime : Microsoft.JSInterop.IJSRuntime { protected JSRuntime() { } - protected System.TimeSpan? DefaultAsyncTimeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } - protected internal System.Text.Json.JsonSerializerOptions JsonSerializerOptions { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + protected System.TimeSpan? DefaultAsyncTimeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } + protected internal System.Text.Json.JsonSerializerOptions JsonSerializerOptions { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } protected abstract void BeginInvokeJS(long taskId, string identifier, string argsJson); protected internal abstract void EndInvokeDotNet(Microsoft.JSInterop.Infrastructure.DotNetInvocationInfo invocationInfo, in Microsoft.JSInterop.Infrastructure.DotNetInvocationResult invocationResult); public System.Threading.Tasks.ValueTask InvokeAsync(string identifier, object[] args) { throw null; } @@ -82,10 +82,10 @@ public readonly partial struct DotNetInvocationInfo private readonly object _dummy; private readonly int _dummyPrimitive; public DotNetInvocationInfo(string assemblyName, string methodIdentifier, long dotNetObjectId, string callId) { throw null; } - public string AssemblyName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string CallId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public long DotNetObjectId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string MethodIdentifier { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string AssemblyName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public string CallId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public long DotNetObjectId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public string MethodIdentifier { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } } [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public readonly partial struct DotNetInvocationResult @@ -94,9 +94,9 @@ public readonly partial struct DotNetInvocationResult private readonly int _dummyPrimitive; public DotNetInvocationResult(System.Exception exception, string errorKind) { throw null; } public DotNetInvocationResult(object result) { throw null; } - public string ErrorKind { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public System.Exception Exception { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public object Result { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public bool Success { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string ErrorKind { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public System.Exception Exception { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public object Result { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public bool Success { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } } } diff --git a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs index 2d8c51caaf8e..3abe22eea07e 100644 --- a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs +++ b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs @@ -42,13 +42,13 @@ public sealed partial class JSInvokableAttribute : System.Attribute { public JSInvokableAttribute() { } public JSInvokableAttribute(string identifier) { } - public string Identifier { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string Identifier { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } } public abstract partial class JSRuntime : Microsoft.JSInterop.IJSRuntime { protected JSRuntime() { } - protected System.TimeSpan? DefaultAsyncTimeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } - protected internal System.Text.Json.JsonSerializerOptions JsonSerializerOptions { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + protected System.TimeSpan? DefaultAsyncTimeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } + protected internal System.Text.Json.JsonSerializerOptions JsonSerializerOptions { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } protected abstract void BeginInvokeJS(long taskId, string identifier, string argsJson); protected internal abstract void EndInvokeDotNet(Microsoft.JSInterop.Infrastructure.DotNetInvocationInfo invocationInfo, in Microsoft.JSInterop.Infrastructure.DotNetInvocationResult invocationResult); public System.Threading.Tasks.ValueTask InvokeAsync(string identifier, object[] args) { throw null; } @@ -82,10 +82,10 @@ public readonly partial struct DotNetInvocationInfo private readonly object _dummy; private readonly int _dummyPrimitive; public DotNetInvocationInfo(string assemblyName, string methodIdentifier, long dotNetObjectId, string callId) { throw null; } - public string AssemblyName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string CallId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public long DotNetObjectId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string MethodIdentifier { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string AssemblyName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public string CallId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public long DotNetObjectId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public string MethodIdentifier { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } } [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public readonly partial struct DotNetInvocationResult @@ -94,9 +94,9 @@ public readonly partial struct DotNetInvocationResult private readonly int _dummyPrimitive; public DotNetInvocationResult(System.Exception exception, string errorKind) { throw null; } public DotNetInvocationResult(object result) { throw null; } - public string ErrorKind { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public System.Exception Exception { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public object Result { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public bool Success { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string ErrorKind { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public System.Exception Exception { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public object Result { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public bool Success { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } } } diff --git a/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netcoreapp.cs b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netcoreapp.cs index 174cac28e5cb..8a5f5baa08c7 100644 --- a/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netcoreapp.cs +++ b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netcoreapp.cs @@ -24,10 +24,10 @@ public partial class LocalizedString public LocalizedString(string name, string value) { } public LocalizedString(string name, string value, bool resourceNotFound) { } public LocalizedString(string name, string value, bool resourceNotFound, string searchedLocation) { } - public string Name { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public bool ResourceNotFound { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string SearchedLocation { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string Value { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string Name { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public bool ResourceNotFound { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public string SearchedLocation { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public string Value { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } public static implicit operator string (Microsoft.Extensions.Localization.LocalizedString localizedString) { throw null; } public override string ToString() { throw null; } } diff --git a/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netstandard2.0.cs b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netstandard2.0.cs index 174cac28e5cb..8a5f5baa08c7 100644 --- a/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netstandard2.0.cs +++ b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netstandard2.0.cs @@ -24,10 +24,10 @@ public partial class LocalizedString public LocalizedString(string name, string value) { } public LocalizedString(string name, string value, bool resourceNotFound) { } public LocalizedString(string name, string value, bool resourceNotFound, string searchedLocation) { } - public string Name { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public bool ResourceNotFound { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string SearchedLocation { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string Value { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string Name { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public bool ResourceNotFound { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public string SearchedLocation { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public string Value { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } public static implicit operator string (Microsoft.Extensions.Localization.LocalizedString localizedString) { throw null; } public override string ToString() { throw null; } } diff --git a/src/Localization/Localization/ref/Microsoft.Extensions.Localization.netcoreapp.cs b/src/Localization/Localization/ref/Microsoft.Extensions.Localization.netcoreapp.cs index 80175da71840..741e1ce10624 100644 --- a/src/Localization/Localization/ref/Microsoft.Extensions.Localization.netcoreapp.cs +++ b/src/Localization/Localization/ref/Microsoft.Extensions.Localization.netcoreapp.cs @@ -18,13 +18,13 @@ public partial interface IResourceNamesCache public partial class LocalizationOptions { public LocalizationOptions() { } - public string ResourcesPath { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public string ResourcesPath { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } } [System.AttributeUsageAttribute(System.AttributeTargets.Assembly, AllowMultiple=false, Inherited=false)] public partial class ResourceLocationAttribute : System.Attribute { public ResourceLocationAttribute(string resourceLocation) { } - public string ResourceLocation { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string ResourceLocation { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } } public partial class ResourceManagerStringLocalizer : Microsoft.Extensions.Localization.IStringLocalizer { @@ -69,7 +69,7 @@ public ResourceNamesCache() { } public partial class RootNamespaceAttribute : System.Attribute { public RootNamespaceAttribute(string rootNamespace) { } - public string RootNamespace { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string RootNamespace { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } } } namespace Microsoft.Extensions.Localization.Internal @@ -77,7 +77,7 @@ namespace Microsoft.Extensions.Localization.Internal public partial class AssemblyWrapper { public AssemblyWrapper(System.Reflection.Assembly assembly) { } - public System.Reflection.Assembly Assembly { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Reflection.Assembly Assembly { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } public virtual string FullName { get { throw null; } } public virtual System.IO.Stream GetManifestResourceStream(string name) { throw null; } } diff --git a/src/Localization/Localization/ref/Microsoft.Extensions.Localization.netstandard2.0.cs b/src/Localization/Localization/ref/Microsoft.Extensions.Localization.netstandard2.0.cs index 80175da71840..741e1ce10624 100644 --- a/src/Localization/Localization/ref/Microsoft.Extensions.Localization.netstandard2.0.cs +++ b/src/Localization/Localization/ref/Microsoft.Extensions.Localization.netstandard2.0.cs @@ -18,13 +18,13 @@ public partial interface IResourceNamesCache public partial class LocalizationOptions { public LocalizationOptions() { } - public string ResourcesPath { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public string ResourcesPath { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } } [System.AttributeUsageAttribute(System.AttributeTargets.Assembly, AllowMultiple=false, Inherited=false)] public partial class ResourceLocationAttribute : System.Attribute { public ResourceLocationAttribute(string resourceLocation) { } - public string ResourceLocation { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string ResourceLocation { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } } public partial class ResourceManagerStringLocalizer : Microsoft.Extensions.Localization.IStringLocalizer { @@ -69,7 +69,7 @@ public ResourceNamesCache() { } public partial class RootNamespaceAttribute : System.Attribute { public RootNamespaceAttribute(string rootNamespace) { } - public string RootNamespace { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string RootNamespace { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } } } namespace Microsoft.Extensions.Localization.Internal @@ -77,7 +77,7 @@ namespace Microsoft.Extensions.Localization.Internal public partial class AssemblyWrapper { public AssemblyWrapper(System.Reflection.Assembly assembly) { } - public System.Reflection.Assembly Assembly { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Reflection.Assembly Assembly { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } public virtual string FullName { get { throw null; } } public virtual System.IO.Stream GetManifestResourceStream(string name) { throw null; } } diff --git a/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.netstandard2.0.cs b/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.netstandard2.0.cs index 083aaf14efe4..b3b72bec86e3 100644 --- a/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.netstandard2.0.cs +++ b/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.netstandard2.0.cs @@ -6,7 +6,7 @@ namespace Microsoft.Extensions.ObjectPool public partial class DefaultObjectPoolProvider : Microsoft.Extensions.ObjectPool.ObjectPoolProvider { public DefaultObjectPoolProvider() { } - public int MaximumRetained { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public int MaximumRetained { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } public override Microsoft.Extensions.ObjectPool.ObjectPool Create(Microsoft.Extensions.ObjectPool.IPooledObjectPolicy policy) { throw null; } } public partial class DefaultObjectPool : Microsoft.Extensions.ObjectPool.ObjectPool where T : class @@ -68,8 +68,8 @@ protected PooledObjectPolicy() { } public partial class StringBuilderPooledObjectPolicy : Microsoft.Extensions.ObjectPool.PooledObjectPolicy { public StringBuilderPooledObjectPolicy() { } - public int InitialCapacity { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } - public int MaximumRetainedCapacity { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public int InitialCapacity { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } + public int MaximumRetainedCapacity { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } public override System.Text.StringBuilder Create() { throw null; } public override bool Return(System.Text.StringBuilder obj) { throw null; } } diff --git a/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs b/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs index a7b9c07a9968..5fa7e9422e76 100644 --- a/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs +++ b/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs @@ -34,7 +34,7 @@ protected override void RunTestCases(System.Collections.Generic.IEnumerable Filters { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string GitHubIssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Collections.Generic.IReadOnlyList Filters { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public string GitHubIssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } } public static partial class FlakyOn { @@ -131,7 +131,7 @@ public partial class FrameworkSkipConditionAttribute : System.Attribute, Microso { public FrameworkSkipConditionAttribute(Microsoft.AspNetCore.Testing.RuntimeFrameworks excludedFrameworks) { } public bool IsMet { get { throw null; } } - public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } } public static partial class HelixQueues { @@ -174,7 +174,7 @@ public partial class MinimumOSVersionAttribute : System.Attribute, Microsoft.Asp { public MinimumOSVersionAttribute(Microsoft.AspNetCore.Testing.OperatingSystems operatingSystem, string minVersion) { } public bool IsMet { get { throw null; } } - public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } } [System.FlagsAttribute] public enum OperatingSystems @@ -188,29 +188,29 @@ public partial class OSSkipConditionAttribute : System.Attribute, Microsoft.AspN { public OSSkipConditionAttribute(Microsoft.AspNetCore.Testing.OperatingSystems operatingSystem, params string[] versions) { } public bool IsMet { get { throw null; } } - public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } } [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=false)] [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public partial class RepeatAttribute : System.Attribute { public RepeatAttribute(int runCount = 10) { } - public int RunCount { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public int RunCount { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } } public partial class RepeatContext { public RepeatContext(int limit) { } public static Microsoft.AspNetCore.Testing.RepeatContext Current { get { throw null; } } - public int CurrentIteration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } - public int Limit { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public int CurrentIteration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } + public int Limit { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } } [System.AttributeUsageAttribute(System.AttributeTargets.Method)] public partial class ReplaceCultureAttribute : Xunit.Sdk.BeforeAfterTestAttribute { public ReplaceCultureAttribute() { } public ReplaceCultureAttribute(string currentCulture, string currentUICulture) { } - public System.Globalization.CultureInfo Culture { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public System.Globalization.CultureInfo UICulture { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Globalization.CultureInfo Culture { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public System.Globalization.CultureInfo UICulture { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } public override void After(System.Reflection.MethodInfo methodUnderTest) { } public override void Before(System.Reflection.MethodInfo methodUnderTest) { } } @@ -232,7 +232,7 @@ public partial class SkipOnCIAttribute : System.Attribute, Microsoft.AspNetCore. { public SkipOnCIAttribute(string issueUrl = "") { } public bool IsMet { get { throw null; } } - public string IssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string IssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } public string SkipReason { get { throw null; } } public static string GetIfOnAzdo() { throw null; } public static string GetTargetHelixQueue() { throw null; } @@ -245,8 +245,8 @@ public partial class SkipOnHelixAttribute : System.Attribute, Microsoft.AspNetCo { public SkipOnHelixAttribute(string issueUrl) { } public bool IsMet { get { throw null; } } - public string IssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string Queues { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public string IssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public string Queues { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } public string SkipReason { get { throw null; } } public static string GetTargetHelixQueue() { throw null; } public static bool OnHelix() { throw null; } @@ -263,27 +263,27 @@ public override void Serialize(Xunit.Abstractions.IXunitSerializationInfo data) public static partial class TaskExtensions { [System.Diagnostics.DebuggerStepThroughAttribute] - public static System.Threading.Tasks.Task TimeoutAfter(this System.Threading.Tasks.Task task, System.TimeSpan timeout, [System.Runtime.CompilerServices.CallerFilePathAttribute]string filePath = null, [System.Runtime.CompilerServices.CallerLineNumberAttribute]int lineNumber = 0) { throw null; } + public static System.Threading.Tasks.Task TimeoutAfter(this System.Threading.Tasks.Task task, System.TimeSpan timeout, [System.Runtime.CompilerServices.CallerFilePathAttribute] string filePath = null, [System.Runtime.CompilerServices.CallerLineNumberAttribute] int lineNumber = 0) { throw null; } [System.Diagnostics.DebuggerStepThroughAttribute] - public static System.Threading.Tasks.Task TimeoutAfter(this System.Threading.Tasks.Task task, System.TimeSpan timeout, [System.Runtime.CompilerServices.CallerFilePathAttribute]string filePath = null, [System.Runtime.CompilerServices.CallerLineNumberAttribute]int lineNumber = 0) { throw null; } + public static System.Threading.Tasks.Task TimeoutAfter(this System.Threading.Tasks.Task task, System.TimeSpan timeout, [System.Runtime.CompilerServices.CallerFilePathAttribute] string filePath = null, [System.Runtime.CompilerServices.CallerLineNumberAttribute] int lineNumber = 0) { throw null; } } public sealed partial class TestContext { public TestContext(System.Type testClass, object[] constructorArguments, System.Reflection.MethodInfo testMethod, object[] methodArguments, Xunit.Abstractions.ITestOutputHelper output) { } - public object[] ConstructorArguments { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public object[] ConstructorArguments { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } public Microsoft.AspNetCore.Testing.TestFileOutputContext FileOutput { get { throw null; } } - public object[] MethodArguments { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public Xunit.Abstractions.ITestOutputHelper Output { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public System.Type TestClass { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public System.Reflection.MethodInfo TestMethod { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public object[] MethodArguments { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public Xunit.Abstractions.ITestOutputHelper Output { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public System.Type TestClass { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public System.Reflection.MethodInfo TestMethod { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } } public sealed partial class TestFileOutputContext { public TestFileOutputContext(Microsoft.AspNetCore.Testing.TestContext parent) { } - public string AssemblyOutputDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string TestClassName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string TestClassOutputDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string TestName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string AssemblyOutputDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public string TestClassName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public string TestClassOutputDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public string TestName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } public static string GetAssemblyBaseDirectory(System.Reflection.Assembly assembly, string baseDirectory = null) { throw null; } public static string GetOutputDirectory(System.Reflection.Assembly assembly) { throw null; } public static string GetTestClassName(System.Type type) { throw null; } @@ -299,8 +299,8 @@ public static partial class TestMethodExtensions public partial class TestOutputDirectoryAttribute : System.Attribute { public TestOutputDirectoryAttribute(string targetFramework, string baseDirectory = null) { } - public string BaseDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string TargetFramework { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string BaseDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public string TargetFramework { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } } [System.ObsoleteAttribute("This API is obsolete and the pattern its usage encouraged should not be used anymore. See https://github.com/aspnet/Extensions/issues/1697 for details.")] public partial class TestPathUtilities diff --git a/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs b/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs index a7b9c07a9968..5fa7e9422e76 100644 --- a/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs +++ b/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs @@ -34,7 +34,7 @@ protected override void RunTestCases(System.Collections.Generic.IEnumerable Filters { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string GitHubIssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Collections.Generic.IReadOnlyList Filters { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public string GitHubIssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } } public static partial class FlakyOn { @@ -131,7 +131,7 @@ public partial class FrameworkSkipConditionAttribute : System.Attribute, Microso { public FrameworkSkipConditionAttribute(Microsoft.AspNetCore.Testing.RuntimeFrameworks excludedFrameworks) { } public bool IsMet { get { throw null; } } - public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } } public static partial class HelixQueues { @@ -174,7 +174,7 @@ public partial class MinimumOSVersionAttribute : System.Attribute, Microsoft.Asp { public MinimumOSVersionAttribute(Microsoft.AspNetCore.Testing.OperatingSystems operatingSystem, string minVersion) { } public bool IsMet { get { throw null; } } - public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } } [System.FlagsAttribute] public enum OperatingSystems @@ -188,29 +188,29 @@ public partial class OSSkipConditionAttribute : System.Attribute, Microsoft.AspN { public OSSkipConditionAttribute(Microsoft.AspNetCore.Testing.OperatingSystems operatingSystem, params string[] versions) { } public bool IsMet { get { throw null; } } - public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } } [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=false)] [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public partial class RepeatAttribute : System.Attribute { public RepeatAttribute(int runCount = 10) { } - public int RunCount { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public int RunCount { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } } public partial class RepeatContext { public RepeatContext(int limit) { } public static Microsoft.AspNetCore.Testing.RepeatContext Current { get { throw null; } } - public int CurrentIteration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } - public int Limit { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public int CurrentIteration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } + public int Limit { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } } [System.AttributeUsageAttribute(System.AttributeTargets.Method)] public partial class ReplaceCultureAttribute : Xunit.Sdk.BeforeAfterTestAttribute { public ReplaceCultureAttribute() { } public ReplaceCultureAttribute(string currentCulture, string currentUICulture) { } - public System.Globalization.CultureInfo Culture { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public System.Globalization.CultureInfo UICulture { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Globalization.CultureInfo Culture { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public System.Globalization.CultureInfo UICulture { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } public override void After(System.Reflection.MethodInfo methodUnderTest) { } public override void Before(System.Reflection.MethodInfo methodUnderTest) { } } @@ -232,7 +232,7 @@ public partial class SkipOnCIAttribute : System.Attribute, Microsoft.AspNetCore. { public SkipOnCIAttribute(string issueUrl = "") { } public bool IsMet { get { throw null; } } - public string IssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string IssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } public string SkipReason { get { throw null; } } public static string GetIfOnAzdo() { throw null; } public static string GetTargetHelixQueue() { throw null; } @@ -245,8 +245,8 @@ public partial class SkipOnHelixAttribute : System.Attribute, Microsoft.AspNetCo { public SkipOnHelixAttribute(string issueUrl) { } public bool IsMet { get { throw null; } } - public string IssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string Queues { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public string IssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public string Queues { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } public string SkipReason { get { throw null; } } public static string GetTargetHelixQueue() { throw null; } public static bool OnHelix() { throw null; } @@ -263,27 +263,27 @@ public override void Serialize(Xunit.Abstractions.IXunitSerializationInfo data) public static partial class TaskExtensions { [System.Diagnostics.DebuggerStepThroughAttribute] - public static System.Threading.Tasks.Task TimeoutAfter(this System.Threading.Tasks.Task task, System.TimeSpan timeout, [System.Runtime.CompilerServices.CallerFilePathAttribute]string filePath = null, [System.Runtime.CompilerServices.CallerLineNumberAttribute]int lineNumber = 0) { throw null; } + public static System.Threading.Tasks.Task TimeoutAfter(this System.Threading.Tasks.Task task, System.TimeSpan timeout, [System.Runtime.CompilerServices.CallerFilePathAttribute] string filePath = null, [System.Runtime.CompilerServices.CallerLineNumberAttribute] int lineNumber = 0) { throw null; } [System.Diagnostics.DebuggerStepThroughAttribute] - public static System.Threading.Tasks.Task TimeoutAfter(this System.Threading.Tasks.Task task, System.TimeSpan timeout, [System.Runtime.CompilerServices.CallerFilePathAttribute]string filePath = null, [System.Runtime.CompilerServices.CallerLineNumberAttribute]int lineNumber = 0) { throw null; } + public static System.Threading.Tasks.Task TimeoutAfter(this System.Threading.Tasks.Task task, System.TimeSpan timeout, [System.Runtime.CompilerServices.CallerFilePathAttribute] string filePath = null, [System.Runtime.CompilerServices.CallerLineNumberAttribute] int lineNumber = 0) { throw null; } } public sealed partial class TestContext { public TestContext(System.Type testClass, object[] constructorArguments, System.Reflection.MethodInfo testMethod, object[] methodArguments, Xunit.Abstractions.ITestOutputHelper output) { } - public object[] ConstructorArguments { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public object[] ConstructorArguments { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } public Microsoft.AspNetCore.Testing.TestFileOutputContext FileOutput { get { throw null; } } - public object[] MethodArguments { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public Xunit.Abstractions.ITestOutputHelper Output { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public System.Type TestClass { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public System.Reflection.MethodInfo TestMethod { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public object[] MethodArguments { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public Xunit.Abstractions.ITestOutputHelper Output { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public System.Type TestClass { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public System.Reflection.MethodInfo TestMethod { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } } public sealed partial class TestFileOutputContext { public TestFileOutputContext(Microsoft.AspNetCore.Testing.TestContext parent) { } - public string AssemblyOutputDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string TestClassName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string TestClassOutputDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string TestName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string AssemblyOutputDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public string TestClassName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public string TestClassOutputDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public string TestName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } public static string GetAssemblyBaseDirectory(System.Reflection.Assembly assembly, string baseDirectory = null) { throw null; } public static string GetOutputDirectory(System.Reflection.Assembly assembly) { throw null; } public static string GetTestClassName(System.Type type) { throw null; } @@ -299,8 +299,8 @@ public static partial class TestMethodExtensions public partial class TestOutputDirectoryAttribute : System.Attribute { public TestOutputDirectoryAttribute(string targetFramework, string baseDirectory = null) { } - public string BaseDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string TargetFramework { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string BaseDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public string TargetFramework { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } } [System.ObsoleteAttribute("This API is obsolete and the pattern its usage encouraged should not be used anymore. See https://github.com/aspnet/Extensions/issues/1697 for details.")] public partial class TestPathUtilities diff --git a/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.netcoreapp.cs b/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.netcoreapp.cs index 18cdcbdfa352..ad8e11a40eae 100644 --- a/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.netcoreapp.cs +++ b/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.netcoreapp.cs @@ -14,7 +14,7 @@ namespace Microsoft.Extensions.WebEncoders public sealed partial class WebEncoderOptions { public WebEncoderOptions() { } - public System.Text.Encodings.Web.TextEncoderSettings TextEncoderSettings { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public System.Text.Encodings.Web.TextEncoderSettings TextEncoderSettings { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } } } namespace Microsoft.Extensions.WebEncoders.Testing diff --git a/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.netstandard2.0.cs b/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.netstandard2.0.cs index 18cdcbdfa352..ad8e11a40eae 100644 --- a/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.netstandard2.0.cs +++ b/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.netstandard2.0.cs @@ -14,7 +14,7 @@ namespace Microsoft.Extensions.WebEncoders public sealed partial class WebEncoderOptions { public WebEncoderOptions() { } - public System.Text.Encodings.Web.TextEncoderSettings TextEncoderSettings { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public System.Text.Encodings.Web.TextEncoderSettings TextEncoderSettings { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } } } namespace Microsoft.Extensions.WebEncoders.Testing From d39613077947d504f27b290e4e0d3091cdc4e27d Mon Sep 17 00:00:00 2001 From: Kahbazi Date: Mon, 11 Nov 2019 21:23:28 +0330 Subject: [PATCH 157/183] Add startvs for each solution (dotnet/extensions#2630) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/e41d90f66109f3387063f412a0ec61c85f8ecbdb --- src/JSInterop/startvs.cmd | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 src/JSInterop/startvs.cmd diff --git a/src/JSInterop/startvs.cmd b/src/JSInterop/startvs.cmd new file mode 100644 index 000000000000..9812a275beb6 --- /dev/null +++ b/src/JSInterop/startvs.cmd @@ -0,0 +1,3 @@ +@ECHO OFF + +%~dp0..\..\startvs.cmd %~dp0JSInterop.slnf \ No newline at end of file From 6619bbe90749d9fe479b30ceb6e018963cdf0a5e Mon Sep 17 00:00:00 2001 From: William Godbe Date: Thu, 21 Nov 2019 12:25:38 -0800 Subject: [PATCH 158/183] Switch to Arcade model for PackageIcon (dotnet/extensions#2663) * Switch to Arcade model for PackageIcon * Update dependencies from Arcade * Update dependencies from Arcade * Switch to dotnet-eng \n\nCommit migrated from https://github.com/dotnet/extensions/commit/cad4d7d0a80c84b7c8927263be988d192220bb42 --- .../src/Microsoft.Extensions.FileProviders.Embedded.csproj | 1 - ...rosoft.Extensions.FileProviders.Embedded.multitarget.nuspec | 3 +-- ...soft.Extensions.FileProviders.Embedded.netcoreapp3.0.nuspec | 3 +-- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj index 4858cae97e60..d36323e3b151 100644 --- a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj @@ -32,6 +32,5 @@ - diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.multitarget.nuspec b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.multitarget.nuspec index c9ab1ba847ea..958cebb7b99b 100644 --- a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.multitarget.nuspec +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.multitarget.nuspec @@ -10,16 +10,15 @@ - packageIcon.png + $CommonFileElements$ - diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.netcoreapp3.0.nuspec b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.netcoreapp3.0.nuspec index b42e51174f83..057de99b89aa 100644 --- a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.netcoreapp3.0.nuspec +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.netcoreapp3.0.nuspec @@ -7,16 +7,15 @@ - packageIcon.png + $CommonFileElements$ - From 3f6d4d569ba32da4f002595d3f48a7bc5a4f29a9 Mon Sep 17 00:00:00 2001 From: Andrew Stanton-Nurse Date: Fri, 22 Nov 2019 10:25:07 -0800 Subject: [PATCH 159/183] update jsinterop NPM package ID for 5.0 (dotnet/extensions#2634) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/41c727faa604f1b418356dceb437e86c22f17bd8 --- .../src/Microsoft.JSInterop.JS.npmproj | 24 +- .../src/package-lock.json | 696 +++++++++--------- .../Microsoft.JSInterop.JS/src/package.json | 4 +- 3 files changed, 362 insertions(+), 362 deletions(-) diff --git a/src/JSInterop/Microsoft.JSInterop.JS/src/Microsoft.JSInterop.JS.npmproj b/src/JSInterop/Microsoft.JSInterop.JS/src/Microsoft.JSInterop.JS.npmproj index e8d0554ff701..589b7e1c5a7b 100644 --- a/src/JSInterop/Microsoft.JSInterop.JS/src/Microsoft.JSInterop.JS.npmproj +++ b/src/JSInterop/Microsoft.JSInterop.JS/src/Microsoft.JSInterop.JS.npmproj @@ -1,12 +1,12 @@ - - - - - @dotnet/jsinterop - true - false - true - - - - + + + + + @microsoft/dotnet-js-interop + true + false + true + + + + diff --git a/src/JSInterop/Microsoft.JSInterop.JS/src/package-lock.json b/src/JSInterop/Microsoft.JSInterop.JS/src/package-lock.json index 4c82321255da..d2d9c46b2ae5 100644 --- a/src/JSInterop/Microsoft.JSInterop.JS/src/package-lock.json +++ b/src/JSInterop/Microsoft.JSInterop.JS/src/package-lock.json @@ -1,348 +1,348 @@ -{ - "name": "@dotnet/jsinterop", - "version": "3.0.0-dev", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" - }, - "dependencies": { - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - } - } - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", - "dev": true - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "commander": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", - "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, - "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", - "dev": true - }, - "js-yaml": { - "version": "3.12.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.1.tgz", - "integrity": "sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", - "dev": true - }, - "resolve": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", - "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - }, - "rimraf": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", - "dev": true, - "requires": { - "glob": "^7.0.5" - } - }, - "semver": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", - "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", - "dev": true - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - }, - "tslib": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", - "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", - "dev": true - }, - "tslint": { - "version": "5.12.1", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.12.1.tgz", - "integrity": "sha512-sfodBHOucFg6egff8d1BvuofoOQ/nOeYNfbp7LDlKBcLNrL3lmS5zoiDGyOMdT7YsEXAwWpTdAHwOGOc8eRZAw==", - "dev": true, - "requires": { - "babel-code-frame": "^6.22.0", - "builtin-modules": "^1.1.1", - "chalk": "^2.3.0", - "commander": "^2.12.1", - "diff": "^3.2.0", - "glob": "^7.1.1", - "js-yaml": "^3.7.0", - "minimatch": "^3.0.4", - "resolve": "^1.3.2", - "semver": "^5.3.0", - "tslib": "^1.8.0", - "tsutils": "^2.27.2" - } - }, - "tsutils": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", - "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - } - }, - "typescript": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz", - "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==", - "dev": true - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - } - } -} +{ + "name": "@microsoft/dotnet-js-interop", + "version": "5.0.0-dev", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + }, + "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + } + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "commander": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "js-yaml": { + "version": "3.12.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.1.tgz", + "integrity": "sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "resolve": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", + "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "dev": true, + "requires": { + "glob": "^7.0.5" + } + }, + "semver": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", + "dev": true + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "tslib": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", + "dev": true + }, + "tslint": { + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.12.1.tgz", + "integrity": "sha512-sfodBHOucFg6egff8d1BvuofoOQ/nOeYNfbp7LDlKBcLNrL3lmS5zoiDGyOMdT7YsEXAwWpTdAHwOGOc8eRZAw==", + "dev": true, + "requires": { + "babel-code-frame": "^6.22.0", + "builtin-modules": "^1.1.1", + "chalk": "^2.3.0", + "commander": "^2.12.1", + "diff": "^3.2.0", + "glob": "^7.1.1", + "js-yaml": "^3.7.0", + "minimatch": "^3.0.4", + "resolve": "^1.3.2", + "semver": "^5.3.0", + "tslib": "^1.8.0", + "tsutils": "^2.27.2" + } + }, + "tsutils": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", + "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "typescript": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz", + "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + } + } +} diff --git a/src/JSInterop/Microsoft.JSInterop.JS/src/package.json b/src/JSInterop/Microsoft.JSInterop.JS/src/package.json index 803e02a35b85..8f04908e7877 100644 --- a/src/JSInterop/Microsoft.JSInterop.JS/src/package.json +++ b/src/JSInterop/Microsoft.JSInterop.JS/src/package.json @@ -1,6 +1,6 @@ { - "name": "@dotnet/jsinterop", - "version": "3.0.0-dev", + "name": "@microsoft/dotnet-js-interop", + "version": "5.0.0-dev", "description": "Provides abstractions and features for interop between .NET and JavaScript code.", "main": "dist/Microsoft.JSInterop.js", "types": "dist/Microsoft.JSInterop.d.js", From 6a1b15596723633b77b1bfb156e58880624bf014 Mon Sep 17 00:00:00 2001 From: Chris Ross Date: Mon, 25 Nov 2019 21:23:57 -0800 Subject: [PATCH 160/183] Adding MaximumOSVersionAttribute (dotnet/extensions#2715) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/9f29643656b25b53221de8d11c4b98c7b118f3e1 --- .../ref/Microsoft.AspNetCore.Testing.net46.cs | 7 ++ ...osoft.AspNetCore.Testing.netstandard2.0.cs | 7 ++ .../src/xunit/MaximumOSVersionAttribute.cs | 79 +++++++++++++++++++ .../test/MaximumOSVersionAttributeTest.cs | 77 ++++++++++++++++++ src/Testing/test/MaximumOSVersionTest.cs | 75 ++++++++++++++++++ 5 files changed, 245 insertions(+) create mode 100644 src/Testing/src/xunit/MaximumOSVersionAttribute.cs create mode 100644 src/Testing/test/MaximumOSVersionAttributeTest.cs create mode 100644 src/Testing/test/MaximumOSVersionTest.cs diff --git a/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs b/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs index 5fa7e9422e76..a7191eb156fc 100644 --- a/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs +++ b/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs @@ -170,6 +170,13 @@ public partial interface ITestMethodLifecycle System.Threading.Tasks.Task OnTestStartAsync(Microsoft.AspNetCore.Testing.TestContext context, System.Threading.CancellationToken cancellationToken); } [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=true)] + public partial class MaximumOSVersionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition + { + public MaximumOSVersionAttribute(Microsoft.AspNetCore.Testing.OperatingSystems operatingSystem, string maxVersion) { } + public bool IsMet { get { throw null; } } + public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=true)] public partial class MinimumOSVersionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition { public MinimumOSVersionAttribute(Microsoft.AspNetCore.Testing.OperatingSystems operatingSystem, string minVersion) { } diff --git a/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs b/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs index 5fa7e9422e76..a7191eb156fc 100644 --- a/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs +++ b/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs @@ -170,6 +170,13 @@ public partial interface ITestMethodLifecycle System.Threading.Tasks.Task OnTestStartAsync(Microsoft.AspNetCore.Testing.TestContext context, System.Threading.CancellationToken cancellationToken); } [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=true)] + public partial class MaximumOSVersionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition + { + public MaximumOSVersionAttribute(Microsoft.AspNetCore.Testing.OperatingSystems operatingSystem, string maxVersion) { } + public bool IsMet { get { throw null; } } + public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=true)] public partial class MinimumOSVersionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition { public MinimumOSVersionAttribute(Microsoft.AspNetCore.Testing.OperatingSystems operatingSystem, string minVersion) { } diff --git a/src/Testing/src/xunit/MaximumOSVersionAttribute.cs b/src/Testing/src/xunit/MaximumOSVersionAttribute.cs new file mode 100644 index 000000000000..82d59910befe --- /dev/null +++ b/src/Testing/src/xunit/MaximumOSVersionAttribute.cs @@ -0,0 +1,79 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Runtime.InteropServices; + +namespace Microsoft.AspNetCore.Testing +{ + /// + /// Skips a test if the OS is the given type (Windows) and the OS version is greater than specified. + /// E.g. Specifying Window 8 skips on Win 10, but not on Linux. Combine with OSSkipConditionAttribute as needed. + /// + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly, AllowMultiple = true)] + public class MaximumOSVersionAttribute : Attribute, ITestCondition + { + private readonly OperatingSystems _targetOS; + private readonly Version _maxVersion; + private readonly OperatingSystems _currentOS; + private readonly Version _currentVersion; + private readonly bool _skip; + + public MaximumOSVersionAttribute(OperatingSystems operatingSystem, string maxVersion) : + this(operatingSystem, Version.Parse(maxVersion), GetCurrentOS(), GetCurrentOSVersion()) + { + } + + // to enable unit testing + internal MaximumOSVersionAttribute(OperatingSystems targetOS, Version maxVersion, OperatingSystems currentOS, Version currentVersion) + { + if (targetOS != OperatingSystems.Windows) + { + throw new NotImplementedException("Max version support is only implemented for Windows."); + } + _targetOS = targetOS; + _maxVersion = maxVersion; + _currentOS = currentOS; + _currentVersion = currentVersion; + + // Do not skip other OS's, Use OSSkipConditionAttribute or a separate MaximumOsVersionAttribute for that. + _skip = _targetOS == _currentOS && _maxVersion < _currentVersion; + SkipReason = $"This test requires {_targetOS} {_maxVersion} or earlier."; + } + + // Since a test would be executed only if 'IsMet' is true, return false if we want to skip + public bool IsMet => !_skip; + + public string SkipReason { get; set; } + + private static OperatingSystems GetCurrentOS() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return OperatingSystems.Windows; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + return OperatingSystems.Linux; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + return OperatingSystems.MacOSX; + } + throw new PlatformNotSupportedException(); + } + + static private Version GetCurrentOSVersion() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return Environment.OSVersion.Version; + } + else + { + // Not implemented, but this will still be called before the OS check happens so don't throw. + return new Version(0, 0); + } + } + } +} diff --git a/src/Testing/test/MaximumOSVersionAttributeTest.cs b/src/Testing/test/MaximumOSVersionAttributeTest.cs new file mode 100644 index 000000000000..ca2faa76e537 --- /dev/null +++ b/src/Testing/test/MaximumOSVersionAttributeTest.cs @@ -0,0 +1,77 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Xunit; + +namespace Microsoft.AspNetCore.Testing +{ + public class MaximumOSVersionAttributeTest + { + [Fact] + public void Linux_ThrowsNotImplemeneted() + { + Assert.Throws(() => new MaximumOSVersionAttribute(OperatingSystems.Linux, "2.5")); + } + + [Fact] + public void Mac_ThrowsNotImplemeneted() + { + Assert.Throws(() => new MaximumOSVersionAttribute(OperatingSystems.MacOSX, "2.5")); + } + + [Fact] + public void WindowsOrLinux_ThrowsNotImplemeneted() + { + Assert.Throws(() => new MaximumOSVersionAttribute(OperatingSystems.Linux | OperatingSystems.Windows, "2.5")); + } + + [Fact] + public void DoesNotSkip_EarlierVersions() + { + var osSkipAttribute = new MaximumOSVersionAttribute( + OperatingSystems.Windows, + new Version("2.5"), + OperatingSystems.Windows, + new Version("2.0")); + + Assert.True(osSkipAttribute.IsMet); + } + + [Fact] + public void DoesNotSkip_SameVersion() + { + var osSkipAttribute = new MaximumOSVersionAttribute( + OperatingSystems.Windows, + new Version("2.5"), + OperatingSystems.Windows, + new Version("2.5")); + + Assert.True(osSkipAttribute.IsMet); + } + + [Fact] + public void Skip_LaterVersion() + { + var osSkipAttribute = new MaximumOSVersionAttribute( + OperatingSystems.Windows, + new Version("2.5"), + OperatingSystems.Windows, + new Version("3.0")); + + Assert.False(osSkipAttribute.IsMet); + } + + [Fact] + public void DoesNotSkip_WhenOnlyVersionsMatch() + { + var osSkipAttribute = new MaximumOSVersionAttribute( + OperatingSystems.Windows, + new Version("2.5"), + OperatingSystems.Linux, + new Version("2.5")); + + Assert.True(osSkipAttribute.IsMet); + } + } +} diff --git a/src/Testing/test/MaximumOSVersionTest.cs b/src/Testing/test/MaximumOSVersionTest.cs new file mode 100644 index 000000000000..e9a0b08d99d5 --- /dev/null +++ b/src/Testing/test/MaximumOSVersionTest.cs @@ -0,0 +1,75 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Runtime.InteropServices; +using Microsoft.Win32; +using Xunit; + +namespace Microsoft.AspNetCore.Testing +{ + [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)] + public class MaximumOSVersionTest + { + [ConditionalFact] + [MaximumOSVersion(OperatingSystems.Windows, WindowsVersions.Win7)] + public void RunTest_Win7DoesRunOnWin7() + { + Assert.True( + RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && + Environment.OSVersion.Version.ToString().StartsWith("6.1"), + "Test should only be running on Win7 or Win2008R2."); + } + + [ConditionalTheory] + [MaximumOSVersion(OperatingSystems.Windows, WindowsVersions.Win7)] + [InlineData(1)] + public void RunTheory_Win7DoesRunOnWin7(int arg) + { + Assert.True( + RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && + Environment.OSVersion.Version.ToString().StartsWith("6.1"), + "Test should only be running on Win7 or Win2008R2."); + } + + [ConditionalFact] + [MaximumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10_RS4)] + [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)] + public void RunTest_Win10_RS4() + { + Assert.True(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)); + var versionKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion"); + Assert.NotNull(versionKey); + var currentVersion = (string)versionKey.GetValue("CurrentBuildNumber"); + Assert.NotNull(currentVersion); + Assert.True(17134 >= int.Parse(currentVersion)); + } + + [ConditionalFact] + [MaximumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10_19H2)] + [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)] + public void RunTest_Win10_19H2() + { + Assert.True(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)); + var versionKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion"); + Assert.NotNull(versionKey); + var currentVersion = (string)versionKey.GetValue("CurrentBuildNumber"); + Assert.NotNull(currentVersion); + Assert.True(18363 >= int.Parse(currentVersion)); + } + } + + [MaximumOSVersion(OperatingSystems.Windows, WindowsVersions.Win7)] + [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)] + public class OSMaxVersionClassTest + { + [ConditionalFact] + public void TestSkipClass_Win7DoesRunOnWin7() + { + Assert.True( + RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && + Environment.OSVersion.Version.ToString().StartsWith("6.1"), + "Test should only be running on Win7 or Win2008R2."); + } + } +} From 4d04c24f5bf33b12d534e0caf5f99b7f52ab9a49 Mon Sep 17 00:00:00 2001 From: Chris Ross Date: Wed, 27 Nov 2019 22:10:34 -0800 Subject: [PATCH 161/183] Better MaxOsVersion tests, more WindowsVersions (dotnet/extensions#2728) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/ec155951d69dc8ef620bbf9bae723cda6f98b5fc --- .../ref/Microsoft.AspNetCore.Testing.net46.cs | 4 +++- ...icrosoft.AspNetCore.Testing.netstandard2.0.cs | 4 +++- .../src/xunit/MaximumOSVersionAttribute.cs | 3 ++- src/Testing/src/xunit/WindowsVersions.cs | 14 ++++++++++++-- .../test/MaximumOSVersionAttributeTest.cs | 16 ++++++++-------- 5 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs b/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs index a7191eb156fc..eb901008cc17 100644 --- a/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs +++ b/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs @@ -326,9 +326,11 @@ public static partial class TestPlatformHelper public static partial class WindowsVersions { public const string Win10 = "10.0"; + public const string Win10_19H1 = "10.0.18362"; public const string Win10_19H2 = "10.0.18363"; - public const string Win10_20H1 = "10.0.18990"; + public const string Win10_20H1 = "10.0.19033"; public const string Win10_RS4 = "10.0.17134"; + public const string Win10_RS5 = "10.0.17763"; public const string Win2008R2 = "6.1"; public const string Win7 = "6.1"; public const string Win8 = "6.2"; diff --git a/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs b/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs index a7191eb156fc..eb901008cc17 100644 --- a/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs +++ b/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs @@ -326,9 +326,11 @@ public static partial class TestPlatformHelper public static partial class WindowsVersions { public const string Win10 = "10.0"; + public const string Win10_19H1 = "10.0.18362"; public const string Win10_19H2 = "10.0.18363"; - public const string Win10_20H1 = "10.0.18990"; + public const string Win10_20H1 = "10.0.19033"; public const string Win10_RS4 = "10.0.17134"; + public const string Win10_RS5 = "10.0.17763"; public const string Win2008R2 = "6.1"; public const string Win7 = "6.1"; public const string Win8 = "6.2"; diff --git a/src/Testing/src/xunit/MaximumOSVersionAttribute.cs b/src/Testing/src/xunit/MaximumOSVersionAttribute.cs index 82d59910befe..439ba4c2295b 100644 --- a/src/Testing/src/xunit/MaximumOSVersionAttribute.cs +++ b/src/Testing/src/xunit/MaximumOSVersionAttribute.cs @@ -34,7 +34,8 @@ internal MaximumOSVersionAttribute(OperatingSystems targetOS, Version maxVersion _targetOS = targetOS; _maxVersion = maxVersion; _currentOS = currentOS; - _currentVersion = currentVersion; + // We drop the 4th field because it is not significant and it messes up the comparisons. + _currentVersion = new Version(currentVersion.Major, currentVersion.Minor, currentVersion.Build); // Do not skip other OS's, Use OSSkipConditionAttribute or a separate MaximumOsVersionAttribute for that. _skip = _targetOS == _currentOS && _maxVersion < _currentVersion; diff --git a/src/Testing/src/xunit/WindowsVersions.cs b/src/Testing/src/xunit/WindowsVersions.cs index d0ef86d1a840..a8c2b43048d9 100644 --- a/src/Testing/src/xunit/WindowsVersions.cs +++ b/src/Testing/src/xunit/WindowsVersions.cs @@ -23,14 +23,24 @@ public static class WindowsVersions /// public const string Win10_RS4 = "10.0.17134"; + /// + /// 1809, RS5, 17763 + /// + public const string Win10_RS5 = "10.0.17763"; + + /// + /// 1903, 19H1, 18362 + /// + public const string Win10_19H1 = "10.0.18362"; + /// /// 1909, 19H2, 18363 /// public const string Win10_19H2 = "10.0.18363"; /// - /// _, 20H2, 18990 + /// 2004, 20H1, 19033 /// - public const string Win10_20H1 = "10.0.18990"; + public const string Win10_20H1 = "10.0.19033"; } } diff --git a/src/Testing/test/MaximumOSVersionAttributeTest.cs b/src/Testing/test/MaximumOSVersionAttributeTest.cs index ca2faa76e537..a830af19bb56 100644 --- a/src/Testing/test/MaximumOSVersionAttributeTest.cs +++ b/src/Testing/test/MaximumOSVersionAttributeTest.cs @@ -31,9 +31,9 @@ public void DoesNotSkip_EarlierVersions() { var osSkipAttribute = new MaximumOSVersionAttribute( OperatingSystems.Windows, - new Version("2.5"), + new Version("2.5.9"), OperatingSystems.Windows, - new Version("2.0")); + new Version("2.0.10.12")); Assert.True(osSkipAttribute.IsMet); } @@ -43,9 +43,9 @@ public void DoesNotSkip_SameVersion() { var osSkipAttribute = new MaximumOSVersionAttribute( OperatingSystems.Windows, - new Version("2.5"), + new Version("2.5.10"), OperatingSystems.Windows, - new Version("2.5")); + new Version("2.5.10.12")); Assert.True(osSkipAttribute.IsMet); } @@ -55,9 +55,9 @@ public void Skip_LaterVersion() { var osSkipAttribute = new MaximumOSVersionAttribute( OperatingSystems.Windows, - new Version("2.5"), + new Version("2.5.11"), OperatingSystems.Windows, - new Version("3.0")); + new Version("3.0.10.12")); Assert.False(osSkipAttribute.IsMet); } @@ -67,9 +67,9 @@ public void DoesNotSkip_WhenOnlyVersionsMatch() { var osSkipAttribute = new MaximumOSVersionAttribute( OperatingSystems.Windows, - new Version("2.5"), + new Version("2.5.10.12"), OperatingSystems.Linux, - new Version("2.5")); + new Version("2.5.10.12")); Assert.True(osSkipAttribute.IsMet); } From 73da7ca7140bc9be91175645928bf7eb5da78ba8 Mon Sep 17 00:00:00 2001 From: Chris Ross Date: Mon, 2 Dec 2019 14:21:43 -0800 Subject: [PATCH 162/183] Fix MaxOsVersion on Ubuntu (dotnet/extensions#2738) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/e8491a2488c4ef514294ca9c8467f9f3e5157f54 --- .../src/xunit/MaximumOSVersionAttribute.cs | 5 ++++- src/Testing/test/MaximumOSVersionAttributeTest.cs | 12 ++++++++++++ src/Testing/test/MaximumOSVersionTest.cs | 15 +++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/Testing/src/xunit/MaximumOSVersionAttribute.cs b/src/Testing/src/xunit/MaximumOSVersionAttribute.cs index 439ba4c2295b..19ee1098d2c2 100644 --- a/src/Testing/src/xunit/MaximumOSVersionAttribute.cs +++ b/src/Testing/src/xunit/MaximumOSVersionAttribute.cs @@ -35,7 +35,10 @@ internal MaximumOSVersionAttribute(OperatingSystems targetOS, Version maxVersion _maxVersion = maxVersion; _currentOS = currentOS; // We drop the 4th field because it is not significant and it messes up the comparisons. - _currentVersion = new Version(currentVersion.Major, currentVersion.Minor, currentVersion.Build); + _currentVersion = new Version(currentVersion.Major, currentVersion.Minor, + // Major and Minor are required by the parser, but if Build isn't specified then it returns -1 + // which the constructor rejects. + currentVersion.Build == -1 ? 0 : currentVersion.Build); // Do not skip other OS's, Use OSSkipConditionAttribute or a separate MaximumOsVersionAttribute for that. _skip = _targetOS == _currentOS && _maxVersion < _currentVersion; diff --git a/src/Testing/test/MaximumOSVersionAttributeTest.cs b/src/Testing/test/MaximumOSVersionAttributeTest.cs index a830af19bb56..ca71d7063bd0 100644 --- a/src/Testing/test/MaximumOSVersionAttributeTest.cs +++ b/src/Testing/test/MaximumOSVersionAttributeTest.cs @@ -26,6 +26,18 @@ public void WindowsOrLinux_ThrowsNotImplemeneted() Assert.Throws(() => new MaximumOSVersionAttribute(OperatingSystems.Linux | OperatingSystems.Windows, "2.5")); } + [Fact] + public void DoesNotSkip_ShortVersions() + { + var osSkipAttribute = new MaximumOSVersionAttribute( + OperatingSystems.Windows, + new Version("2.5"), + OperatingSystems.Windows, + new Version("2.0")); + + Assert.True(osSkipAttribute.IsMet); + } + [Fact] public void DoesNotSkip_EarlierVersions() { diff --git a/src/Testing/test/MaximumOSVersionTest.cs b/src/Testing/test/MaximumOSVersionTest.cs index e9a0b08d99d5..e18d828fbf4b 100644 --- a/src/Testing/test/MaximumOSVersionTest.cs +++ b/src/Testing/test/MaximumOSVersionTest.cs @@ -72,4 +72,19 @@ public void TestSkipClass_Win7DoesRunOnWin7() "Test should only be running on Win7 or Win2008R2."); } } + + // Let this one run cross plat just to check the constructor logic. + [MaximumOSVersion(OperatingSystems.Windows, WindowsVersions.Win7)] + public class OSMaxVersionCrossPlatTest + { + [ConditionalFact] + public void TestSkipClass_Win7DoesRunOnWin7() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + Assert.True(Environment.OSVersion.Version.ToString().StartsWith("6.1"), + "Test should only be running on Win7 or Win2008R2."); + } + } + } } From 03059afd138f9085ab5d1fc9e967c032679c8a32 Mon Sep 17 00:00:00 2001 From: dotnet-maestro-bot Date: Wed, 4 Dec 2019 17:07:43 -0800 Subject: [PATCH 163/183] [automated] Merge branch 'release/3.0' => 'release/3.1' (dotnet/extensions#2748) * [release/3.0] Update dependencies from dotnet/core-setup (dotnet/extensions#2719) * Update dependencies from https://github.com/dotnet/core-setup build 20191126.03 - Microsoft.NETCore.App.Runtime.win-x64 - 3.0.2-servicing-19576-03 * Update dependencies from https://github.com/dotnet/core-setup build 20191126.05 - Microsoft.NETCore.App.Runtime.win-x64 - 3.0.2-servicing-19576-05 * Update dependencies from https://github.com/dotnet/core-setup build 20191126.08 - Microsoft.NETCore.App.Runtime.win-x64 - 3.0.2-servicing-19576-08 Dependency coherency updates - Microsoft.NETCore.Platforms - 3.0.1-servicing.19576.7 (parent: Microsoft.NETCore.App.Runtime.win-x64) * Update dependencies from https://github.com/dotnet/arcade build 20191127.5 (dotnet/extensions#2729) - Microsoft.DotNet.Arcade.Sdk - 1.0.0-beta.19577.5 - Microsoft.DotNet.GenAPI - 1.0.0-beta.19577.5 - Microsoft.DotNet.Helix.Sdk - 2.0.0-beta.19577.5 * Build implementation projects against ref assemblies * Add explicit references in tests, where needed \n\nCommit migrated from https://github.com/dotnet/extensions/commit/d40e21ccc14908a054b2181b1d6aeb22c49c630d --- .../Microsoft.Extensions.Configuration.KeyPerFile.Tests.csproj | 1 + .../Microsoft.Extensions.Diagnostics.HealthChecks.Tests.csproj | 1 + .../test/Microsoft.Extensions.WebEncoders.Tests.csproj | 1 + 3 files changed, 3 insertions(+) diff --git a/src/Configuration.KeyPerFile/test/Microsoft.Extensions.Configuration.KeyPerFile.Tests.csproj b/src/Configuration.KeyPerFile/test/Microsoft.Extensions.Configuration.KeyPerFile.Tests.csproj index 096cf2e2e0dc..7abceed7028d 100644 --- a/src/Configuration.KeyPerFile/test/Microsoft.Extensions.Configuration.KeyPerFile.Tests.csproj +++ b/src/Configuration.KeyPerFile/test/Microsoft.Extensions.Configuration.KeyPerFile.Tests.csproj @@ -15,6 +15,7 @@ + diff --git a/src/HealthChecks/HealthChecks/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests.csproj b/src/HealthChecks/HealthChecks/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests.csproj index b6fa1537da13..1591585b826e 100644 --- a/src/HealthChecks/HealthChecks/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests.csproj +++ b/src/HealthChecks/HealthChecks/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests.csproj @@ -17,6 +17,7 @@ + diff --git a/src/WebEncoders/test/Microsoft.Extensions.WebEncoders.Tests.csproj b/src/WebEncoders/test/Microsoft.Extensions.WebEncoders.Tests.csproj index 0f93bf3fc67d..3bf6fc1c9cd1 100755 --- a/src/WebEncoders/test/Microsoft.Extensions.WebEncoders.Tests.csproj +++ b/src/WebEncoders/test/Microsoft.Extensions.WebEncoders.Tests.csproj @@ -8,6 +8,7 @@ + From ee8715254db898065696de558650d2bfa488a900 Mon Sep 17 00:00:00 2001 From: Eddy Nakamura Date: Thu, 5 Dec 2019 08:07:00 -0300 Subject: [PATCH 164/183] updating inline to \n\nCommit migrated from https://github.com/dotnet/extensions/commit/e9515b009649cad0b76243b031137b3aa010a3ef --- .../Microsoft.JSInterop/src/IJSInProcessRuntime.cs | 2 +- src/JSInterop/Microsoft.JSInterop/src/IJSRuntime.cs | 4 ++-- .../Microsoft.JSInterop/src/JSInProcessRuntime.cs | 2 +- .../src/JSInProcessRuntimeExtensions.cs | 2 +- src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs | 4 ++-- .../Microsoft.JSInterop/src/JSRuntimeExtensions.cs | 12 ++++++------ src/ObjectPool/src/IPooledObjectPolicy.cs | 4 ++-- src/Testing/src/xunit/FlakyAttribute.cs | 2 +- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/JSInterop/Microsoft.JSInterop/src/IJSInProcessRuntime.cs b/src/JSInterop/Microsoft.JSInterop/src/IJSInProcessRuntime.cs index cae5126db22f..6da8d7f3a791 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/IJSInProcessRuntime.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/IJSInProcessRuntime.cs @@ -12,7 +12,7 @@ public interface IJSInProcessRuntime : IJSRuntime /// Invokes the specified JavaScript function synchronously. /// /// The JSON-serializable return type. - /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. /// JSON-serializable arguments. /// An instance of obtained by JSON-deserializing the return value. T Invoke(string identifier, params object[] args); diff --git a/src/JSInterop/Microsoft.JSInterop/src/IJSRuntime.cs b/src/JSInterop/Microsoft.JSInterop/src/IJSRuntime.cs index 6edc725177b4..26a7be400fc0 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/IJSRuntime.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/IJSRuntime.cs @@ -19,7 +19,7 @@ public interface IJSRuntime /// /// /// The JSON-serializable return type. - /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. /// JSON-serializable arguments. /// An instance of obtained by JSON-deserializing the return value. ValueTask InvokeAsync(string identifier, object[] args); @@ -28,7 +28,7 @@ public interface IJSRuntime /// Invokes the specified JavaScript function asynchronously. /// /// The JSON-serializable return type. - /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. /// /// A cancellation token to signal the cancellation of the operation. Specifying this parameter will override any default cancellations such as due to timeouts /// () from being applied. diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntime.cs b/src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntime.cs index 2b96bbbbb5bd..e84ab1b631cf 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntime.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntime.cs @@ -14,7 +14,7 @@ public abstract class JSInProcessRuntime : JSRuntime, IJSInProcessRuntime /// Invokes the specified JavaScript function synchronously. /// /// The JSON-serializable return type. - /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. /// JSON-serializable arguments. /// An instance of obtained by JSON-deserializing the return value. public TValue Invoke(string identifier, params object[] args) diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntimeExtensions.cs b/src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntimeExtensions.cs index 73bc2478480b..5a040d9b786c 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntimeExtensions.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntimeExtensions.cs @@ -14,7 +14,7 @@ public static class JSInProcessRuntimeExtensions /// Invokes the specified JavaScript function synchronously. /// /// The . - /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. /// JSON-serializable arguments. public static void InvokeVoid(this IJSInProcessRuntime jsRuntime, string identifier, params object[] args) { diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs b/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs index 4dca7a5db3f6..8a97ada85903 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs @@ -59,7 +59,7 @@ protected JSRuntime() /// /// /// The JSON-serializable return type. - /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. /// JSON-serializable arguments. /// An instance of obtained by JSON-deserializing the return value. public ValueTask InvokeAsync(string identifier, object[] args) @@ -76,7 +76,7 @@ public ValueTask InvokeAsync(string identifier, object[] args) /// Invokes the specified JavaScript function asynchronously. /// /// The JSON-serializable return type. - /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. /// /// A cancellation token to signal the cancellation of the operation. Specifying this parameter will override any default cancellations such as due to timeouts /// () from being applied. diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeExtensions.cs b/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeExtensions.cs index ff4d3fd1524d..354eddbbf660 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeExtensions.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeExtensions.cs @@ -16,7 +16,7 @@ public static class JSRuntimeExtensions /// Invokes the specified JavaScript function asynchronously. /// /// The . - /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. /// JSON-serializable arguments. /// A that represents the asynchronous invocation operation. public static async ValueTask InvokeVoidAsync(this IJSRuntime jsRuntime, string identifier, params object[] args) @@ -38,7 +38,7 @@ public static async ValueTask InvokeVoidAsync(this IJSRuntime jsRuntime, string /// /// The . /// The JSON-serializable return type. - /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. /// JSON-serializable arguments. /// An instance of obtained by JSON-deserializing the return value. public static ValueTask InvokeAsync(this IJSRuntime jsRuntime, string identifier, params object[] args) @@ -56,7 +56,7 @@ public static ValueTask InvokeAsync(this IJSRuntime jsRuntime, s /// /// The JSON-serializable return type. /// The . - /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. /// /// A cancellation token to signal the cancellation of the operation. Specifying this parameter will override any default cancellations such as due to timeouts /// () from being applied. @@ -77,7 +77,7 @@ public static ValueTask InvokeAsync(this IJSRuntime jsRuntime, s /// Invokes the specified JavaScript function asynchronously. /// /// The . - /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. /// /// A cancellation token to signal the cancellation of the operation. Specifying this parameter will override any default cancellations such as due to timeouts /// () from being applied. @@ -98,7 +98,7 @@ public static async ValueTask InvokeVoidAsync(this IJSRuntime jsRuntime, string /// Invokes the specified JavaScript function asynchronously. /// /// The . - /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. /// The duration after which to cancel the async operation. Overrides default timeouts (). /// JSON-serializable arguments. /// A that represents the asynchronous invocation operation. @@ -120,7 +120,7 @@ public static async ValueTask InvokeAsync(this IJSRuntime jsRunt /// Invokes the specified JavaScript function asynchronously. /// /// The . - /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. /// The duration after which to cancel the async operation. Overrides default timeouts (). /// JSON-serializable arguments. /// A that represents the asynchronous invocation operation. diff --git a/src/ObjectPool/src/IPooledObjectPolicy.cs b/src/ObjectPool/src/IPooledObjectPolicy.cs index 75193980300f..638fec2c8162 100644 --- a/src/ObjectPool/src/IPooledObjectPolicy.cs +++ b/src/ObjectPool/src/IPooledObjectPolicy.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace Microsoft.Extensions.ObjectPool @@ -19,7 +19,7 @@ public interface IPooledObjectPolicy /// Runs some processing when an object was returned to the pool. Can be used to reset the state of an object and indicate if the object should be returned to the pool. /// /// The object to return to the pool. - /// true if the object should be returned to the pool. false if it's not possible/desirable for the pool to keep the object. + /// true if the object should be returned to the pool. false if it's not possible/desirable for the pool to keep the object. bool Return(T obj); } } diff --git a/src/Testing/src/xunit/FlakyAttribute.cs b/src/Testing/src/xunit/FlakyAttribute.cs index daceb964fdf5..f77c56dfc0cf 100644 --- a/src/Testing/src/xunit/FlakyAttribute.cs +++ b/src/Testing/src/xunit/FlakyAttribute.cs @@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Testing /// properties. Once these traits are applied, build scripts can include/exclude tests based on them. /// /// - /// All flakiness-related traits start with Flaky: and are grouped first by the process running the tests: Azure Pipelines (AzP) or Helix. + /// All flakiness-related traits start with Flaky: and are grouped first by the process running the tests: Azure Pipelines (AzP) or Helix. /// Then there is a segment specifying the "selector" which indicates where the test is flaky. Finally a segment specifying the value of that selector. /// The value of these traits is always either "true" or the trait is not present. We encode the entire selector in the name of the trait because xUnit.net only /// provides "==" and "!=" operators for traits, there is no way to check if a trait "contains" or "does not contain" a value. VSTest does support "contains" checks From e8c39310746887942cea0aae6d9f71648faaf515 Mon Sep 17 00:00:00 2001 From: Eddy Nakamura Date: Thu, 5 Dec 2019 13:55:58 -0300 Subject: [PATCH 165/183] Apply suggestions from code review Co-Authored-By: Andrew Stanton-Nurse \n\nCommit migrated from https://github.com/dotnet/extensions/commit/c79268ee19fd3d81d312822d4abc8574a1629f9b --- src/ObjectPool/src/IPooledObjectPolicy.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ObjectPool/src/IPooledObjectPolicy.cs b/src/ObjectPool/src/IPooledObjectPolicy.cs index 638fec2c8162..458131e8247f 100644 --- a/src/ObjectPool/src/IPooledObjectPolicy.cs +++ b/src/ObjectPool/src/IPooledObjectPolicy.cs @@ -19,7 +19,7 @@ public interface IPooledObjectPolicy /// Runs some processing when an object was returned to the pool. Can be used to reset the state of an object and indicate if the object should be returned to the pool. /// /// The object to return to the pool. - /// true if the object should be returned to the pool. false if it's not possible/desirable for the pool to keep the object. + /// if the object should be returned to the pool. if it's not possible/desirable for the pool to keep the object. bool Return(T obj); } } From e467364175acbdda3e5affa81d802d71b37589c8 Mon Sep 17 00:00:00 2001 From: Chris Ross Date: Thu, 5 Dec 2019 10:58:15 -0800 Subject: [PATCH 166/183] Remove version checks from OSSkipCondition (dotnet/extensions#2755) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/0aa6213e1c4abd0cf45189e93751bf38b9f8c07c --- .../ref/Microsoft.AspNetCore.Testing.net46.cs | 3 + ...osoft.AspNetCore.Testing.netstandard2.0.cs | 3 + .../src/xunit/OSSkipConditionAttribute.cs | 57 +++----------- src/Testing/src/xunit/WindowsVersions.cs | 3 + .../test/OSSkipConditionAttributeTest.cs | 78 +++---------------- src/Testing/test/OSSkipConditionTest.cs | 11 --- 6 files changed, 28 insertions(+), 127 deletions(-) diff --git a/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs b/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs index eb901008cc17..af28ec60e049 100644 --- a/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs +++ b/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs @@ -193,6 +193,8 @@ public enum OperatingSystems [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=true)] public partial class OSSkipConditionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition { + public OSSkipConditionAttribute(Microsoft.AspNetCore.Testing.OperatingSystems operatingSystem) { } + [System.ObsoleteAttribute("Use the Minimum/MaximumOSVersionAttribute for version checks.", true)] public OSSkipConditionAttribute(Microsoft.AspNetCore.Testing.OperatingSystems operatingSystem, params string[] versions) { } public bool IsMet { get { throw null; } } public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } @@ -331,6 +333,7 @@ public static partial class WindowsVersions public const string Win10_20H1 = "10.0.19033"; public const string Win10_RS4 = "10.0.17134"; public const string Win10_RS5 = "10.0.17763"; + [System.ObsoleteAttribute("Use Win7 instead.", true)] public const string Win2008R2 = "6.1"; public const string Win7 = "6.1"; public const string Win8 = "6.2"; diff --git a/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs b/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs index eb901008cc17..af28ec60e049 100644 --- a/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs +++ b/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs @@ -193,6 +193,8 @@ public enum OperatingSystems [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=true)] public partial class OSSkipConditionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition { + public OSSkipConditionAttribute(Microsoft.AspNetCore.Testing.OperatingSystems operatingSystem) { } + [System.ObsoleteAttribute("Use the Minimum/MaximumOSVersionAttribute for version checks.", true)] public OSSkipConditionAttribute(Microsoft.AspNetCore.Testing.OperatingSystems operatingSystem, params string[] versions) { } public bool IsMet { get { throw null; } } public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } @@ -331,6 +333,7 @@ public static partial class WindowsVersions public const string Win10_20H1 = "10.0.19033"; public const string Win10_RS4 = "10.0.17134"; public const string Win10_RS5 = "10.0.17763"; + [System.ObsoleteAttribute("Use Win7 instead.", true)] public const string Win2008R2 = "6.1"; public const string Win7 = "6.1"; public const string Win8 = "6.2"; diff --git a/src/Testing/src/xunit/OSSkipConditionAttribute.cs b/src/Testing/src/xunit/OSSkipConditionAttribute.cs index 7655a3b45acc..50e3cae19217 100644 --- a/src/Testing/src/xunit/OSSkipConditionAttribute.cs +++ b/src/Testing/src/xunit/OSSkipConditionAttribute.cs @@ -2,8 +2,6 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Collections.Generic; -using System.Linq; using System.Runtime.InteropServices; namespace Microsoft.AspNetCore.Testing @@ -12,46 +10,31 @@ namespace Microsoft.AspNetCore.Testing public class OSSkipConditionAttribute : Attribute, ITestCondition { private readonly OperatingSystems _excludedOperatingSystem; - private readonly IEnumerable _excludedVersions; private readonly OperatingSystems _osPlatform; - private readonly string _osVersion; - public OSSkipConditionAttribute(OperatingSystems operatingSystem, params string[] versions) : - this( - operatingSystem, - GetCurrentOS(), - GetCurrentOSVersion(), - versions) + public OSSkipConditionAttribute(OperatingSystems operatingSystem) : + this(operatingSystem, GetCurrentOS()) + { + } + + [Obsolete("Use the Minimum/MaximumOSVersionAttribute for version checks.", error: true)] + public OSSkipConditionAttribute(OperatingSystems operatingSystem, params string[] versions) : + this(operatingSystem, GetCurrentOS()) { } // to enable unit testing - internal OSSkipConditionAttribute( - OperatingSystems operatingSystem, OperatingSystems osPlatform, string osVersion, params string[] versions) + internal OSSkipConditionAttribute(OperatingSystems operatingSystem, OperatingSystems osPlatform) { _excludedOperatingSystem = operatingSystem; - _excludedVersions = versions ?? Enumerable.Empty(); _osPlatform = osPlatform; - _osVersion = osVersion; } public bool IsMet { get { - var currentOSInfo = new OSInfo() - { - OperatingSystem = _osPlatform, - Version = _osVersion, - }; - - var skip = (_excludedOperatingSystem & currentOSInfo.OperatingSystem) == currentOSInfo.OperatingSystem; - if (_excludedVersions.Any()) - { - skip = skip - && _excludedVersions.Any(ex => _osVersion.StartsWith(ex, StringComparison.OrdinalIgnoreCase)); - } - + var skip = (_excludedOperatingSystem & _osPlatform) == _osPlatform; // Since a test would be excuted only if 'IsMet' is true, return false if we want to skip return !skip; } @@ -75,25 +58,5 @@ static private OperatingSystems GetCurrentOS() } throw new PlatformNotSupportedException(); } - - static private string GetCurrentOSVersion() - { - // currently not used on other OS's - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - return Environment.OSVersion.Version.ToString(); - } - else - { - return string.Empty; - } - } - - private class OSInfo - { - public OperatingSystems OperatingSystem { get; set; } - - public string Version { get; set; } - } } } diff --git a/src/Testing/src/xunit/WindowsVersions.cs b/src/Testing/src/xunit/WindowsVersions.cs index a8c2b43048d9..44448c74d191 100644 --- a/src/Testing/src/xunit/WindowsVersions.cs +++ b/src/Testing/src/xunit/WindowsVersions.cs @@ -1,6 +1,8 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; + namespace Microsoft.AspNetCore.Testing { /// @@ -10,6 +12,7 @@ public static class WindowsVersions { public const string Win7 = "6.1"; + [Obsolete("Use Win7 instead.", error: true)] public const string Win2008R2 = Win7; public const string Win8 = "6.2"; diff --git a/src/Testing/test/OSSkipConditionAttributeTest.cs b/src/Testing/test/OSSkipConditionAttributeTest.cs index 199af3ab6e60..d4bc4f2b7418 100644 --- a/src/Testing/test/OSSkipConditionAttributeTest.cs +++ b/src/Testing/test/OSSkipConditionAttributeTest.cs @@ -10,13 +10,12 @@ namespace Microsoft.AspNetCore.Testing public class OSSkipConditionAttributeTest { [Fact] - public void Skips_WhenOnlyOperatingSystemIsSupplied() + public void Skips_WhenOperatingSystemMatches() { // Act var osSkipAttribute = new OSSkipConditionAttribute( OperatingSystems.Windows, - OperatingSystems.Windows, - "2.5"); + OperatingSystems.Windows); // Assert Assert.False(osSkipAttribute.IsMet); @@ -28,77 +27,18 @@ public void DoesNotSkip_WhenOperatingSystemDoesNotMatch() // Act var osSkipAttribute = new OSSkipConditionAttribute( OperatingSystems.Linux, - OperatingSystems.Windows, - "2.5"); + OperatingSystems.Windows); // Assert Assert.True(osSkipAttribute.IsMet); } - [Fact] - public void DoesNotSkip_WhenVersionsDoNotMatch() - { - // Act - var osSkipAttribute = new OSSkipConditionAttribute( - OperatingSystems.Windows, - OperatingSystems.Windows, - "2.5", - "10.0"); - - // Assert - Assert.True(osSkipAttribute.IsMet); - } - - [Fact] - public void DoesNotSkip_WhenOnlyVersionsMatch() - { - // Act - var osSkipAttribute = new OSSkipConditionAttribute( - OperatingSystems.Linux, - OperatingSystems.Windows, - "2.5", - "2.5"); - - // Assert - Assert.True(osSkipAttribute.IsMet); - } - - [Theory] - [InlineData("2.5", "2.5")] - [InlineData("blue", "Blue")] - public void Skips_WhenVersionsMatches(string currentOSVersion, string skipVersion) - { - // Act - var osSkipAttribute = new OSSkipConditionAttribute( - OperatingSystems.Windows, - OperatingSystems.Windows, - currentOSVersion, - skipVersion); - - // Assert - Assert.False(osSkipAttribute.IsMet); - } - - [Fact] - public void Skips_WhenVersionsMatchesOutOfMultiple() - { - // Act - var osSkipAttribute = new OSSkipConditionAttribute( - OperatingSystems.Windows, - OperatingSystems.Windows, - "2.5", - "10.0", "3.4", "2.5"); - - // Assert - Assert.False(osSkipAttribute.IsMet); - } - [Fact] public void Skips_BothMacOSXAndLinux() { // Act - var osSkipAttributeLinux = new OSSkipConditionAttribute(OperatingSystems.Linux | OperatingSystems.MacOSX, OperatingSystems.Linux, string.Empty); - var osSkipAttributeMacOSX = new OSSkipConditionAttribute(OperatingSystems.Linux | OperatingSystems.MacOSX, OperatingSystems.MacOSX, string.Empty); + var osSkipAttributeLinux = new OSSkipConditionAttribute(OperatingSystems.Linux | OperatingSystems.MacOSX, OperatingSystems.Linux); + var osSkipAttributeMacOSX = new OSSkipConditionAttribute(OperatingSystems.Linux | OperatingSystems.MacOSX, OperatingSystems.MacOSX); // Assert Assert.False(osSkipAttributeLinux.IsMet); @@ -109,8 +49,8 @@ public void Skips_BothMacOSXAndLinux() public void Skips_BothMacOSXAndWindows() { // Act - var osSkipAttribute = new OSSkipConditionAttribute(OperatingSystems.Windows | OperatingSystems.MacOSX, OperatingSystems.Windows, string.Empty); - var osSkipAttributeMacOSX = new OSSkipConditionAttribute(OperatingSystems.Windows | OperatingSystems.MacOSX, OperatingSystems.MacOSX, string.Empty); + var osSkipAttribute = new OSSkipConditionAttribute(OperatingSystems.Windows | OperatingSystems.MacOSX, OperatingSystems.Windows); + var osSkipAttributeMacOSX = new OSSkipConditionAttribute(OperatingSystems.Windows | OperatingSystems.MacOSX, OperatingSystems.MacOSX); // Assert Assert.False(osSkipAttribute.IsMet); @@ -121,8 +61,8 @@ public void Skips_BothMacOSXAndWindows() public void Skips_BothWindowsAndLinux() { // Act - var osSkipAttribute = new OSSkipConditionAttribute(OperatingSystems.Linux | OperatingSystems.Windows, OperatingSystems.Windows, string.Empty); - var osSkipAttributeLinux = new OSSkipConditionAttribute(OperatingSystems.Linux | OperatingSystems.Windows, OperatingSystems.Linux, string.Empty); + var osSkipAttribute = new OSSkipConditionAttribute(OperatingSystems.Linux | OperatingSystems.Windows, OperatingSystems.Windows); + var osSkipAttributeLinux = new OSSkipConditionAttribute(OperatingSystems.Linux | OperatingSystems.Windows, OperatingSystems.Linux); // Assert Assert.False(osSkipAttribute.IsMet); diff --git a/src/Testing/test/OSSkipConditionTest.cs b/src/Testing/test/OSSkipConditionTest.cs index a7904b17305b..6aeecaddccb6 100644 --- a/src/Testing/test/OSSkipConditionTest.cs +++ b/src/Testing/test/OSSkipConditionTest.cs @@ -1,7 +1,6 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; using System.Runtime.InteropServices; using Xunit; @@ -27,16 +26,6 @@ public void TestSkipMacOSX() "Test should not be running on MacOSX."); } - [ConditionalFact] - [OSSkipCondition(OperatingSystems.Windows, WindowsVersions.Win7, WindowsVersions.Win2008R2)] - public void RunTest_DoesNotRunOnWin7OrWin2008R2() - { - Assert.False( - RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && - Environment.OSVersion.Version.ToString().StartsWith("6.1"), - "Test should not be running on Win7 or Win2008R2."); - } - [ConditionalFact] [OSSkipCondition(OperatingSystems.Windows)] public void TestSkipWindows() From bb08ec9c0455bdb0b9946eb389eb3d98311a7eb2 Mon Sep 17 00:00:00 2001 From: William Godbe Date: Thu, 12 Dec 2019 13:43:49 -0800 Subject: [PATCH 167/183] Set IsShipping=false for all TestingUtils projects (dotnet/extensions#2782) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/0743e1c118ae495da45edef900e392d187303b46 --- src/Testing/Directory.Build.props | 1 + src/Testing/src/Microsoft.AspNetCore.Testing.csproj | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Testing/Directory.Build.props b/src/Testing/Directory.Build.props index 68f87d4f2484..b49dba01d926 100644 --- a/src/Testing/Directory.Build.props +++ b/src/Testing/Directory.Build.props @@ -4,5 +4,6 @@ true + false diff --git a/src/Testing/src/Microsoft.AspNetCore.Testing.csproj b/src/Testing/src/Microsoft.AspNetCore.Testing.csproj index 70ac622461ac..448351fee566 100644 --- a/src/Testing/src/Microsoft.AspNetCore.Testing.csproj +++ b/src/Testing/src/Microsoft.AspNetCore.Testing.csproj @@ -9,7 +9,6 @@ false true - false true true From 3ab9051a117c0728412c8d63138c4699cc7df476 Mon Sep 17 00:00:00 2001 From: Ryan Brandenburg Date: Fri, 3 Jan 2020 09:34:44 -0800 Subject: [PATCH 168/183] Remove Obsoleted methods for 5.0 (dotnet/extensions#2562) Remove Obsoleted methods for 5.0 \n\nCommit migrated from https://github.com/dotnet/extensions/commit/582427c350fe8842553a8c33b4015662f95c1c78 --- ...ns.Localization.Abstractions.netcoreapp.cs | 4 - ...ocalization.Abstractions.netstandard2.0.cs | 4 - .../Abstractions/src/IStringLocalizer.cs | 8 - .../Abstractions/src/StringLocalizerOfT.cs | 4 - ...soft.Extensions.Localization.netcoreapp.cs | 10 -- ....Extensions.Localization.netstandard2.0.cs | 10 -- .../src/ResourceManagerStringLocalizer.cs | 24 --- ...sourceManagerWithCultureStringLocalizer.cs | 165 ------------------ .../StringLocalizerOfTTest.cs | 22 --- 9 files changed, 251 deletions(-) delete mode 100644 src/Localization/Localization/src/ResourceManagerWithCultureStringLocalizer.cs diff --git a/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netcoreapp.cs b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netcoreapp.cs index 8a5f5baa08c7..3b6186e98fa7 100644 --- a/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netcoreapp.cs +++ b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netcoreapp.cs @@ -8,8 +8,6 @@ public partial interface IStringLocalizer Microsoft.Extensions.Localization.LocalizedString this[string name] { get; } Microsoft.Extensions.Localization.LocalizedString this[string name, params object[] arguments] { get; } System.Collections.Generic.IEnumerable GetAllStrings(bool includeParentCultures); - [System.ObsoleteAttribute("This method is obsolete. Use `CurrentCulture` and `CurrentUICulture` instead.")] - Microsoft.Extensions.Localization.IStringLocalizer WithCulture(System.Globalization.CultureInfo culture); } public partial interface IStringLocalizerFactory { @@ -43,7 +41,5 @@ public StringLocalizer(Microsoft.Extensions.Localization.IStringLocalizerFactory public virtual Microsoft.Extensions.Localization.LocalizedString this[string name] { get { throw null; } } public virtual Microsoft.Extensions.Localization.LocalizedString this[string name, params object[] arguments] { get { throw null; } } public System.Collections.Generic.IEnumerable GetAllStrings(bool includeParentCultures) { throw null; } - [System.ObsoleteAttribute("This method is obsolete. Use `CurrentCulture` and `CurrentUICulture` instead.")] - public virtual Microsoft.Extensions.Localization.IStringLocalizer WithCulture(System.Globalization.CultureInfo culture) { throw null; } } } diff --git a/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netstandard2.0.cs b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netstandard2.0.cs index 8a5f5baa08c7..3b6186e98fa7 100644 --- a/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netstandard2.0.cs +++ b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netstandard2.0.cs @@ -8,8 +8,6 @@ public partial interface IStringLocalizer Microsoft.Extensions.Localization.LocalizedString this[string name] { get; } Microsoft.Extensions.Localization.LocalizedString this[string name, params object[] arguments] { get; } System.Collections.Generic.IEnumerable GetAllStrings(bool includeParentCultures); - [System.ObsoleteAttribute("This method is obsolete. Use `CurrentCulture` and `CurrentUICulture` instead.")] - Microsoft.Extensions.Localization.IStringLocalizer WithCulture(System.Globalization.CultureInfo culture); } public partial interface IStringLocalizerFactory { @@ -43,7 +41,5 @@ public StringLocalizer(Microsoft.Extensions.Localization.IStringLocalizerFactory public virtual Microsoft.Extensions.Localization.LocalizedString this[string name] { get { throw null; } } public virtual Microsoft.Extensions.Localization.LocalizedString this[string name, params object[] arguments] { get { throw null; } } public System.Collections.Generic.IEnumerable GetAllStrings(bool includeParentCultures) { throw null; } - [System.ObsoleteAttribute("This method is obsolete. Use `CurrentCulture` and `CurrentUICulture` instead.")] - public virtual Microsoft.Extensions.Localization.IStringLocalizer WithCulture(System.Globalization.CultureInfo culture) { throw null; } } } diff --git a/src/Localization/Abstractions/src/IStringLocalizer.cs b/src/Localization/Abstractions/src/IStringLocalizer.cs index cabdf434ac4d..206092cd2d64 100644 --- a/src/Localization/Abstractions/src/IStringLocalizer.cs +++ b/src/Localization/Abstractions/src/IStringLocalizer.cs @@ -35,13 +35,5 @@ public interface IStringLocalizer /// /// The strings. IEnumerable GetAllStrings(bool includeParentCultures); - - /// - /// Creates a new for a specific . - /// - /// The to use. - /// A culture-specific . - [Obsolete("This method is obsolete. Use `CurrentCulture` and `CurrentUICulture` instead.")] - IStringLocalizer WithCulture(CultureInfo culture); } } diff --git a/src/Localization/Abstractions/src/StringLocalizerOfT.cs b/src/Localization/Abstractions/src/StringLocalizerOfT.cs index 4190ca14ffca..0f30ca6c877e 100644 --- a/src/Localization/Abstractions/src/StringLocalizerOfT.cs +++ b/src/Localization/Abstractions/src/StringLocalizerOfT.cs @@ -29,10 +29,6 @@ public StringLocalizer(IStringLocalizerFactory factory) _localizer = factory.Create(typeof(TResourceSource)); } - /// - [Obsolete("This method is obsolete. Use `CurrentCulture` and `CurrentUICulture` instead.")] - public virtual IStringLocalizer WithCulture(CultureInfo culture) => _localizer.WithCulture(culture); - /// public virtual LocalizedString this[string name] { diff --git a/src/Localization/Localization/ref/Microsoft.Extensions.Localization.netcoreapp.cs b/src/Localization/Localization/ref/Microsoft.Extensions.Localization.netcoreapp.cs index 741e1ce10624..dea13ee3640d 100644 --- a/src/Localization/Localization/ref/Microsoft.Extensions.Localization.netcoreapp.cs +++ b/src/Localization/Localization/ref/Microsoft.Extensions.Localization.netcoreapp.cs @@ -36,8 +36,6 @@ public ResourceManagerStringLocalizer(System.Resources.ResourceManager resourceM public virtual System.Collections.Generic.IEnumerable GetAllStrings(bool includeParentCultures) { throw null; } protected System.Collections.Generic.IEnumerable GetAllStrings(bool includeParentCultures, System.Globalization.CultureInfo culture) { throw null; } protected string GetStringSafely(string name, System.Globalization.CultureInfo culture) { throw null; } - [System.ObsoleteAttribute("This method is obsolete. Use `CurrentCulture` and `CurrentUICulture` instead.")] - public Microsoft.Extensions.Localization.IStringLocalizer WithCulture(System.Globalization.CultureInfo culture) { throw null; } } public partial class ResourceManagerStringLocalizerFactory : Microsoft.Extensions.Localization.IStringLocalizerFactory { @@ -52,14 +50,6 @@ public ResourceManagerStringLocalizerFactory(Microsoft.Extensions.Options.IOptio protected virtual string GetResourcePrefix(string location, string baseName, string resourceLocation) { throw null; } protected virtual Microsoft.Extensions.Localization.RootNamespaceAttribute GetRootNamespaceAttribute(System.Reflection.Assembly assembly) { throw null; } } - [System.ObsoleteAttribute("This method is obsolete. Use `CurrentCulture` and `CurrentUICulture` instead.")] - public partial class ResourceManagerWithCultureStringLocalizer : Microsoft.Extensions.Localization.ResourceManagerStringLocalizer - { - public ResourceManagerWithCultureStringLocalizer(System.Resources.ResourceManager resourceManager, System.Reflection.Assembly resourceAssembly, string baseName, Microsoft.Extensions.Localization.IResourceNamesCache resourceNamesCache, System.Globalization.CultureInfo culture, Microsoft.Extensions.Logging.ILogger logger) : base (default(System.Resources.ResourceManager), default(System.Reflection.Assembly), default(string), default(Microsoft.Extensions.Localization.IResourceNamesCache), default(Microsoft.Extensions.Logging.ILogger)) { } - public override Microsoft.Extensions.Localization.LocalizedString this[string name] { get { throw null; } } - public override Microsoft.Extensions.Localization.LocalizedString this[string name, params object[] arguments] { get { throw null; } } - public override System.Collections.Generic.IEnumerable GetAllStrings(bool includeParentCultures) { throw null; } - } public partial class ResourceNamesCache : Microsoft.Extensions.Localization.IResourceNamesCache { public ResourceNamesCache() { } diff --git a/src/Localization/Localization/ref/Microsoft.Extensions.Localization.netstandard2.0.cs b/src/Localization/Localization/ref/Microsoft.Extensions.Localization.netstandard2.0.cs index 741e1ce10624..dea13ee3640d 100644 --- a/src/Localization/Localization/ref/Microsoft.Extensions.Localization.netstandard2.0.cs +++ b/src/Localization/Localization/ref/Microsoft.Extensions.Localization.netstandard2.0.cs @@ -36,8 +36,6 @@ public ResourceManagerStringLocalizer(System.Resources.ResourceManager resourceM public virtual System.Collections.Generic.IEnumerable GetAllStrings(bool includeParentCultures) { throw null; } protected System.Collections.Generic.IEnumerable GetAllStrings(bool includeParentCultures, System.Globalization.CultureInfo culture) { throw null; } protected string GetStringSafely(string name, System.Globalization.CultureInfo culture) { throw null; } - [System.ObsoleteAttribute("This method is obsolete. Use `CurrentCulture` and `CurrentUICulture` instead.")] - public Microsoft.Extensions.Localization.IStringLocalizer WithCulture(System.Globalization.CultureInfo culture) { throw null; } } public partial class ResourceManagerStringLocalizerFactory : Microsoft.Extensions.Localization.IStringLocalizerFactory { @@ -52,14 +50,6 @@ public ResourceManagerStringLocalizerFactory(Microsoft.Extensions.Options.IOptio protected virtual string GetResourcePrefix(string location, string baseName, string resourceLocation) { throw null; } protected virtual Microsoft.Extensions.Localization.RootNamespaceAttribute GetRootNamespaceAttribute(System.Reflection.Assembly assembly) { throw null; } } - [System.ObsoleteAttribute("This method is obsolete. Use `CurrentCulture` and `CurrentUICulture` instead.")] - public partial class ResourceManagerWithCultureStringLocalizer : Microsoft.Extensions.Localization.ResourceManagerStringLocalizer - { - public ResourceManagerWithCultureStringLocalizer(System.Resources.ResourceManager resourceManager, System.Reflection.Assembly resourceAssembly, string baseName, Microsoft.Extensions.Localization.IResourceNamesCache resourceNamesCache, System.Globalization.CultureInfo culture, Microsoft.Extensions.Logging.ILogger logger) : base (default(System.Resources.ResourceManager), default(System.Reflection.Assembly), default(string), default(Microsoft.Extensions.Localization.IResourceNamesCache), default(Microsoft.Extensions.Logging.ILogger)) { } - public override Microsoft.Extensions.Localization.LocalizedString this[string name] { get { throw null; } } - public override Microsoft.Extensions.Localization.LocalizedString this[string name, params object[] arguments] { get { throw null; } } - public override System.Collections.Generic.IEnumerable GetAllStrings(bool includeParentCultures) { throw null; } - } public partial class ResourceNamesCache : Microsoft.Extensions.Localization.IResourceNamesCache { public ResourceNamesCache() { } diff --git a/src/Localization/Localization/src/ResourceManagerStringLocalizer.cs b/src/Localization/Localization/src/ResourceManagerStringLocalizer.cs index a8321fca0ade..4f13611a47d9 100644 --- a/src/Localization/Localization/src/ResourceManagerStringLocalizer.cs +++ b/src/Localization/Localization/src/ResourceManagerStringLocalizer.cs @@ -146,30 +146,6 @@ public virtual LocalizedString this[string name] } } - /// - /// Creates a new for a specific . - /// - /// The to use. - /// A culture-specific . - [Obsolete("This method is obsolete. Use `CurrentCulture` and `CurrentUICulture` instead.")] - public IStringLocalizer WithCulture(CultureInfo culture) - { - return culture == null - ? new ResourceManagerStringLocalizer( - _resourceManager, - _resourceStringProvider, - _resourceBaseName, - _resourceNamesCache, - _logger) - : new ResourceManagerWithCultureStringLocalizer( - _resourceManager, - _resourceStringProvider, - _resourceBaseName, - _resourceNamesCache, - culture, - _logger); - } - /// public virtual IEnumerable GetAllStrings(bool includeParentCultures) => GetAllStrings(includeParentCultures, CultureInfo.CurrentUICulture); diff --git a/src/Localization/Localization/src/ResourceManagerWithCultureStringLocalizer.cs b/src/Localization/Localization/src/ResourceManagerWithCultureStringLocalizer.cs deleted file mode 100644 index 2bc51289da14..000000000000 --- a/src/Localization/Localization/src/ResourceManagerWithCultureStringLocalizer.cs +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Reflection; -using System.Resources; -using Microsoft.Extensions.Localization.Internal; -using Microsoft.Extensions.Logging; - -namespace Microsoft.Extensions.Localization -{ - /// - /// An that uses the and - /// to provide localized strings for a specific . - /// - [Obsolete("This method is obsolete. Use `CurrentCulture` and `CurrentUICulture` instead.")] - public class ResourceManagerWithCultureStringLocalizer : ResourceManagerStringLocalizer - { - private readonly string _resourceBaseName; - private readonly CultureInfo _culture; - - /// - /// Creates a new . - /// - /// The to read strings from. - /// The that can find the resources. - /// The base name of the embedded resource that contains the strings. - /// Cache of the list of strings for a given resource assembly name. - /// The specific to use. - /// The . - internal ResourceManagerWithCultureStringLocalizer( - ResourceManager resourceManager, - IResourceStringProvider resourceStringProvider, - string baseName, - IResourceNamesCache resourceNamesCache, - CultureInfo culture, - ILogger logger) - : base(resourceManager, resourceStringProvider, baseName, resourceNamesCache, logger) - { - if (resourceManager == null) - { - throw new ArgumentNullException(nameof(resourceManager)); - } - - if (resourceStringProvider == null) - { - throw new ArgumentNullException(nameof(resourceStringProvider)); - } - - if (baseName == null) - { - throw new ArgumentNullException(nameof(baseName)); - } - - if (resourceNamesCache == null) - { - throw new ArgumentNullException(nameof(resourceNamesCache)); - } - - if (culture == null) - { - throw new ArgumentNullException(nameof(culture)); - } - - if (logger == null) - { - throw new ArgumentNullException(nameof(logger)); - } - - _resourceBaseName = baseName; - _culture = culture; - } - - /// - /// Creates a new . - /// - /// The to read strings from. - /// The that contains the strings as embedded resources. - /// The base name of the embedded resource that contains the strings. - /// Cache of the list of strings for a given resource assembly name. - /// The specific to use. - /// The . - public ResourceManagerWithCultureStringLocalizer( - ResourceManager resourceManager, - Assembly resourceAssembly, - string baseName, - IResourceNamesCache resourceNamesCache, - CultureInfo culture, - ILogger logger) - : base(resourceManager, resourceAssembly, baseName, resourceNamesCache, logger) - { - if (resourceManager == null) - { - throw new ArgumentNullException(nameof(resourceManager)); - } - - if (resourceAssembly == null) - { - throw new ArgumentNullException(nameof(resourceAssembly)); - } - - if (baseName == null) - { - throw new ArgumentNullException(nameof(baseName)); - } - - if (resourceNamesCache == null) - { - throw new ArgumentNullException(nameof(resourceNamesCache)); - } - - if (culture == null) - { - throw new ArgumentNullException(nameof(culture)); - } - - if (logger == null) - { - throw new ArgumentNullException(nameof(logger)); - } - - _resourceBaseName = baseName; - _culture = culture; - } - - /// - public override LocalizedString this[string name] - { - get - { - if (name == null) - { - throw new ArgumentNullException(nameof(name)); - } - - var value = GetStringSafely(name, _culture); - - return new LocalizedString(name, value ?? name, resourceNotFound: value == null, searchedLocation: _resourceBaseName); - } - } - - /// - public override LocalizedString this[string name, params object[] arguments] - { - get - { - if (name == null) - { - throw new ArgumentNullException(nameof(name)); - } - - var format = GetStringSafely(name, _culture); - var value = string.Format(_culture, format ?? name, arguments); - - return new LocalizedString(name, value, resourceNotFound: format == null, searchedLocation: _resourceBaseName); - } - } - - /// - public override IEnumerable GetAllStrings(bool includeParentCultures) => - GetAllStrings(includeParentCultures, _culture); - } -} diff --git a/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests/StringLocalizerOfTTest.cs b/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests/StringLocalizerOfTTest.cs index ce06e74d1c21..53d290064e44 100644 --- a/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests/StringLocalizerOfTTest.cs +++ b/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests/StringLocalizerOfTTest.cs @@ -33,28 +33,6 @@ public void Constructor_ResolvesLocalizerFromFactory() factory.Verify(mock => mock.Create(typeof(object)), Times.Once()); } - [Fact] - public void WithCulture_InvokesWithCultureFromInnerLocalizer() - { - // Arrange - var factory = new Mock(); - var innerLocalizer = new Mock(); - factory.Setup(mock => mock.Create(typeof(object))) - .Returns(innerLocalizer.Object); - - var localizer = new StringLocalizer(factory.Object); - - // Act -#pragma warning disable CS0618 - localizer.WithCulture(CultureInfo.GetCultureInfo("fr-FR")); -#pragma warning restore CS0618 - - // Assert -#pragma warning disable CS0618 - innerLocalizer.Verify(mock => mock.WithCulture(CultureInfo.GetCultureInfo("fr-FR")), Times.Once()); -#pragma warning restore CS0618 - } - [Fact] public void Indexer_ThrowsAnExceptionForNullName() { From d65b7239a68eff63dd175a0ca1d37ccc956a798a Mon Sep 17 00:00:00 2001 From: John Luo Date: Mon, 6 Jan 2020 11:40:44 -0800 Subject: [PATCH 169/183] Preserve functional test logs on CI (dotnet/extensions#2819) * Add option to preserve function test logs * Upload test logs as artifacts * Preserve binlogs * Add target to ensure all functional test logs preserved \n\nCommit migrated from https://github.com/dotnet/extensions/commit/08aa4560e589002f8f2444e4b27b8f0930cfbb46 --- src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs | 4 +++- .../ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs | 4 +++- src/Testing/src/TestFileOutputContext.cs | 6 ++++++ src/Testing/src/TestOutputDirectoryAttribute.cs | 4 +++- 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs b/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs index af28ec60e049..e737371a8c80 100644 --- a/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs +++ b/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs @@ -295,6 +295,7 @@ public TestFileOutputContext(Microsoft.AspNetCore.Testing.TestContext parent) { public string TestName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } public static string GetAssemblyBaseDirectory(System.Reflection.Assembly assembly, string baseDirectory = null) { throw null; } public static string GetOutputDirectory(System.Reflection.Assembly assembly) { throw null; } + public static bool GetPreserveExistingLogsInOutput(System.Reflection.Assembly assembly) { throw null; } public static string GetTestClassName(System.Type type) { throw null; } public static string GetTestMethodName(System.Reflection.MethodInfo method, object[] arguments) { throw null; } public string GetUniqueFileName(string prefix, string extension) { throw null; } @@ -307,8 +308,9 @@ public static partial class TestMethodExtensions [System.AttributeUsageAttribute(System.AttributeTargets.Assembly, AllowMultiple=false, Inherited=true)] public partial class TestOutputDirectoryAttribute : System.Attribute { - public TestOutputDirectoryAttribute(string targetFramework, string baseDirectory = null) { } + public TestOutputDirectoryAttribute(string preserveExistingLogsInOutput, string targetFramework, string baseDirectory = null) { } public string BaseDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public bool PreserveExistingLogsInOutput { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } public string TargetFramework { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } } [System.ObsoleteAttribute("This API is obsolete and the pattern its usage encouraged should not be used anymore. See https://github.com/aspnet/Extensions/issues/1697 for details.")] diff --git a/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs b/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs index af28ec60e049..e737371a8c80 100644 --- a/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs +++ b/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs @@ -295,6 +295,7 @@ public TestFileOutputContext(Microsoft.AspNetCore.Testing.TestContext parent) { public string TestName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } public static string GetAssemblyBaseDirectory(System.Reflection.Assembly assembly, string baseDirectory = null) { throw null; } public static string GetOutputDirectory(System.Reflection.Assembly assembly) { throw null; } + public static bool GetPreserveExistingLogsInOutput(System.Reflection.Assembly assembly) { throw null; } public static string GetTestClassName(System.Type type) { throw null; } public static string GetTestMethodName(System.Reflection.MethodInfo method, object[] arguments) { throw null; } public string GetUniqueFileName(string prefix, string extension) { throw null; } @@ -307,8 +308,9 @@ public static partial class TestMethodExtensions [System.AttributeUsageAttribute(System.AttributeTargets.Assembly, AllowMultiple=false, Inherited=true)] public partial class TestOutputDirectoryAttribute : System.Attribute { - public TestOutputDirectoryAttribute(string targetFramework, string baseDirectory = null) { } + public TestOutputDirectoryAttribute(string preserveExistingLogsInOutput, string targetFramework, string baseDirectory = null) { } public string BaseDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public bool PreserveExistingLogsInOutput { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } public string TargetFramework { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } } [System.ObsoleteAttribute("This API is obsolete and the pattern its usage encouraged should not be used anymore. See https://github.com/aspnet/Extensions/issues/1697 for details.")] diff --git a/src/Testing/src/TestFileOutputContext.cs b/src/Testing/src/TestFileOutputContext.cs index 496a1379fb6c..fb79fd7bf79c 100644 --- a/src/Testing/src/TestFileOutputContext.cs +++ b/src/Testing/src/TestFileOutputContext.cs @@ -93,6 +93,12 @@ public static string GetAssemblyBaseDirectory(Assembly assembly, string baseDire return Path.Combine(baseDirectory, assembly.GetName().Name, attribute.TargetFramework); } + public static bool GetPreserveExistingLogsInOutput(Assembly assembly) + { + var attribute = assembly.GetCustomAttributes().OfType().FirstOrDefault(); + return attribute.PreserveExistingLogsInOutput; + } + public static string GetTestClassName(Type type) { var shortNameAttribute = diff --git a/src/Testing/src/TestOutputDirectoryAttribute.cs b/src/Testing/src/TestOutputDirectoryAttribute.cs index 4ae8cea05457..b1895c1d922a 100644 --- a/src/Testing/src/TestOutputDirectoryAttribute.cs +++ b/src/Testing/src/TestOutputDirectoryAttribute.cs @@ -8,13 +8,15 @@ namespace Microsoft.AspNetCore.Testing [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false, Inherited = true)] public class TestOutputDirectoryAttribute : Attribute { - public TestOutputDirectoryAttribute(string targetFramework, string baseDirectory = null) + public TestOutputDirectoryAttribute(string preserveExistingLogsInOutput, string targetFramework, string baseDirectory = null) { TargetFramework = targetFramework; BaseDirectory = baseDirectory; + PreserveExistingLogsInOutput = bool.Parse(preserveExistingLogsInOutput); } public string BaseDirectory { get; } public string TargetFramework { get; } + public bool PreserveExistingLogsInOutput { get; } } } From 639de3f5f48ebda1779a1c53a223f01a9dad7fc0 Mon Sep 17 00:00:00 2001 From: Kevin Pilch Date: Thu, 9 Jan 2020 13:39:52 -0800 Subject: [PATCH 170/183] Update links for repository move/rename (dotnet/extensions#2840) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/6b493684d90150b96b3057fdd0f45441bf9545d9 --- .../DependencyInjection/ServiceCollectionExtensionsTest.cs | 2 +- src/JSInterop/Microsoft.JSInterop.JS/src/package.json | 6 +++--- .../test/Infrastructure/DotNetDispatcherTest.cs | 6 +++--- src/Localization/README.md | 2 +- src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs | 2 +- .../ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs | 2 +- src/Testing/src/TestPathUtilities.cs | 2 +- src/Testing/test/TestPathUtilitiesTest.cs | 2 +- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/HealthChecks/HealthChecks/test/DependencyInjection/ServiceCollectionExtensionsTest.cs b/src/HealthChecks/HealthChecks/test/DependencyInjection/ServiceCollectionExtensionsTest.cs index 98371e9f139e..4a191a365f6c 100644 --- a/src/HealthChecks/HealthChecks/test/DependencyInjection/ServiceCollectionExtensionsTest.cs +++ b/src/HealthChecks/HealthChecks/test/DependencyInjection/ServiceCollectionExtensionsTest.cs @@ -42,7 +42,7 @@ public void AddHealthChecks_RegistersSingletonHealthCheckServiceIdempotently() }); } - [Fact] // see: https://github.com/aspnet/Extensions/issues/639 + [Fact] // see: https://github.com/dotnet/extensions/issues/639 public void AddHealthChecks_RegistersPublisherService_WhenOtherHostedServicesRegistered() { // Arrange diff --git a/src/JSInterop/Microsoft.JSInterop.JS/src/package.json b/src/JSInterop/Microsoft.JSInterop.JS/src/package.json index 8f04908e7877..37273c492f45 100644 --- a/src/JSInterop/Microsoft.JSInterop.JS/src/package.json +++ b/src/JSInterop/Microsoft.JSInterop.JS/src/package.json @@ -12,14 +12,14 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/aspnet/Extensions.git" + "url": "git+https://github.com/dotnet/extensions.git" }, "author": "Microsoft", "license": "Apache-2.0", "bugs": { - "url": "https://github.com/aspnet/Extensions/issues" + "url": "https://github.com/dotnet/extensions/issues" }, - "homepage": "https://github.com/aspnet/Extensions/tree/master/src/JSInterop#readme", + "homepage": "https://github.com/dotnet/extensions/tree/master/src/JSInterop#readme", "files": [ "dist/**" ], diff --git a/src/JSInterop/Microsoft.JSInterop/test/Infrastructure/DotNetDispatcherTest.cs b/src/JSInterop/Microsoft.JSInterop/test/Infrastructure/DotNetDispatcherTest.cs index c85565d5f27e..1f482b268077 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/Infrastructure/DotNetDispatcherTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/Infrastructure/DotNetDispatcherTest.cs @@ -298,7 +298,7 @@ public async Task EndInvoke_WithErrorString() Assert.Equal(expected, ex.Message); } - [Fact(Skip = "https://github.com/aspnet/AspNetCore/issues/12357")] + [Fact(Skip = "https://github.com/dotnet/aspnetcore/issues/12357")] public void EndInvoke_AfterCancel() { // Arrange @@ -527,7 +527,7 @@ public async Task CanInvokeSyncThrowingMethod() Assert.False(jsRuntime.LastCompletionResult.Success); // Fails // Make sure the method that threw the exception shows up in the call stack - // https://github.com/aspnet/AspNetCore/issues/8612 + // https://github.com/dotnet/aspnetcore/issues/8612 Assert.Contains(nameof(ThrowingClass.ThrowingMethod), jsRuntime.LastCompletionResult.Exception.ToString()); } @@ -549,7 +549,7 @@ public async Task CanInvokeAsyncThrowingMethod() Assert.False(jsRuntime.LastCompletionResult.Success); // Fails // Make sure the method that threw the exception shows up in the call stack - // https://github.com/aspnet/AspNetCore/issues/8612 + // https://github.com/dotnet/aspnetcore/issues/8612 Assert.Contains(nameof(ThrowingClass.AsyncThrowingMethod), jsRuntime.LastCompletionResult.Exception.ToString()); } diff --git a/src/Localization/README.md b/src/Localization/README.md index dc6894b6e09e..503620b3c67b 100644 --- a/src/Localization/README.md +++ b/src/Localization/README.md @@ -3,4 +3,4 @@ Localization These projects provide abstractions for localizing resources in .NET applications. -The ASP.NET Core implementation of localization can be found in https://github.com/aspnet/AspNetCore/tree/master/src/Middleware/Localization. +The ASP.NET Core implementation of localization can be found in https://github.com/dotnet/aspnetcore/tree/master/src/Middleware/Localization. diff --git a/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs b/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs index e737371a8c80..17a922a6c7b7 100644 --- a/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs +++ b/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs @@ -313,7 +313,7 @@ public TestOutputDirectoryAttribute(string preserveExistingLogsInOutput, string public bool PreserveExistingLogsInOutput { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } public string TargetFramework { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } } - [System.ObsoleteAttribute("This API is obsolete and the pattern its usage encouraged should not be used anymore. See https://github.com/aspnet/Extensions/issues/1697 for details.")] + [System.ObsoleteAttribute("This API is obsolete and the pattern its usage encouraged should not be used anymore. See https://github.com/dotnet/extensions/issues/1697 for details.")] public partial class TestPathUtilities { public TestPathUtilities() { } diff --git a/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs b/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs index e737371a8c80..17a922a6c7b7 100644 --- a/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs +++ b/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs @@ -313,7 +313,7 @@ public TestOutputDirectoryAttribute(string preserveExistingLogsInOutput, string public bool PreserveExistingLogsInOutput { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } public string TargetFramework { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } } - [System.ObsoleteAttribute("This API is obsolete and the pattern its usage encouraged should not be used anymore. See https://github.com/aspnet/Extensions/issues/1697 for details.")] + [System.ObsoleteAttribute("This API is obsolete and the pattern its usage encouraged should not be used anymore. See https://github.com/dotnet/extensions/issues/1697 for details.")] public partial class TestPathUtilities { public TestPathUtilities() { } diff --git a/src/Testing/src/TestPathUtilities.cs b/src/Testing/src/TestPathUtilities.cs index a5d83feeff6a..6d4449ca92fa 100644 --- a/src/Testing/src/TestPathUtilities.cs +++ b/src/Testing/src/TestPathUtilities.cs @@ -6,7 +6,7 @@ namespace Microsoft.AspNetCore.Testing { - [Obsolete("This API is obsolete and the pattern its usage encouraged should not be used anymore. See https://github.com/aspnet/Extensions/issues/1697 for details.")] + [Obsolete("This API is obsolete and the pattern its usage encouraged should not be used anymore. See https://github.com/dotnet/extensions/issues/1697 for details.")] public class TestPathUtilities { public static string GetRepoRootDirectory() diff --git a/src/Testing/test/TestPathUtilitiesTest.cs b/src/Testing/test/TestPathUtilitiesTest.cs index 4a6a74ae9c87..024f476f07f6 100644 --- a/src/Testing/test/TestPathUtilitiesTest.cs +++ b/src/Testing/test/TestPathUtilitiesTest.cs @@ -9,7 +9,7 @@ namespace Microsoft.AspNetCore.Testing { public class TestPathUtilitiesTest { - // Entire test pending removal - see https://github.com/aspnet/Extensions/issues/1697 + // Entire test pending removal - see https://github.com/dotnet/extensions/issues/1697 #pragma warning disable 0618 [Fact] From 7d27ed93f7f79b663faa050079def10b2d60eb33 Mon Sep 17 00:00:00 2001 From: Kahbazi Date: Fri, 10 Jan 2020 03:07:56 +0330 Subject: [PATCH 171/183] Set argument if no option has found (dotnet/extensions#2822) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/1513ad0854b32d08f86acd4cd25225af556c2c81 --- .../CommandLine/CommandLineApplication.cs | 46 +++++++++++++++- .../CommandLineApplicationTests.cs | 54 +++++++++++++++++++ 2 files changed, 98 insertions(+), 2 deletions(-) diff --git a/src/Shared/CommandLineUtils/CommandLine/CommandLineApplication.cs b/src/Shared/CommandLineUtils/CommandLine/CommandLineApplication.cs index 51590d59903a..ce608f65bc76 100644 --- a/src/Shared/CommandLineUtils/CommandLine/CommandLineApplication.cs +++ b/src/Shared/CommandLineUtils/CommandLine/CommandLineApplication.cs @@ -26,10 +26,13 @@ internal class CommandLineApplication // options. private readonly bool _continueAfterUnexpectedArg; - public CommandLineApplication(bool throwOnUnexpectedArg = true, bool continueAfterUnexpectedArg = false) + private readonly bool _treatUnmatchedOptionsAsArguments; + + public CommandLineApplication(bool throwOnUnexpectedArg = true, bool continueAfterUnexpectedArg = false, bool treatUnmatchedOptionsAsArguments = false) { _throwOnUnexpectedArg = throwOnUnexpectedArg; _continueAfterUnexpectedArg = continueAfterUnexpectedArg; + _treatUnmatchedOptionsAsArguments = treatUnmatchedOptionsAsArguments; Options = new List(); Arguments = new List(); Commands = new List(); @@ -136,6 +139,7 @@ public int Execute(params string[] args) CommandLineApplication command = this; CommandOption option = null; IEnumerator arguments = null; + var argumentsAssigned = false; for (var index = 0; index < args.Length; index++) { @@ -161,6 +165,25 @@ public int Execute(params string[] args) var longOptionName = longOption[0]; option = command.GetOptions().SingleOrDefault(opt => string.Equals(opt.LongName, longOptionName, StringComparison.Ordinal)); + if (option == null && _treatUnmatchedOptionsAsArguments) + { + if (arguments == null) + { + arguments = new CommandArgumentEnumerator(command.Arguments.GetEnumerator()); + } + if (arguments.MoveNext()) + { + processed = true; + arguments.Current.Values.Add(arg); + argumentsAssigned = true; + continue; + } + //else + //{ + // argumentsAssigned = false; + //} + } + if (option == null) { var ignoreContinueAfterUnexpectedArg = false; @@ -221,6 +244,25 @@ public int Execute(params string[] args) processed = true; option = command.GetOptions().SingleOrDefault(opt => string.Equals(opt.ShortName, shortOption[0], StringComparison.Ordinal)); + if (option == null && _treatUnmatchedOptionsAsArguments) + { + if (arguments == null) + { + arguments = new CommandArgumentEnumerator(command.Arguments.GetEnumerator()); + } + if (arguments.MoveNext()) + { + processed = true; + arguments.Current.Values.Add(arg); + argumentsAssigned = true; + continue; + } + //else + //{ + // argumentsAssigned = false; + //} + } + // If not a short option, try symbol option if (option == null) { @@ -278,7 +320,7 @@ public int Execute(params string[] args) option = null; } - if (!processed && arguments == null) + if (!processed && !argumentsAssigned) { var currentCommand = command; foreach (var subcommand in command.Commands) diff --git a/src/Shared/test/Shared.Tests/CommandLineApplicationTests.cs b/src/Shared/test/Shared.Tests/CommandLineApplicationTests.cs index cc6d0d841ef1..0bdc4a8f1dfd 100644 --- a/src/Shared/test/Shared.Tests/CommandLineApplicationTests.cs +++ b/src/Shared/test/Shared.Tests/CommandLineApplicationTests.cs @@ -1166,5 +1166,59 @@ public void ThrowsExceptionOnInvalidOption() Assert.Equal($"Unrecognized option '{inputOption}'", exception.Message); } + + [Fact] + public void TreatUnmatchedOptionsAsArguments() + { + CommandArgument first = null; + CommandArgument second = null; + + CommandOption firstOption = null; + CommandOption secondOption = null; + + var firstUnmatchedOption = "-firstUnmatchedOption"; + var firstActualOption = "-firstActualOption"; + var seconUnmatchedOption = "--secondUnmatchedOption"; + var secondActualOption = "--secondActualOption"; + + var app = new CommandLineApplication(treatUnmatchedOptionsAsArguments: true); + + app.Command("test", c => + { + firstOption = c.Option("-firstActualOption", "first option", CommandOptionType.NoValue); + secondOption = c.Option("--secondActualOption", "second option", CommandOptionType.NoValue); + + first = c.Argument("first", "First argument"); + second = c.Argument("second", "Second argument"); + c.OnExecute(() => 0); + }); + + app.Execute("test", firstUnmatchedOption, firstActualOption, seconUnmatchedOption, secondActualOption); + + Assert.Equal(firstUnmatchedOption, first.Value); + Assert.Equal(seconUnmatchedOption, second.Value); + + Assert.Equal(firstActualOption, firstOption.Template); + Assert.Equal(secondActualOption, secondOption.Template); + } + + [Fact] + public void ThrowExceptionWhenUnmatchedOptionAndTreatUnmatchedOptionsAsArgumentsIsFalse() + { + CommandArgument first = null; + + var firstOption = "-firstUnmatchedOption"; + + var app = new CommandLineApplication(treatUnmatchedOptionsAsArguments: false); + app.Command("test", c => + { + first = c.Argument("first", "First argument"); + c.OnExecute(() => 0); + }); + + var exception = Assert.Throws(() => app.Execute("test", firstOption)); + + Assert.Equal($"Unrecognized option '{firstOption}'", exception.Message); + } } } From 9eed4375d1c2d6c174759e288152f207c62a4024 Mon Sep 17 00:00:00 2001 From: John Luo Date: Wed, 15 Jan 2020 16:36:56 -0800 Subject: [PATCH 172/183] Remove ref project from Microsoft.AspNetCore.Testing (dotnet/extensions#2817) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/b624d6ba91b9c292ff9a2aa786a575b0eaa31b4b --- .../ref/Microsoft.AspNetCore.Testing.csproj | 32 -- .../ref/Microsoft.AspNetCore.Testing.net46.cs | 360 ------------------ ...osoft.AspNetCore.Testing.netstandard2.0.cs | 360 ------------------ .../src/Microsoft.AspNetCore.Testing.csproj | 3 +- .../Microsoft.AspNetCore.Testing.Tests.csproj | 1 - 5 files changed, 1 insertion(+), 755 deletions(-) delete mode 100644 src/Testing/ref/Microsoft.AspNetCore.Testing.csproj delete mode 100644 src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs delete mode 100644 src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs diff --git a/src/Testing/ref/Microsoft.AspNetCore.Testing.csproj b/src/Testing/ref/Microsoft.AspNetCore.Testing.csproj deleted file mode 100644 index 4606a5411106..000000000000 --- a/src/Testing/ref/Microsoft.AspNetCore.Testing.csproj +++ /dev/null @@ -1,32 +0,0 @@ - - - - netstandard2.0;net46 - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs b/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs deleted file mode 100644 index a7b9c07a9968..000000000000 --- a/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs +++ /dev/null @@ -1,360 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -namespace Microsoft.AspNetCore.Testing -{ - public partial class AspNetTestAssemblyRunner : Xunit.Sdk.XunitTestAssemblyRunner - { - public AspNetTestAssemblyRunner(Xunit.Abstractions.ITestAssembly testAssembly, System.Collections.Generic.IEnumerable testCases, Xunit.Abstractions.IMessageSink diagnosticMessageSink, Xunit.Abstractions.IMessageSink executionMessageSink, Xunit.Abstractions.ITestFrameworkExecutionOptions executionOptions) : base (default(Xunit.Abstractions.ITestAssembly), default(System.Collections.Generic.IEnumerable), default(Xunit.Abstractions.IMessageSink), default(Xunit.Abstractions.IMessageSink), default(Xunit.Abstractions.ITestFrameworkExecutionOptions)) { } - [System.Diagnostics.DebuggerStepThroughAttribute] - protected override System.Threading.Tasks.Task AfterTestAssemblyStartingAsync() { throw null; } - protected override System.Threading.Tasks.Task BeforeTestAssemblyFinishedAsync() { throw null; } - protected override System.Threading.Tasks.Task RunTestCollectionAsync(Xunit.Sdk.IMessageBus messageBus, Xunit.Abstractions.ITestCollection testCollection, System.Collections.Generic.IEnumerable testCases, System.Threading.CancellationTokenSource cancellationTokenSource) { throw null; } - } - public partial class AspNetTestCollectionRunner : Xunit.Sdk.XunitTestCollectionRunner - { - public AspNetTestCollectionRunner(System.Collections.Generic.Dictionary assemblyFixtureMappings, Xunit.Abstractions.ITestCollection testCollection, System.Collections.Generic.IEnumerable testCases, Xunit.Abstractions.IMessageSink diagnosticMessageSink, Xunit.Sdk.IMessageBus messageBus, Xunit.Sdk.ITestCaseOrderer testCaseOrderer, Xunit.Sdk.ExceptionAggregator aggregator, System.Threading.CancellationTokenSource cancellationTokenSource) : base (default(Xunit.Abstractions.ITestCollection), default(System.Collections.Generic.IEnumerable), default(Xunit.Abstractions.IMessageSink), default(Xunit.Sdk.IMessageBus), default(Xunit.Sdk.ITestCaseOrderer), default(Xunit.Sdk.ExceptionAggregator), default(System.Threading.CancellationTokenSource)) { } - [System.Diagnostics.DebuggerStepThroughAttribute] - protected override System.Threading.Tasks.Task AfterTestCollectionStartingAsync() { throw null; } - protected override System.Threading.Tasks.Task BeforeTestCollectionFinishedAsync() { throw null; } - protected override System.Threading.Tasks.Task RunTestClassAsync(Xunit.Abstractions.ITestClass testClass, Xunit.Abstractions.IReflectionTypeInfo @class, System.Collections.Generic.IEnumerable testCases) { throw null; } - } - public partial class AspNetTestFramework : Xunit.Sdk.XunitTestFramework - { - public AspNetTestFramework(Xunit.Abstractions.IMessageSink messageSink) : base (default(Xunit.Abstractions.IMessageSink)) { } - protected override Xunit.Abstractions.ITestFrameworkExecutor CreateExecutor(System.Reflection.AssemblyName assemblyName) { throw null; } - } - public partial class AspNetTestFrameworkExecutor : Xunit.Sdk.XunitTestFrameworkExecutor - { - public AspNetTestFrameworkExecutor(System.Reflection.AssemblyName assemblyName, Xunit.Abstractions.ISourceInformationProvider sourceInformationProvider, Xunit.Abstractions.IMessageSink diagnosticMessageSink) : base (default(System.Reflection.AssemblyName), default(Xunit.Abstractions.ISourceInformationProvider), default(Xunit.Abstractions.IMessageSink)) { } - [System.Diagnostics.DebuggerStepThroughAttribute] - protected override void RunTestCases(System.Collections.Generic.IEnumerable testCases, Xunit.Abstractions.IMessageSink executionMessageSink, Xunit.Abstractions.ITestFrameworkExecutionOptions executionOptions) { } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Assembly, AllowMultiple=true)] - public partial class AssemblyFixtureAttribute : System.Attribute - { - public AssemblyFixtureAttribute(System.Type fixtureType) { } - public System.Type FixtureType { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Method, AllowMultiple=false)] - [Xunit.Sdk.XunitTestCaseDiscovererAttribute("Microsoft.AspNetCore.Testing.ConditionalFactDiscoverer", "Microsoft.AspNetCore.Testing")] - public partial class ConditionalFactAttribute : Xunit.FactAttribute - { - public ConditionalFactAttribute() { } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Method, AllowMultiple=false)] - [Xunit.Sdk.XunitTestCaseDiscovererAttribute("Microsoft.AspNetCore.Testing.ConditionalTheoryDiscoverer", "Microsoft.AspNetCore.Testing")] - public partial class ConditionalTheoryAttribute : Xunit.TheoryAttribute - { - public ConditionalTheoryAttribute() { } - } - public partial class CultureReplacer : System.IDisposable - { - public CultureReplacer(System.Globalization.CultureInfo culture, System.Globalization.CultureInfo uiCulture) { } - public CultureReplacer(string culture = "en-GB", string uiCulture = "en-US") { } - public static System.Globalization.CultureInfo DefaultCulture { get { throw null; } } - public static string DefaultCultureName { get { throw null; } } - public static string DefaultUICultureName { get { throw null; } } - public void Dispose() { } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Method, Inherited=true, AllowMultiple=false)] - public sealed partial class DockerOnlyAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition - { - public DockerOnlyAttribute() { } - public bool IsMet { get { throw null; } } - public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=true)] - public partial class EnvironmentVariableSkipConditionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition - { - public EnvironmentVariableSkipConditionAttribute(string variableName, params string[] values) { } - public bool IsMet { get { throw null; } } - public bool RunOnMatch { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } - public string SkipReason { get { throw null; } } - } - public static partial class ExceptionAssert - { - public static System.ArgumentException ThrowsArgument(System.Action testCode, string paramName, string exceptionMessage) { throw null; } - public static System.Threading.Tasks.Task ThrowsArgumentAsync(System.Func testCode, string paramName, string exceptionMessage) { throw null; } - public static System.ArgumentNullException ThrowsArgumentNull(System.Action testCode, string paramName) { throw null; } - public static System.ArgumentException ThrowsArgumentNullOrEmpty(System.Action testCode, string paramName) { throw null; } - public static System.Threading.Tasks.Task ThrowsArgumentNullOrEmptyAsync(System.Func testCode, string paramName) { throw null; } - public static System.ArgumentException ThrowsArgumentNullOrEmptyString(System.Action testCode, string paramName) { throw null; } - public static System.Threading.Tasks.Task ThrowsArgumentNullOrEmptyStringAsync(System.Func testCode, string paramName) { throw null; } - public static System.ArgumentOutOfRangeException ThrowsArgumentOutOfRange(System.Action testCode, string paramName, string exceptionMessage, object actualValue = null) { throw null; } - [System.Diagnostics.DebuggerStepThroughAttribute] - public static System.Threading.Tasks.Task ThrowsAsync(System.Func testCode, string exceptionMessage) where TException : System.Exception { throw null; } - public static TException Throws(System.Action testCode) where TException : System.Exception { throw null; } - public static TException Throws(System.Action testCode, string exceptionMessage) where TException : System.Exception { throw null; } - public static TException Throws(System.Func testCode, string exceptionMessage) where TException : System.Exception { throw null; } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method)] - [Xunit.Sdk.TraitDiscovererAttribute("Microsoft.AspNetCore.Testing.FlakyTraitDiscoverer", "Microsoft.AspNetCore.Testing")] - public sealed partial class FlakyAttribute : System.Attribute, Xunit.Sdk.ITraitAttribute - { - public FlakyAttribute(string gitHubIssueUrl, string firstFilter, params string[] additionalFilters) { } - public System.Collections.Generic.IReadOnlyList Filters { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string GitHubIssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - } - public static partial class FlakyOn - { - public const string All = "All"; - public static partial class AzP - { - public const string All = "AzP:All"; - public const string Linux = "AzP:OS:Linux"; - public const string macOS = "AzP:OS:Darwin"; - public const string Windows = "AzP:OS:Windows_NT"; - } - public static partial class Helix - { - public const string All = "Helix:Queue:All"; - public const string Centos7Amd64 = "Helix:Queue:Centos.7.Amd64.Open"; - public const string Debian8Amd64 = "Helix:Queue:Debian.8.Amd64.Open"; - public const string Debian9Amd64 = "Helix:Queue:Debian.9.Amd64.Open"; - public const string Fedora27Amd64 = "Helix:Queue:Fedora.27.Amd64.Open"; - public const string Fedora28Amd64 = "Helix:Queue:Fedora.28.Amd64.Open"; - public const string macOS1012Amd64 = "Helix:Queue:OSX.1012.Amd64.Open"; - public const string Redhat7Amd64 = "Helix:Queue:Redhat.7.Amd64.Open"; - public const string Ubuntu1604Amd64 = "Helix:Queue:Ubuntu.1604.Amd64.Open"; - public const string Ubuntu1810Amd64 = "Helix:Queue:Ubuntu.1810.Amd64.Open"; - public const string Windows10Amd64 = "Helix:Queue:Windows.10.Amd64.ClientRS4.VS2017.Open"; - } - } - public partial class FlakyTraitDiscoverer : Xunit.Sdk.ITraitDiscoverer - { - public FlakyTraitDiscoverer() { } - public System.Collections.Generic.IEnumerable> GetTraits(Xunit.Abstractions.IAttributeInfo traitAttribute) { throw null; } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Method, AllowMultiple=false)] - public partial class FrameworkSkipConditionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition - { - public FrameworkSkipConditionAttribute(Microsoft.AspNetCore.Testing.RuntimeFrameworks excludedFrameworks) { } - public bool IsMet { get { throw null; } } - public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } - } - public static partial class HelixQueues - { - public const string Centos7Amd64 = "Centos.7.Amd64.Open"; - public const string Debian8Amd64 = "Debian.8.Amd64.Open"; - public const string Debian9Amd64 = "Debian.9.Amd64.Open"; - public const string Fedora27Amd64 = "Fedora.27.Amd64.Open"; - public const string Fedora28Amd64 = "Fedora.28.Amd64.Open"; - public const string macOS1012Amd64 = "OSX.1012.Amd64.Open"; - public const string Redhat7Amd64 = "Redhat.7.Amd64.Open"; - public const string Ubuntu1604Amd64 = "Ubuntu.1604.Amd64.Open"; - public const string Ubuntu1810Amd64 = "Ubuntu.1810.Amd64.Open"; - public const string Windows10Amd64 = "Windows.10.Amd64.ClientRS4.VS2017.Open"; - } - public static partial class HttpClientSlim - { - [System.Diagnostics.DebuggerStepThroughAttribute] - public static System.Threading.Tasks.Task GetSocket(System.Uri requestUri) { throw null; } - [System.Diagnostics.DebuggerStepThroughAttribute] - public static System.Threading.Tasks.Task GetStringAsync(string requestUri, bool validateCertificate = true) { throw null; } - [System.Diagnostics.DebuggerStepThroughAttribute] - public static System.Threading.Tasks.Task GetStringAsync(System.Uri requestUri, bool validateCertificate = true) { throw null; } - [System.Diagnostics.DebuggerStepThroughAttribute] - public static System.Threading.Tasks.Task PostAsync(string requestUri, System.Net.Http.HttpContent content, bool validateCertificate = true) { throw null; } - [System.Diagnostics.DebuggerStepThroughAttribute] - public static System.Threading.Tasks.Task PostAsync(System.Uri requestUri, System.Net.Http.HttpContent content, bool validateCertificate = true) { throw null; } - } - public partial interface ITestCondition - { - bool IsMet { get; } - string SkipReason { get; } - } - public partial interface ITestMethodLifecycle - { - System.Threading.Tasks.Task OnTestEndAsync(Microsoft.AspNetCore.Testing.TestContext context, System.Exception exception, System.Threading.CancellationToken cancellationToken); - System.Threading.Tasks.Task OnTestStartAsync(Microsoft.AspNetCore.Testing.TestContext context, System.Threading.CancellationToken cancellationToken); - } - [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=true)] - public partial class MinimumOSVersionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition - { - public MinimumOSVersionAttribute(Microsoft.AspNetCore.Testing.OperatingSystems operatingSystem, string minVersion) { } - public bool IsMet { get { throw null; } } - public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } - } - [System.FlagsAttribute] - public enum OperatingSystems - { - Linux = 1, - MacOSX = 2, - Windows = 4, - } - [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=true)] - public partial class OSSkipConditionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition - { - public OSSkipConditionAttribute(Microsoft.AspNetCore.Testing.OperatingSystems operatingSystem, params string[] versions) { } - public bool IsMet { get { throw null; } } - public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=false)] - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] - public partial class RepeatAttribute : System.Attribute - { - public RepeatAttribute(int runCount = 10) { } - public int RunCount { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - } - public partial class RepeatContext - { - public RepeatContext(int limit) { } - public static Microsoft.AspNetCore.Testing.RepeatContext Current { get { throw null; } } - public int CurrentIteration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } - public int Limit { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Method)] - public partial class ReplaceCultureAttribute : Xunit.Sdk.BeforeAfterTestAttribute - { - public ReplaceCultureAttribute() { } - public ReplaceCultureAttribute(string currentCulture, string currentUICulture) { } - public System.Globalization.CultureInfo Culture { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public System.Globalization.CultureInfo UICulture { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public override void After(System.Reflection.MethodInfo methodUnderTest) { } - public override void Before(System.Reflection.MethodInfo methodUnderTest) { } - } - [System.FlagsAttribute] - public enum RuntimeFrameworks - { - None = 0, - Mono = 1, - CLR = 2, - CoreCLR = 4, - } - [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class, AllowMultiple=false)] - public partial class ShortClassNameAttribute : System.Attribute - { - public ShortClassNameAttribute() { } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=false)] - public partial class SkipOnCIAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition - { - public SkipOnCIAttribute(string issueUrl = "") { } - public bool IsMet { get { throw null; } } - public string IssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string SkipReason { get { throw null; } } - public static string GetIfOnAzdo() { throw null; } - public static string GetTargetHelixQueue() { throw null; } - public static bool OnAzdo() { throw null; } - public static bool OnCI() { throw null; } - public static bool OnHelix() { throw null; } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=false)] - public partial class SkipOnHelixAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition - { - public SkipOnHelixAttribute(string issueUrl) { } - public bool IsMet { get { throw null; } } - public string IssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string Queues { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } - public string SkipReason { get { throw null; } } - public static string GetTargetHelixQueue() { throw null; } - public static bool OnHelix() { throw null; } - } - public partial class SkippedTestCase : Xunit.Sdk.XunitTestCase - { - [System.ObsoleteAttribute("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")] - public SkippedTestCase() { } - public SkippedTestCase(string skipReason, Xunit.Abstractions.IMessageSink diagnosticMessageSink, Xunit.Sdk.TestMethodDisplay defaultMethodDisplay, Xunit.Sdk.TestMethodDisplayOptions defaultMethodDisplayOptions, Xunit.Abstractions.ITestMethod testMethod, object[] testMethodArguments = null) { } - public override void Deserialize(Xunit.Abstractions.IXunitSerializationInfo data) { } - protected override string GetSkipReason(Xunit.Abstractions.IAttributeInfo factAttribute) { throw null; } - public override void Serialize(Xunit.Abstractions.IXunitSerializationInfo data) { } - } - public static partial class TaskExtensions - { - [System.Diagnostics.DebuggerStepThroughAttribute] - public static System.Threading.Tasks.Task TimeoutAfter(this System.Threading.Tasks.Task task, System.TimeSpan timeout, [System.Runtime.CompilerServices.CallerFilePathAttribute]string filePath = null, [System.Runtime.CompilerServices.CallerLineNumberAttribute]int lineNumber = 0) { throw null; } - [System.Diagnostics.DebuggerStepThroughAttribute] - public static System.Threading.Tasks.Task TimeoutAfter(this System.Threading.Tasks.Task task, System.TimeSpan timeout, [System.Runtime.CompilerServices.CallerFilePathAttribute]string filePath = null, [System.Runtime.CompilerServices.CallerLineNumberAttribute]int lineNumber = 0) { throw null; } - } - public sealed partial class TestContext - { - public TestContext(System.Type testClass, object[] constructorArguments, System.Reflection.MethodInfo testMethod, object[] methodArguments, Xunit.Abstractions.ITestOutputHelper output) { } - public object[] ConstructorArguments { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public Microsoft.AspNetCore.Testing.TestFileOutputContext FileOutput { get { throw null; } } - public object[] MethodArguments { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public Xunit.Abstractions.ITestOutputHelper Output { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public System.Type TestClass { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public System.Reflection.MethodInfo TestMethod { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - } - public sealed partial class TestFileOutputContext - { - public TestFileOutputContext(Microsoft.AspNetCore.Testing.TestContext parent) { } - public string AssemblyOutputDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string TestClassName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string TestClassOutputDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string TestName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public static string GetAssemblyBaseDirectory(System.Reflection.Assembly assembly, string baseDirectory = null) { throw null; } - public static string GetOutputDirectory(System.Reflection.Assembly assembly) { throw null; } - public static string GetTestClassName(System.Type type) { throw null; } - public static string GetTestMethodName(System.Reflection.MethodInfo method, object[] arguments) { throw null; } - public string GetUniqueFileName(string prefix, string extension) { throw null; } - public static string RemoveIllegalFileChars(string s) { throw null; } - } - public static partial class TestMethodExtensions - { - public static string EvaluateSkipConditions(this Xunit.Abstractions.ITestMethod testMethod) { throw null; } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Assembly, AllowMultiple=false, Inherited=true)] - public partial class TestOutputDirectoryAttribute : System.Attribute - { - public TestOutputDirectoryAttribute(string targetFramework, string baseDirectory = null) { } - public string BaseDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string TargetFramework { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - } - [System.ObsoleteAttribute("This API is obsolete and the pattern its usage encouraged should not be used anymore. See https://github.com/aspnet/Extensions/issues/1697 for details.")] - public partial class TestPathUtilities - { - public TestPathUtilities() { } - public static string GetRepoRootDirectory() { throw null; } - public static string GetSolutionRootDirectory(string solution) { throw null; } - } - public static partial class TestPlatformHelper - { - public static bool IsLinux { get { throw null; } } - public static bool IsMac { get { throw null; } } - public static bool IsMono { get { throw null; } } - public static bool IsWindows { get { throw null; } } - } - public static partial class WindowsVersions - { - public const string Win10 = "10.0"; - public const string Win10_19H2 = "10.0.18363"; - public const string Win10_20H1 = "10.0.18990"; - public const string Win10_RS4 = "10.0.17134"; - public const string Win2008R2 = "6.1"; - public const string Win7 = "6.1"; - public const string Win8 = "6.2"; - public const string Win81 = "6.3"; - } -} -namespace Microsoft.AspNetCore.Testing.Tracing -{ - public partial class CollectingEventListener : System.Diagnostics.Tracing.EventListener - { - public CollectingEventListener() { } - public void CollectFrom(System.Diagnostics.Tracing.EventSource eventSource) { } - public void CollectFrom(string eventSourceName) { } - public System.Collections.Generic.IReadOnlyList GetEventsWritten() { throw null; } - protected override void OnEventSourceCreated(System.Diagnostics.Tracing.EventSource eventSource) { } - protected override void OnEventWritten(System.Diagnostics.Tracing.EventWrittenEventArgs eventData) { } - } - public partial class EventAssert - { - public EventAssert(int expectedId, string expectedName, System.Diagnostics.Tracing.EventLevel expectedLevel) { } - public static void Collection(System.Collections.Generic.IEnumerable events, params Microsoft.AspNetCore.Testing.Tracing.EventAssert[] asserts) { } - public static Microsoft.AspNetCore.Testing.Tracing.EventAssert Event(int id, string name, System.Diagnostics.Tracing.EventLevel level) { throw null; } - public Microsoft.AspNetCore.Testing.Tracing.EventAssert Payload(string name, System.Action asserter) { throw null; } - public Microsoft.AspNetCore.Testing.Tracing.EventAssert Payload(string name, object expectedValue) { throw null; } - } - [Xunit.CollectionAttribute("Microsoft.AspNetCore.Testing.Tracing.EventSourceTestCollection")] - public abstract partial class EventSourceTestBase : System.IDisposable - { - public const string CollectionName = "Microsoft.AspNetCore.Testing.Tracing.EventSourceTestCollection"; - public EventSourceTestBase() { } - protected void CollectFrom(System.Diagnostics.Tracing.EventSource eventSource) { } - protected void CollectFrom(string eventSourceName) { } - public void Dispose() { } - protected System.Collections.Generic.IReadOnlyList GetEvents() { throw null; } - } -} diff --git a/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs b/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs deleted file mode 100644 index a7b9c07a9968..000000000000 --- a/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs +++ /dev/null @@ -1,360 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -namespace Microsoft.AspNetCore.Testing -{ - public partial class AspNetTestAssemblyRunner : Xunit.Sdk.XunitTestAssemblyRunner - { - public AspNetTestAssemblyRunner(Xunit.Abstractions.ITestAssembly testAssembly, System.Collections.Generic.IEnumerable testCases, Xunit.Abstractions.IMessageSink diagnosticMessageSink, Xunit.Abstractions.IMessageSink executionMessageSink, Xunit.Abstractions.ITestFrameworkExecutionOptions executionOptions) : base (default(Xunit.Abstractions.ITestAssembly), default(System.Collections.Generic.IEnumerable), default(Xunit.Abstractions.IMessageSink), default(Xunit.Abstractions.IMessageSink), default(Xunit.Abstractions.ITestFrameworkExecutionOptions)) { } - [System.Diagnostics.DebuggerStepThroughAttribute] - protected override System.Threading.Tasks.Task AfterTestAssemblyStartingAsync() { throw null; } - protected override System.Threading.Tasks.Task BeforeTestAssemblyFinishedAsync() { throw null; } - protected override System.Threading.Tasks.Task RunTestCollectionAsync(Xunit.Sdk.IMessageBus messageBus, Xunit.Abstractions.ITestCollection testCollection, System.Collections.Generic.IEnumerable testCases, System.Threading.CancellationTokenSource cancellationTokenSource) { throw null; } - } - public partial class AspNetTestCollectionRunner : Xunit.Sdk.XunitTestCollectionRunner - { - public AspNetTestCollectionRunner(System.Collections.Generic.Dictionary assemblyFixtureMappings, Xunit.Abstractions.ITestCollection testCollection, System.Collections.Generic.IEnumerable testCases, Xunit.Abstractions.IMessageSink diagnosticMessageSink, Xunit.Sdk.IMessageBus messageBus, Xunit.Sdk.ITestCaseOrderer testCaseOrderer, Xunit.Sdk.ExceptionAggregator aggregator, System.Threading.CancellationTokenSource cancellationTokenSource) : base (default(Xunit.Abstractions.ITestCollection), default(System.Collections.Generic.IEnumerable), default(Xunit.Abstractions.IMessageSink), default(Xunit.Sdk.IMessageBus), default(Xunit.Sdk.ITestCaseOrderer), default(Xunit.Sdk.ExceptionAggregator), default(System.Threading.CancellationTokenSource)) { } - [System.Diagnostics.DebuggerStepThroughAttribute] - protected override System.Threading.Tasks.Task AfterTestCollectionStartingAsync() { throw null; } - protected override System.Threading.Tasks.Task BeforeTestCollectionFinishedAsync() { throw null; } - protected override System.Threading.Tasks.Task RunTestClassAsync(Xunit.Abstractions.ITestClass testClass, Xunit.Abstractions.IReflectionTypeInfo @class, System.Collections.Generic.IEnumerable testCases) { throw null; } - } - public partial class AspNetTestFramework : Xunit.Sdk.XunitTestFramework - { - public AspNetTestFramework(Xunit.Abstractions.IMessageSink messageSink) : base (default(Xunit.Abstractions.IMessageSink)) { } - protected override Xunit.Abstractions.ITestFrameworkExecutor CreateExecutor(System.Reflection.AssemblyName assemblyName) { throw null; } - } - public partial class AspNetTestFrameworkExecutor : Xunit.Sdk.XunitTestFrameworkExecutor - { - public AspNetTestFrameworkExecutor(System.Reflection.AssemblyName assemblyName, Xunit.Abstractions.ISourceInformationProvider sourceInformationProvider, Xunit.Abstractions.IMessageSink diagnosticMessageSink) : base (default(System.Reflection.AssemblyName), default(Xunit.Abstractions.ISourceInformationProvider), default(Xunit.Abstractions.IMessageSink)) { } - [System.Diagnostics.DebuggerStepThroughAttribute] - protected override void RunTestCases(System.Collections.Generic.IEnumerable testCases, Xunit.Abstractions.IMessageSink executionMessageSink, Xunit.Abstractions.ITestFrameworkExecutionOptions executionOptions) { } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Assembly, AllowMultiple=true)] - public partial class AssemblyFixtureAttribute : System.Attribute - { - public AssemblyFixtureAttribute(System.Type fixtureType) { } - public System.Type FixtureType { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Method, AllowMultiple=false)] - [Xunit.Sdk.XunitTestCaseDiscovererAttribute("Microsoft.AspNetCore.Testing.ConditionalFactDiscoverer", "Microsoft.AspNetCore.Testing")] - public partial class ConditionalFactAttribute : Xunit.FactAttribute - { - public ConditionalFactAttribute() { } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Method, AllowMultiple=false)] - [Xunit.Sdk.XunitTestCaseDiscovererAttribute("Microsoft.AspNetCore.Testing.ConditionalTheoryDiscoverer", "Microsoft.AspNetCore.Testing")] - public partial class ConditionalTheoryAttribute : Xunit.TheoryAttribute - { - public ConditionalTheoryAttribute() { } - } - public partial class CultureReplacer : System.IDisposable - { - public CultureReplacer(System.Globalization.CultureInfo culture, System.Globalization.CultureInfo uiCulture) { } - public CultureReplacer(string culture = "en-GB", string uiCulture = "en-US") { } - public static System.Globalization.CultureInfo DefaultCulture { get { throw null; } } - public static string DefaultCultureName { get { throw null; } } - public static string DefaultUICultureName { get { throw null; } } - public void Dispose() { } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Method, Inherited=true, AllowMultiple=false)] - public sealed partial class DockerOnlyAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition - { - public DockerOnlyAttribute() { } - public bool IsMet { get { throw null; } } - public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=true)] - public partial class EnvironmentVariableSkipConditionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition - { - public EnvironmentVariableSkipConditionAttribute(string variableName, params string[] values) { } - public bool IsMet { get { throw null; } } - public bool RunOnMatch { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } - public string SkipReason { get { throw null; } } - } - public static partial class ExceptionAssert - { - public static System.ArgumentException ThrowsArgument(System.Action testCode, string paramName, string exceptionMessage) { throw null; } - public static System.Threading.Tasks.Task ThrowsArgumentAsync(System.Func testCode, string paramName, string exceptionMessage) { throw null; } - public static System.ArgumentNullException ThrowsArgumentNull(System.Action testCode, string paramName) { throw null; } - public static System.ArgumentException ThrowsArgumentNullOrEmpty(System.Action testCode, string paramName) { throw null; } - public static System.Threading.Tasks.Task ThrowsArgumentNullOrEmptyAsync(System.Func testCode, string paramName) { throw null; } - public static System.ArgumentException ThrowsArgumentNullOrEmptyString(System.Action testCode, string paramName) { throw null; } - public static System.Threading.Tasks.Task ThrowsArgumentNullOrEmptyStringAsync(System.Func testCode, string paramName) { throw null; } - public static System.ArgumentOutOfRangeException ThrowsArgumentOutOfRange(System.Action testCode, string paramName, string exceptionMessage, object actualValue = null) { throw null; } - [System.Diagnostics.DebuggerStepThroughAttribute] - public static System.Threading.Tasks.Task ThrowsAsync(System.Func testCode, string exceptionMessage) where TException : System.Exception { throw null; } - public static TException Throws(System.Action testCode) where TException : System.Exception { throw null; } - public static TException Throws(System.Action testCode, string exceptionMessage) where TException : System.Exception { throw null; } - public static TException Throws(System.Func testCode, string exceptionMessage) where TException : System.Exception { throw null; } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method)] - [Xunit.Sdk.TraitDiscovererAttribute("Microsoft.AspNetCore.Testing.FlakyTraitDiscoverer", "Microsoft.AspNetCore.Testing")] - public sealed partial class FlakyAttribute : System.Attribute, Xunit.Sdk.ITraitAttribute - { - public FlakyAttribute(string gitHubIssueUrl, string firstFilter, params string[] additionalFilters) { } - public System.Collections.Generic.IReadOnlyList Filters { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string GitHubIssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - } - public static partial class FlakyOn - { - public const string All = "All"; - public static partial class AzP - { - public const string All = "AzP:All"; - public const string Linux = "AzP:OS:Linux"; - public const string macOS = "AzP:OS:Darwin"; - public const string Windows = "AzP:OS:Windows_NT"; - } - public static partial class Helix - { - public const string All = "Helix:Queue:All"; - public const string Centos7Amd64 = "Helix:Queue:Centos.7.Amd64.Open"; - public const string Debian8Amd64 = "Helix:Queue:Debian.8.Amd64.Open"; - public const string Debian9Amd64 = "Helix:Queue:Debian.9.Amd64.Open"; - public const string Fedora27Amd64 = "Helix:Queue:Fedora.27.Amd64.Open"; - public const string Fedora28Amd64 = "Helix:Queue:Fedora.28.Amd64.Open"; - public const string macOS1012Amd64 = "Helix:Queue:OSX.1012.Amd64.Open"; - public const string Redhat7Amd64 = "Helix:Queue:Redhat.7.Amd64.Open"; - public const string Ubuntu1604Amd64 = "Helix:Queue:Ubuntu.1604.Amd64.Open"; - public const string Ubuntu1810Amd64 = "Helix:Queue:Ubuntu.1810.Amd64.Open"; - public const string Windows10Amd64 = "Helix:Queue:Windows.10.Amd64.ClientRS4.VS2017.Open"; - } - } - public partial class FlakyTraitDiscoverer : Xunit.Sdk.ITraitDiscoverer - { - public FlakyTraitDiscoverer() { } - public System.Collections.Generic.IEnumerable> GetTraits(Xunit.Abstractions.IAttributeInfo traitAttribute) { throw null; } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Method, AllowMultiple=false)] - public partial class FrameworkSkipConditionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition - { - public FrameworkSkipConditionAttribute(Microsoft.AspNetCore.Testing.RuntimeFrameworks excludedFrameworks) { } - public bool IsMet { get { throw null; } } - public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } - } - public static partial class HelixQueues - { - public const string Centos7Amd64 = "Centos.7.Amd64.Open"; - public const string Debian8Amd64 = "Debian.8.Amd64.Open"; - public const string Debian9Amd64 = "Debian.9.Amd64.Open"; - public const string Fedora27Amd64 = "Fedora.27.Amd64.Open"; - public const string Fedora28Amd64 = "Fedora.28.Amd64.Open"; - public const string macOS1012Amd64 = "OSX.1012.Amd64.Open"; - public const string Redhat7Amd64 = "Redhat.7.Amd64.Open"; - public const string Ubuntu1604Amd64 = "Ubuntu.1604.Amd64.Open"; - public const string Ubuntu1810Amd64 = "Ubuntu.1810.Amd64.Open"; - public const string Windows10Amd64 = "Windows.10.Amd64.ClientRS4.VS2017.Open"; - } - public static partial class HttpClientSlim - { - [System.Diagnostics.DebuggerStepThroughAttribute] - public static System.Threading.Tasks.Task GetSocket(System.Uri requestUri) { throw null; } - [System.Diagnostics.DebuggerStepThroughAttribute] - public static System.Threading.Tasks.Task GetStringAsync(string requestUri, bool validateCertificate = true) { throw null; } - [System.Diagnostics.DebuggerStepThroughAttribute] - public static System.Threading.Tasks.Task GetStringAsync(System.Uri requestUri, bool validateCertificate = true) { throw null; } - [System.Diagnostics.DebuggerStepThroughAttribute] - public static System.Threading.Tasks.Task PostAsync(string requestUri, System.Net.Http.HttpContent content, bool validateCertificate = true) { throw null; } - [System.Diagnostics.DebuggerStepThroughAttribute] - public static System.Threading.Tasks.Task PostAsync(System.Uri requestUri, System.Net.Http.HttpContent content, bool validateCertificate = true) { throw null; } - } - public partial interface ITestCondition - { - bool IsMet { get; } - string SkipReason { get; } - } - public partial interface ITestMethodLifecycle - { - System.Threading.Tasks.Task OnTestEndAsync(Microsoft.AspNetCore.Testing.TestContext context, System.Exception exception, System.Threading.CancellationToken cancellationToken); - System.Threading.Tasks.Task OnTestStartAsync(Microsoft.AspNetCore.Testing.TestContext context, System.Threading.CancellationToken cancellationToken); - } - [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=true)] - public partial class MinimumOSVersionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition - { - public MinimumOSVersionAttribute(Microsoft.AspNetCore.Testing.OperatingSystems operatingSystem, string minVersion) { } - public bool IsMet { get { throw null; } } - public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } - } - [System.FlagsAttribute] - public enum OperatingSystems - { - Linux = 1, - MacOSX = 2, - Windows = 4, - } - [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=true)] - public partial class OSSkipConditionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition - { - public OSSkipConditionAttribute(Microsoft.AspNetCore.Testing.OperatingSystems operatingSystem, params string[] versions) { } - public bool IsMet { get { throw null; } } - public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=false)] - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] - public partial class RepeatAttribute : System.Attribute - { - public RepeatAttribute(int runCount = 10) { } - public int RunCount { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - } - public partial class RepeatContext - { - public RepeatContext(int limit) { } - public static Microsoft.AspNetCore.Testing.RepeatContext Current { get { throw null; } } - public int CurrentIteration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } - public int Limit { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Method)] - public partial class ReplaceCultureAttribute : Xunit.Sdk.BeforeAfterTestAttribute - { - public ReplaceCultureAttribute() { } - public ReplaceCultureAttribute(string currentCulture, string currentUICulture) { } - public System.Globalization.CultureInfo Culture { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public System.Globalization.CultureInfo UICulture { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public override void After(System.Reflection.MethodInfo methodUnderTest) { } - public override void Before(System.Reflection.MethodInfo methodUnderTest) { } - } - [System.FlagsAttribute] - public enum RuntimeFrameworks - { - None = 0, - Mono = 1, - CLR = 2, - CoreCLR = 4, - } - [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class, AllowMultiple=false)] - public partial class ShortClassNameAttribute : System.Attribute - { - public ShortClassNameAttribute() { } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=false)] - public partial class SkipOnCIAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition - { - public SkipOnCIAttribute(string issueUrl = "") { } - public bool IsMet { get { throw null; } } - public string IssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string SkipReason { get { throw null; } } - public static string GetIfOnAzdo() { throw null; } - public static string GetTargetHelixQueue() { throw null; } - public static bool OnAzdo() { throw null; } - public static bool OnCI() { throw null; } - public static bool OnHelix() { throw null; } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=false)] - public partial class SkipOnHelixAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition - { - public SkipOnHelixAttribute(string issueUrl) { } - public bool IsMet { get { throw null; } } - public string IssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string Queues { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } - public string SkipReason { get { throw null; } } - public static string GetTargetHelixQueue() { throw null; } - public static bool OnHelix() { throw null; } - } - public partial class SkippedTestCase : Xunit.Sdk.XunitTestCase - { - [System.ObsoleteAttribute("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")] - public SkippedTestCase() { } - public SkippedTestCase(string skipReason, Xunit.Abstractions.IMessageSink diagnosticMessageSink, Xunit.Sdk.TestMethodDisplay defaultMethodDisplay, Xunit.Sdk.TestMethodDisplayOptions defaultMethodDisplayOptions, Xunit.Abstractions.ITestMethod testMethod, object[] testMethodArguments = null) { } - public override void Deserialize(Xunit.Abstractions.IXunitSerializationInfo data) { } - protected override string GetSkipReason(Xunit.Abstractions.IAttributeInfo factAttribute) { throw null; } - public override void Serialize(Xunit.Abstractions.IXunitSerializationInfo data) { } - } - public static partial class TaskExtensions - { - [System.Diagnostics.DebuggerStepThroughAttribute] - public static System.Threading.Tasks.Task TimeoutAfter(this System.Threading.Tasks.Task task, System.TimeSpan timeout, [System.Runtime.CompilerServices.CallerFilePathAttribute]string filePath = null, [System.Runtime.CompilerServices.CallerLineNumberAttribute]int lineNumber = 0) { throw null; } - [System.Diagnostics.DebuggerStepThroughAttribute] - public static System.Threading.Tasks.Task TimeoutAfter(this System.Threading.Tasks.Task task, System.TimeSpan timeout, [System.Runtime.CompilerServices.CallerFilePathAttribute]string filePath = null, [System.Runtime.CompilerServices.CallerLineNumberAttribute]int lineNumber = 0) { throw null; } - } - public sealed partial class TestContext - { - public TestContext(System.Type testClass, object[] constructorArguments, System.Reflection.MethodInfo testMethod, object[] methodArguments, Xunit.Abstractions.ITestOutputHelper output) { } - public object[] ConstructorArguments { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public Microsoft.AspNetCore.Testing.TestFileOutputContext FileOutput { get { throw null; } } - public object[] MethodArguments { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public Xunit.Abstractions.ITestOutputHelper Output { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public System.Type TestClass { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public System.Reflection.MethodInfo TestMethod { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - } - public sealed partial class TestFileOutputContext - { - public TestFileOutputContext(Microsoft.AspNetCore.Testing.TestContext parent) { } - public string AssemblyOutputDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string TestClassName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string TestClassOutputDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string TestName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public static string GetAssemblyBaseDirectory(System.Reflection.Assembly assembly, string baseDirectory = null) { throw null; } - public static string GetOutputDirectory(System.Reflection.Assembly assembly) { throw null; } - public static string GetTestClassName(System.Type type) { throw null; } - public static string GetTestMethodName(System.Reflection.MethodInfo method, object[] arguments) { throw null; } - public string GetUniqueFileName(string prefix, string extension) { throw null; } - public static string RemoveIllegalFileChars(string s) { throw null; } - } - public static partial class TestMethodExtensions - { - public static string EvaluateSkipConditions(this Xunit.Abstractions.ITestMethod testMethod) { throw null; } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Assembly, AllowMultiple=false, Inherited=true)] - public partial class TestOutputDirectoryAttribute : System.Attribute - { - public TestOutputDirectoryAttribute(string targetFramework, string baseDirectory = null) { } - public string BaseDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string TargetFramework { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - } - [System.ObsoleteAttribute("This API is obsolete and the pattern its usage encouraged should not be used anymore. See https://github.com/aspnet/Extensions/issues/1697 for details.")] - public partial class TestPathUtilities - { - public TestPathUtilities() { } - public static string GetRepoRootDirectory() { throw null; } - public static string GetSolutionRootDirectory(string solution) { throw null; } - } - public static partial class TestPlatformHelper - { - public static bool IsLinux { get { throw null; } } - public static bool IsMac { get { throw null; } } - public static bool IsMono { get { throw null; } } - public static bool IsWindows { get { throw null; } } - } - public static partial class WindowsVersions - { - public const string Win10 = "10.0"; - public const string Win10_19H2 = "10.0.18363"; - public const string Win10_20H1 = "10.0.18990"; - public const string Win10_RS4 = "10.0.17134"; - public const string Win2008R2 = "6.1"; - public const string Win7 = "6.1"; - public const string Win8 = "6.2"; - public const string Win81 = "6.3"; - } -} -namespace Microsoft.AspNetCore.Testing.Tracing -{ - public partial class CollectingEventListener : System.Diagnostics.Tracing.EventListener - { - public CollectingEventListener() { } - public void CollectFrom(System.Diagnostics.Tracing.EventSource eventSource) { } - public void CollectFrom(string eventSourceName) { } - public System.Collections.Generic.IReadOnlyList GetEventsWritten() { throw null; } - protected override void OnEventSourceCreated(System.Diagnostics.Tracing.EventSource eventSource) { } - protected override void OnEventWritten(System.Diagnostics.Tracing.EventWrittenEventArgs eventData) { } - } - public partial class EventAssert - { - public EventAssert(int expectedId, string expectedName, System.Diagnostics.Tracing.EventLevel expectedLevel) { } - public static void Collection(System.Collections.Generic.IEnumerable events, params Microsoft.AspNetCore.Testing.Tracing.EventAssert[] asserts) { } - public static Microsoft.AspNetCore.Testing.Tracing.EventAssert Event(int id, string name, System.Diagnostics.Tracing.EventLevel level) { throw null; } - public Microsoft.AspNetCore.Testing.Tracing.EventAssert Payload(string name, System.Action asserter) { throw null; } - public Microsoft.AspNetCore.Testing.Tracing.EventAssert Payload(string name, object expectedValue) { throw null; } - } - [Xunit.CollectionAttribute("Microsoft.AspNetCore.Testing.Tracing.EventSourceTestCollection")] - public abstract partial class EventSourceTestBase : System.IDisposable - { - public const string CollectionName = "Microsoft.AspNetCore.Testing.Tracing.EventSourceTestCollection"; - public EventSourceTestBase() { } - protected void CollectFrom(System.Diagnostics.Tracing.EventSource eventSource) { } - protected void CollectFrom(string eventSourceName) { } - public void Dispose() { } - protected System.Collections.Generic.IReadOnlyList GetEvents() { throw null; } - } -} diff --git a/src/Testing/src/Microsoft.AspNetCore.Testing.csproj b/src/Testing/src/Microsoft.AspNetCore.Testing.csproj index 448351fee566..a27f74f21a15 100644 --- a/src/Testing/src/Microsoft.AspNetCore.Testing.csproj +++ b/src/Testing/src/Microsoft.AspNetCore.Testing.csproj @@ -9,10 +9,9 @@ false true - true - true true + false diff --git a/src/Testing/test/Microsoft.AspNetCore.Testing.Tests.csproj b/src/Testing/test/Microsoft.AspNetCore.Testing.Tests.csproj index 5a7366503d6f..654d48dcb1d4 100644 --- a/src/Testing/test/Microsoft.AspNetCore.Testing.Tests.csproj +++ b/src/Testing/test/Microsoft.AspNetCore.Testing.Tests.csproj @@ -14,7 +14,6 @@ - From 45c582fff5abc704672809e372394c6db82614c5 Mon Sep 17 00:00:00 2001 From: William Godbe Date: Fri, 17 Jan 2020 19:24:54 -0800 Subject: [PATCH 173/183] Revert "Remove ref project from Microsoft.AspNetCore.Testing (dotnet/extensions#2817)" (dotnet/extensions#2882) This reverts commit dotnet/extensions@b624d6ba91b9c292ff9a2aa786a575b0eaa31b4b.\n\nCommit migrated from https://github.com/dotnet/extensions/commit/1286a6ff55e300352dabeb6d778c9fcdd258bd08 --- .../ref/Microsoft.AspNetCore.Testing.csproj | 32 ++ .../ref/Microsoft.AspNetCore.Testing.net46.cs | 360 ++++++++++++++++++ ...osoft.AspNetCore.Testing.netstandard2.0.cs | 360 ++++++++++++++++++ .../src/Microsoft.AspNetCore.Testing.csproj | 3 +- .../Microsoft.AspNetCore.Testing.Tests.csproj | 1 + 5 files changed, 755 insertions(+), 1 deletion(-) create mode 100644 src/Testing/ref/Microsoft.AspNetCore.Testing.csproj create mode 100644 src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs create mode 100644 src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs diff --git a/src/Testing/ref/Microsoft.AspNetCore.Testing.csproj b/src/Testing/ref/Microsoft.AspNetCore.Testing.csproj new file mode 100644 index 000000000000..4606a5411106 --- /dev/null +++ b/src/Testing/ref/Microsoft.AspNetCore.Testing.csproj @@ -0,0 +1,32 @@ + + + + netstandard2.0;net46 + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs b/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs new file mode 100644 index 000000000000..a7b9c07a9968 --- /dev/null +++ b/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs @@ -0,0 +1,360 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNetCore.Testing +{ + public partial class AspNetTestAssemblyRunner : Xunit.Sdk.XunitTestAssemblyRunner + { + public AspNetTestAssemblyRunner(Xunit.Abstractions.ITestAssembly testAssembly, System.Collections.Generic.IEnumerable testCases, Xunit.Abstractions.IMessageSink diagnosticMessageSink, Xunit.Abstractions.IMessageSink executionMessageSink, Xunit.Abstractions.ITestFrameworkExecutionOptions executionOptions) : base (default(Xunit.Abstractions.ITestAssembly), default(System.Collections.Generic.IEnumerable), default(Xunit.Abstractions.IMessageSink), default(Xunit.Abstractions.IMessageSink), default(Xunit.Abstractions.ITestFrameworkExecutionOptions)) { } + [System.Diagnostics.DebuggerStepThroughAttribute] + protected override System.Threading.Tasks.Task AfterTestAssemblyStartingAsync() { throw null; } + protected override System.Threading.Tasks.Task BeforeTestAssemblyFinishedAsync() { throw null; } + protected override System.Threading.Tasks.Task RunTestCollectionAsync(Xunit.Sdk.IMessageBus messageBus, Xunit.Abstractions.ITestCollection testCollection, System.Collections.Generic.IEnumerable testCases, System.Threading.CancellationTokenSource cancellationTokenSource) { throw null; } + } + public partial class AspNetTestCollectionRunner : Xunit.Sdk.XunitTestCollectionRunner + { + public AspNetTestCollectionRunner(System.Collections.Generic.Dictionary assemblyFixtureMappings, Xunit.Abstractions.ITestCollection testCollection, System.Collections.Generic.IEnumerable testCases, Xunit.Abstractions.IMessageSink diagnosticMessageSink, Xunit.Sdk.IMessageBus messageBus, Xunit.Sdk.ITestCaseOrderer testCaseOrderer, Xunit.Sdk.ExceptionAggregator aggregator, System.Threading.CancellationTokenSource cancellationTokenSource) : base (default(Xunit.Abstractions.ITestCollection), default(System.Collections.Generic.IEnumerable), default(Xunit.Abstractions.IMessageSink), default(Xunit.Sdk.IMessageBus), default(Xunit.Sdk.ITestCaseOrderer), default(Xunit.Sdk.ExceptionAggregator), default(System.Threading.CancellationTokenSource)) { } + [System.Diagnostics.DebuggerStepThroughAttribute] + protected override System.Threading.Tasks.Task AfterTestCollectionStartingAsync() { throw null; } + protected override System.Threading.Tasks.Task BeforeTestCollectionFinishedAsync() { throw null; } + protected override System.Threading.Tasks.Task RunTestClassAsync(Xunit.Abstractions.ITestClass testClass, Xunit.Abstractions.IReflectionTypeInfo @class, System.Collections.Generic.IEnumerable testCases) { throw null; } + } + public partial class AspNetTestFramework : Xunit.Sdk.XunitTestFramework + { + public AspNetTestFramework(Xunit.Abstractions.IMessageSink messageSink) : base (default(Xunit.Abstractions.IMessageSink)) { } + protected override Xunit.Abstractions.ITestFrameworkExecutor CreateExecutor(System.Reflection.AssemblyName assemblyName) { throw null; } + } + public partial class AspNetTestFrameworkExecutor : Xunit.Sdk.XunitTestFrameworkExecutor + { + public AspNetTestFrameworkExecutor(System.Reflection.AssemblyName assemblyName, Xunit.Abstractions.ISourceInformationProvider sourceInformationProvider, Xunit.Abstractions.IMessageSink diagnosticMessageSink) : base (default(System.Reflection.AssemblyName), default(Xunit.Abstractions.ISourceInformationProvider), default(Xunit.Abstractions.IMessageSink)) { } + [System.Diagnostics.DebuggerStepThroughAttribute] + protected override void RunTestCases(System.Collections.Generic.IEnumerable testCases, Xunit.Abstractions.IMessageSink executionMessageSink, Xunit.Abstractions.ITestFrameworkExecutionOptions executionOptions) { } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly, AllowMultiple=true)] + public partial class AssemblyFixtureAttribute : System.Attribute + { + public AssemblyFixtureAttribute(System.Type fixtureType) { } + public System.Type FixtureType { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Method, AllowMultiple=false)] + [Xunit.Sdk.XunitTestCaseDiscovererAttribute("Microsoft.AspNetCore.Testing.ConditionalFactDiscoverer", "Microsoft.AspNetCore.Testing")] + public partial class ConditionalFactAttribute : Xunit.FactAttribute + { + public ConditionalFactAttribute() { } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Method, AllowMultiple=false)] + [Xunit.Sdk.XunitTestCaseDiscovererAttribute("Microsoft.AspNetCore.Testing.ConditionalTheoryDiscoverer", "Microsoft.AspNetCore.Testing")] + public partial class ConditionalTheoryAttribute : Xunit.TheoryAttribute + { + public ConditionalTheoryAttribute() { } + } + public partial class CultureReplacer : System.IDisposable + { + public CultureReplacer(System.Globalization.CultureInfo culture, System.Globalization.CultureInfo uiCulture) { } + public CultureReplacer(string culture = "en-GB", string uiCulture = "en-US") { } + public static System.Globalization.CultureInfo DefaultCulture { get { throw null; } } + public static string DefaultCultureName { get { throw null; } } + public static string DefaultUICultureName { get { throw null; } } + public void Dispose() { } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Method, Inherited=true, AllowMultiple=false)] + public sealed partial class DockerOnlyAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition + { + public DockerOnlyAttribute() { } + public bool IsMet { get { throw null; } } + public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=true)] + public partial class EnvironmentVariableSkipConditionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition + { + public EnvironmentVariableSkipConditionAttribute(string variableName, params string[] values) { } + public bool IsMet { get { throw null; } } + public bool RunOnMatch { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public string SkipReason { get { throw null; } } + } + public static partial class ExceptionAssert + { + public static System.ArgumentException ThrowsArgument(System.Action testCode, string paramName, string exceptionMessage) { throw null; } + public static System.Threading.Tasks.Task ThrowsArgumentAsync(System.Func testCode, string paramName, string exceptionMessage) { throw null; } + public static System.ArgumentNullException ThrowsArgumentNull(System.Action testCode, string paramName) { throw null; } + public static System.ArgumentException ThrowsArgumentNullOrEmpty(System.Action testCode, string paramName) { throw null; } + public static System.Threading.Tasks.Task ThrowsArgumentNullOrEmptyAsync(System.Func testCode, string paramName) { throw null; } + public static System.ArgumentException ThrowsArgumentNullOrEmptyString(System.Action testCode, string paramName) { throw null; } + public static System.Threading.Tasks.Task ThrowsArgumentNullOrEmptyStringAsync(System.Func testCode, string paramName) { throw null; } + public static System.ArgumentOutOfRangeException ThrowsArgumentOutOfRange(System.Action testCode, string paramName, string exceptionMessage, object actualValue = null) { throw null; } + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.Task ThrowsAsync(System.Func testCode, string exceptionMessage) where TException : System.Exception { throw null; } + public static TException Throws(System.Action testCode) where TException : System.Exception { throw null; } + public static TException Throws(System.Action testCode, string exceptionMessage) where TException : System.Exception { throw null; } + public static TException Throws(System.Func testCode, string exceptionMessage) where TException : System.Exception { throw null; } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method)] + [Xunit.Sdk.TraitDiscovererAttribute("Microsoft.AspNetCore.Testing.FlakyTraitDiscoverer", "Microsoft.AspNetCore.Testing")] + public sealed partial class FlakyAttribute : System.Attribute, Xunit.Sdk.ITraitAttribute + { + public FlakyAttribute(string gitHubIssueUrl, string firstFilter, params string[] additionalFilters) { } + public System.Collections.Generic.IReadOnlyList Filters { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string GitHubIssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } + public static partial class FlakyOn + { + public const string All = "All"; + public static partial class AzP + { + public const string All = "AzP:All"; + public const string Linux = "AzP:OS:Linux"; + public const string macOS = "AzP:OS:Darwin"; + public const string Windows = "AzP:OS:Windows_NT"; + } + public static partial class Helix + { + public const string All = "Helix:Queue:All"; + public const string Centos7Amd64 = "Helix:Queue:Centos.7.Amd64.Open"; + public const string Debian8Amd64 = "Helix:Queue:Debian.8.Amd64.Open"; + public const string Debian9Amd64 = "Helix:Queue:Debian.9.Amd64.Open"; + public const string Fedora27Amd64 = "Helix:Queue:Fedora.27.Amd64.Open"; + public const string Fedora28Amd64 = "Helix:Queue:Fedora.28.Amd64.Open"; + public const string macOS1012Amd64 = "Helix:Queue:OSX.1012.Amd64.Open"; + public const string Redhat7Amd64 = "Helix:Queue:Redhat.7.Amd64.Open"; + public const string Ubuntu1604Amd64 = "Helix:Queue:Ubuntu.1604.Amd64.Open"; + public const string Ubuntu1810Amd64 = "Helix:Queue:Ubuntu.1810.Amd64.Open"; + public const string Windows10Amd64 = "Helix:Queue:Windows.10.Amd64.ClientRS4.VS2017.Open"; + } + } + public partial class FlakyTraitDiscoverer : Xunit.Sdk.ITraitDiscoverer + { + public FlakyTraitDiscoverer() { } + public System.Collections.Generic.IEnumerable> GetTraits(Xunit.Abstractions.IAttributeInfo traitAttribute) { throw null; } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Method, AllowMultiple=false)] + public partial class FrameworkSkipConditionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition + { + public FrameworkSkipConditionAttribute(Microsoft.AspNetCore.Testing.RuntimeFrameworks excludedFrameworks) { } + public bool IsMet { get { throw null; } } + public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + } + public static partial class HelixQueues + { + public const string Centos7Amd64 = "Centos.7.Amd64.Open"; + public const string Debian8Amd64 = "Debian.8.Amd64.Open"; + public const string Debian9Amd64 = "Debian.9.Amd64.Open"; + public const string Fedora27Amd64 = "Fedora.27.Amd64.Open"; + public const string Fedora28Amd64 = "Fedora.28.Amd64.Open"; + public const string macOS1012Amd64 = "OSX.1012.Amd64.Open"; + public const string Redhat7Amd64 = "Redhat.7.Amd64.Open"; + public const string Ubuntu1604Amd64 = "Ubuntu.1604.Amd64.Open"; + public const string Ubuntu1810Amd64 = "Ubuntu.1810.Amd64.Open"; + public const string Windows10Amd64 = "Windows.10.Amd64.ClientRS4.VS2017.Open"; + } + public static partial class HttpClientSlim + { + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.Task GetSocket(System.Uri requestUri) { throw null; } + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.Task GetStringAsync(string requestUri, bool validateCertificate = true) { throw null; } + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.Task GetStringAsync(System.Uri requestUri, bool validateCertificate = true) { throw null; } + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.Task PostAsync(string requestUri, System.Net.Http.HttpContent content, bool validateCertificate = true) { throw null; } + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.Task PostAsync(System.Uri requestUri, System.Net.Http.HttpContent content, bool validateCertificate = true) { throw null; } + } + public partial interface ITestCondition + { + bool IsMet { get; } + string SkipReason { get; } + } + public partial interface ITestMethodLifecycle + { + System.Threading.Tasks.Task OnTestEndAsync(Microsoft.AspNetCore.Testing.TestContext context, System.Exception exception, System.Threading.CancellationToken cancellationToken); + System.Threading.Tasks.Task OnTestStartAsync(Microsoft.AspNetCore.Testing.TestContext context, System.Threading.CancellationToken cancellationToken); + } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=true)] + public partial class MinimumOSVersionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition + { + public MinimumOSVersionAttribute(Microsoft.AspNetCore.Testing.OperatingSystems operatingSystem, string minVersion) { } + public bool IsMet { get { throw null; } } + public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + } + [System.FlagsAttribute] + public enum OperatingSystems + { + Linux = 1, + MacOSX = 2, + Windows = 4, + } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=true)] + public partial class OSSkipConditionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition + { + public OSSkipConditionAttribute(Microsoft.AspNetCore.Testing.OperatingSystems operatingSystem, params string[] versions) { } + public bool IsMet { get { throw null; } } + public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=false)] + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public partial class RepeatAttribute : System.Attribute + { + public RepeatAttribute(int runCount = 10) { } + public int RunCount { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } + public partial class RepeatContext + { + public RepeatContext(int limit) { } + public static Microsoft.AspNetCore.Testing.RepeatContext Current { get { throw null; } } + public int CurrentIteration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public int Limit { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Method)] + public partial class ReplaceCultureAttribute : Xunit.Sdk.BeforeAfterTestAttribute + { + public ReplaceCultureAttribute() { } + public ReplaceCultureAttribute(string currentCulture, string currentUICulture) { } + public System.Globalization.CultureInfo Culture { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Globalization.CultureInfo UICulture { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public override void After(System.Reflection.MethodInfo methodUnderTest) { } + public override void Before(System.Reflection.MethodInfo methodUnderTest) { } + } + [System.FlagsAttribute] + public enum RuntimeFrameworks + { + None = 0, + Mono = 1, + CLR = 2, + CoreCLR = 4, + } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class, AllowMultiple=false)] + public partial class ShortClassNameAttribute : System.Attribute + { + public ShortClassNameAttribute() { } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=false)] + public partial class SkipOnCIAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition + { + public SkipOnCIAttribute(string issueUrl = "") { } + public bool IsMet { get { throw null; } } + public string IssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string SkipReason { get { throw null; } } + public static string GetIfOnAzdo() { throw null; } + public static string GetTargetHelixQueue() { throw null; } + public static bool OnAzdo() { throw null; } + public static bool OnCI() { throw null; } + public static bool OnHelix() { throw null; } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=false)] + public partial class SkipOnHelixAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition + { + public SkipOnHelixAttribute(string issueUrl) { } + public bool IsMet { get { throw null; } } + public string IssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string Queues { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public string SkipReason { get { throw null; } } + public static string GetTargetHelixQueue() { throw null; } + public static bool OnHelix() { throw null; } + } + public partial class SkippedTestCase : Xunit.Sdk.XunitTestCase + { + [System.ObsoleteAttribute("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")] + public SkippedTestCase() { } + public SkippedTestCase(string skipReason, Xunit.Abstractions.IMessageSink diagnosticMessageSink, Xunit.Sdk.TestMethodDisplay defaultMethodDisplay, Xunit.Sdk.TestMethodDisplayOptions defaultMethodDisplayOptions, Xunit.Abstractions.ITestMethod testMethod, object[] testMethodArguments = null) { } + public override void Deserialize(Xunit.Abstractions.IXunitSerializationInfo data) { } + protected override string GetSkipReason(Xunit.Abstractions.IAttributeInfo factAttribute) { throw null; } + public override void Serialize(Xunit.Abstractions.IXunitSerializationInfo data) { } + } + public static partial class TaskExtensions + { + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.Task TimeoutAfter(this System.Threading.Tasks.Task task, System.TimeSpan timeout, [System.Runtime.CompilerServices.CallerFilePathAttribute]string filePath = null, [System.Runtime.CompilerServices.CallerLineNumberAttribute]int lineNumber = 0) { throw null; } + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.Task TimeoutAfter(this System.Threading.Tasks.Task task, System.TimeSpan timeout, [System.Runtime.CompilerServices.CallerFilePathAttribute]string filePath = null, [System.Runtime.CompilerServices.CallerLineNumberAttribute]int lineNumber = 0) { throw null; } + } + public sealed partial class TestContext + { + public TestContext(System.Type testClass, object[] constructorArguments, System.Reflection.MethodInfo testMethod, object[] methodArguments, Xunit.Abstractions.ITestOutputHelper output) { } + public object[] ConstructorArguments { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public Microsoft.AspNetCore.Testing.TestFileOutputContext FileOutput { get { throw null; } } + public object[] MethodArguments { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public Xunit.Abstractions.ITestOutputHelper Output { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Type TestClass { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Reflection.MethodInfo TestMethod { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } + public sealed partial class TestFileOutputContext + { + public TestFileOutputContext(Microsoft.AspNetCore.Testing.TestContext parent) { } + public string AssemblyOutputDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string TestClassName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string TestClassOutputDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string TestName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public static string GetAssemblyBaseDirectory(System.Reflection.Assembly assembly, string baseDirectory = null) { throw null; } + public static string GetOutputDirectory(System.Reflection.Assembly assembly) { throw null; } + public static string GetTestClassName(System.Type type) { throw null; } + public static string GetTestMethodName(System.Reflection.MethodInfo method, object[] arguments) { throw null; } + public string GetUniqueFileName(string prefix, string extension) { throw null; } + public static string RemoveIllegalFileChars(string s) { throw null; } + } + public static partial class TestMethodExtensions + { + public static string EvaluateSkipConditions(this Xunit.Abstractions.ITestMethod testMethod) { throw null; } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly, AllowMultiple=false, Inherited=true)] + public partial class TestOutputDirectoryAttribute : System.Attribute + { + public TestOutputDirectoryAttribute(string targetFramework, string baseDirectory = null) { } + public string BaseDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string TargetFramework { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } + [System.ObsoleteAttribute("This API is obsolete and the pattern its usage encouraged should not be used anymore. See https://github.com/aspnet/Extensions/issues/1697 for details.")] + public partial class TestPathUtilities + { + public TestPathUtilities() { } + public static string GetRepoRootDirectory() { throw null; } + public static string GetSolutionRootDirectory(string solution) { throw null; } + } + public static partial class TestPlatformHelper + { + public static bool IsLinux { get { throw null; } } + public static bool IsMac { get { throw null; } } + public static bool IsMono { get { throw null; } } + public static bool IsWindows { get { throw null; } } + } + public static partial class WindowsVersions + { + public const string Win10 = "10.0"; + public const string Win10_19H2 = "10.0.18363"; + public const string Win10_20H1 = "10.0.18990"; + public const string Win10_RS4 = "10.0.17134"; + public const string Win2008R2 = "6.1"; + public const string Win7 = "6.1"; + public const string Win8 = "6.2"; + public const string Win81 = "6.3"; + } +} +namespace Microsoft.AspNetCore.Testing.Tracing +{ + public partial class CollectingEventListener : System.Diagnostics.Tracing.EventListener + { + public CollectingEventListener() { } + public void CollectFrom(System.Diagnostics.Tracing.EventSource eventSource) { } + public void CollectFrom(string eventSourceName) { } + public System.Collections.Generic.IReadOnlyList GetEventsWritten() { throw null; } + protected override void OnEventSourceCreated(System.Diagnostics.Tracing.EventSource eventSource) { } + protected override void OnEventWritten(System.Diagnostics.Tracing.EventWrittenEventArgs eventData) { } + } + public partial class EventAssert + { + public EventAssert(int expectedId, string expectedName, System.Diagnostics.Tracing.EventLevel expectedLevel) { } + public static void Collection(System.Collections.Generic.IEnumerable events, params Microsoft.AspNetCore.Testing.Tracing.EventAssert[] asserts) { } + public static Microsoft.AspNetCore.Testing.Tracing.EventAssert Event(int id, string name, System.Diagnostics.Tracing.EventLevel level) { throw null; } + public Microsoft.AspNetCore.Testing.Tracing.EventAssert Payload(string name, System.Action asserter) { throw null; } + public Microsoft.AspNetCore.Testing.Tracing.EventAssert Payload(string name, object expectedValue) { throw null; } + } + [Xunit.CollectionAttribute("Microsoft.AspNetCore.Testing.Tracing.EventSourceTestCollection")] + public abstract partial class EventSourceTestBase : System.IDisposable + { + public const string CollectionName = "Microsoft.AspNetCore.Testing.Tracing.EventSourceTestCollection"; + public EventSourceTestBase() { } + protected void CollectFrom(System.Diagnostics.Tracing.EventSource eventSource) { } + protected void CollectFrom(string eventSourceName) { } + public void Dispose() { } + protected System.Collections.Generic.IReadOnlyList GetEvents() { throw null; } + } +} diff --git a/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs b/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs new file mode 100644 index 000000000000..a7b9c07a9968 --- /dev/null +++ b/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs @@ -0,0 +1,360 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNetCore.Testing +{ + public partial class AspNetTestAssemblyRunner : Xunit.Sdk.XunitTestAssemblyRunner + { + public AspNetTestAssemblyRunner(Xunit.Abstractions.ITestAssembly testAssembly, System.Collections.Generic.IEnumerable testCases, Xunit.Abstractions.IMessageSink diagnosticMessageSink, Xunit.Abstractions.IMessageSink executionMessageSink, Xunit.Abstractions.ITestFrameworkExecutionOptions executionOptions) : base (default(Xunit.Abstractions.ITestAssembly), default(System.Collections.Generic.IEnumerable), default(Xunit.Abstractions.IMessageSink), default(Xunit.Abstractions.IMessageSink), default(Xunit.Abstractions.ITestFrameworkExecutionOptions)) { } + [System.Diagnostics.DebuggerStepThroughAttribute] + protected override System.Threading.Tasks.Task AfterTestAssemblyStartingAsync() { throw null; } + protected override System.Threading.Tasks.Task BeforeTestAssemblyFinishedAsync() { throw null; } + protected override System.Threading.Tasks.Task RunTestCollectionAsync(Xunit.Sdk.IMessageBus messageBus, Xunit.Abstractions.ITestCollection testCollection, System.Collections.Generic.IEnumerable testCases, System.Threading.CancellationTokenSource cancellationTokenSource) { throw null; } + } + public partial class AspNetTestCollectionRunner : Xunit.Sdk.XunitTestCollectionRunner + { + public AspNetTestCollectionRunner(System.Collections.Generic.Dictionary assemblyFixtureMappings, Xunit.Abstractions.ITestCollection testCollection, System.Collections.Generic.IEnumerable testCases, Xunit.Abstractions.IMessageSink diagnosticMessageSink, Xunit.Sdk.IMessageBus messageBus, Xunit.Sdk.ITestCaseOrderer testCaseOrderer, Xunit.Sdk.ExceptionAggregator aggregator, System.Threading.CancellationTokenSource cancellationTokenSource) : base (default(Xunit.Abstractions.ITestCollection), default(System.Collections.Generic.IEnumerable), default(Xunit.Abstractions.IMessageSink), default(Xunit.Sdk.IMessageBus), default(Xunit.Sdk.ITestCaseOrderer), default(Xunit.Sdk.ExceptionAggregator), default(System.Threading.CancellationTokenSource)) { } + [System.Diagnostics.DebuggerStepThroughAttribute] + protected override System.Threading.Tasks.Task AfterTestCollectionStartingAsync() { throw null; } + protected override System.Threading.Tasks.Task BeforeTestCollectionFinishedAsync() { throw null; } + protected override System.Threading.Tasks.Task RunTestClassAsync(Xunit.Abstractions.ITestClass testClass, Xunit.Abstractions.IReflectionTypeInfo @class, System.Collections.Generic.IEnumerable testCases) { throw null; } + } + public partial class AspNetTestFramework : Xunit.Sdk.XunitTestFramework + { + public AspNetTestFramework(Xunit.Abstractions.IMessageSink messageSink) : base (default(Xunit.Abstractions.IMessageSink)) { } + protected override Xunit.Abstractions.ITestFrameworkExecutor CreateExecutor(System.Reflection.AssemblyName assemblyName) { throw null; } + } + public partial class AspNetTestFrameworkExecutor : Xunit.Sdk.XunitTestFrameworkExecutor + { + public AspNetTestFrameworkExecutor(System.Reflection.AssemblyName assemblyName, Xunit.Abstractions.ISourceInformationProvider sourceInformationProvider, Xunit.Abstractions.IMessageSink diagnosticMessageSink) : base (default(System.Reflection.AssemblyName), default(Xunit.Abstractions.ISourceInformationProvider), default(Xunit.Abstractions.IMessageSink)) { } + [System.Diagnostics.DebuggerStepThroughAttribute] + protected override void RunTestCases(System.Collections.Generic.IEnumerable testCases, Xunit.Abstractions.IMessageSink executionMessageSink, Xunit.Abstractions.ITestFrameworkExecutionOptions executionOptions) { } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly, AllowMultiple=true)] + public partial class AssemblyFixtureAttribute : System.Attribute + { + public AssemblyFixtureAttribute(System.Type fixtureType) { } + public System.Type FixtureType { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Method, AllowMultiple=false)] + [Xunit.Sdk.XunitTestCaseDiscovererAttribute("Microsoft.AspNetCore.Testing.ConditionalFactDiscoverer", "Microsoft.AspNetCore.Testing")] + public partial class ConditionalFactAttribute : Xunit.FactAttribute + { + public ConditionalFactAttribute() { } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Method, AllowMultiple=false)] + [Xunit.Sdk.XunitTestCaseDiscovererAttribute("Microsoft.AspNetCore.Testing.ConditionalTheoryDiscoverer", "Microsoft.AspNetCore.Testing")] + public partial class ConditionalTheoryAttribute : Xunit.TheoryAttribute + { + public ConditionalTheoryAttribute() { } + } + public partial class CultureReplacer : System.IDisposable + { + public CultureReplacer(System.Globalization.CultureInfo culture, System.Globalization.CultureInfo uiCulture) { } + public CultureReplacer(string culture = "en-GB", string uiCulture = "en-US") { } + public static System.Globalization.CultureInfo DefaultCulture { get { throw null; } } + public static string DefaultCultureName { get { throw null; } } + public static string DefaultUICultureName { get { throw null; } } + public void Dispose() { } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Method, Inherited=true, AllowMultiple=false)] + public sealed partial class DockerOnlyAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition + { + public DockerOnlyAttribute() { } + public bool IsMet { get { throw null; } } + public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=true)] + public partial class EnvironmentVariableSkipConditionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition + { + public EnvironmentVariableSkipConditionAttribute(string variableName, params string[] values) { } + public bool IsMet { get { throw null; } } + public bool RunOnMatch { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public string SkipReason { get { throw null; } } + } + public static partial class ExceptionAssert + { + public static System.ArgumentException ThrowsArgument(System.Action testCode, string paramName, string exceptionMessage) { throw null; } + public static System.Threading.Tasks.Task ThrowsArgumentAsync(System.Func testCode, string paramName, string exceptionMessage) { throw null; } + public static System.ArgumentNullException ThrowsArgumentNull(System.Action testCode, string paramName) { throw null; } + public static System.ArgumentException ThrowsArgumentNullOrEmpty(System.Action testCode, string paramName) { throw null; } + public static System.Threading.Tasks.Task ThrowsArgumentNullOrEmptyAsync(System.Func testCode, string paramName) { throw null; } + public static System.ArgumentException ThrowsArgumentNullOrEmptyString(System.Action testCode, string paramName) { throw null; } + public static System.Threading.Tasks.Task ThrowsArgumentNullOrEmptyStringAsync(System.Func testCode, string paramName) { throw null; } + public static System.ArgumentOutOfRangeException ThrowsArgumentOutOfRange(System.Action testCode, string paramName, string exceptionMessage, object actualValue = null) { throw null; } + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.Task ThrowsAsync(System.Func testCode, string exceptionMessage) where TException : System.Exception { throw null; } + public static TException Throws(System.Action testCode) where TException : System.Exception { throw null; } + public static TException Throws(System.Action testCode, string exceptionMessage) where TException : System.Exception { throw null; } + public static TException Throws(System.Func testCode, string exceptionMessage) where TException : System.Exception { throw null; } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method)] + [Xunit.Sdk.TraitDiscovererAttribute("Microsoft.AspNetCore.Testing.FlakyTraitDiscoverer", "Microsoft.AspNetCore.Testing")] + public sealed partial class FlakyAttribute : System.Attribute, Xunit.Sdk.ITraitAttribute + { + public FlakyAttribute(string gitHubIssueUrl, string firstFilter, params string[] additionalFilters) { } + public System.Collections.Generic.IReadOnlyList Filters { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string GitHubIssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } + public static partial class FlakyOn + { + public const string All = "All"; + public static partial class AzP + { + public const string All = "AzP:All"; + public const string Linux = "AzP:OS:Linux"; + public const string macOS = "AzP:OS:Darwin"; + public const string Windows = "AzP:OS:Windows_NT"; + } + public static partial class Helix + { + public const string All = "Helix:Queue:All"; + public const string Centos7Amd64 = "Helix:Queue:Centos.7.Amd64.Open"; + public const string Debian8Amd64 = "Helix:Queue:Debian.8.Amd64.Open"; + public const string Debian9Amd64 = "Helix:Queue:Debian.9.Amd64.Open"; + public const string Fedora27Amd64 = "Helix:Queue:Fedora.27.Amd64.Open"; + public const string Fedora28Amd64 = "Helix:Queue:Fedora.28.Amd64.Open"; + public const string macOS1012Amd64 = "Helix:Queue:OSX.1012.Amd64.Open"; + public const string Redhat7Amd64 = "Helix:Queue:Redhat.7.Amd64.Open"; + public const string Ubuntu1604Amd64 = "Helix:Queue:Ubuntu.1604.Amd64.Open"; + public const string Ubuntu1810Amd64 = "Helix:Queue:Ubuntu.1810.Amd64.Open"; + public const string Windows10Amd64 = "Helix:Queue:Windows.10.Amd64.ClientRS4.VS2017.Open"; + } + } + public partial class FlakyTraitDiscoverer : Xunit.Sdk.ITraitDiscoverer + { + public FlakyTraitDiscoverer() { } + public System.Collections.Generic.IEnumerable> GetTraits(Xunit.Abstractions.IAttributeInfo traitAttribute) { throw null; } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Method, AllowMultiple=false)] + public partial class FrameworkSkipConditionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition + { + public FrameworkSkipConditionAttribute(Microsoft.AspNetCore.Testing.RuntimeFrameworks excludedFrameworks) { } + public bool IsMet { get { throw null; } } + public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + } + public static partial class HelixQueues + { + public const string Centos7Amd64 = "Centos.7.Amd64.Open"; + public const string Debian8Amd64 = "Debian.8.Amd64.Open"; + public const string Debian9Amd64 = "Debian.9.Amd64.Open"; + public const string Fedora27Amd64 = "Fedora.27.Amd64.Open"; + public const string Fedora28Amd64 = "Fedora.28.Amd64.Open"; + public const string macOS1012Amd64 = "OSX.1012.Amd64.Open"; + public const string Redhat7Amd64 = "Redhat.7.Amd64.Open"; + public const string Ubuntu1604Amd64 = "Ubuntu.1604.Amd64.Open"; + public const string Ubuntu1810Amd64 = "Ubuntu.1810.Amd64.Open"; + public const string Windows10Amd64 = "Windows.10.Amd64.ClientRS4.VS2017.Open"; + } + public static partial class HttpClientSlim + { + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.Task GetSocket(System.Uri requestUri) { throw null; } + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.Task GetStringAsync(string requestUri, bool validateCertificate = true) { throw null; } + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.Task GetStringAsync(System.Uri requestUri, bool validateCertificate = true) { throw null; } + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.Task PostAsync(string requestUri, System.Net.Http.HttpContent content, bool validateCertificate = true) { throw null; } + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.Task PostAsync(System.Uri requestUri, System.Net.Http.HttpContent content, bool validateCertificate = true) { throw null; } + } + public partial interface ITestCondition + { + bool IsMet { get; } + string SkipReason { get; } + } + public partial interface ITestMethodLifecycle + { + System.Threading.Tasks.Task OnTestEndAsync(Microsoft.AspNetCore.Testing.TestContext context, System.Exception exception, System.Threading.CancellationToken cancellationToken); + System.Threading.Tasks.Task OnTestStartAsync(Microsoft.AspNetCore.Testing.TestContext context, System.Threading.CancellationToken cancellationToken); + } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=true)] + public partial class MinimumOSVersionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition + { + public MinimumOSVersionAttribute(Microsoft.AspNetCore.Testing.OperatingSystems operatingSystem, string minVersion) { } + public bool IsMet { get { throw null; } } + public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + } + [System.FlagsAttribute] + public enum OperatingSystems + { + Linux = 1, + MacOSX = 2, + Windows = 4, + } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=true)] + public partial class OSSkipConditionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition + { + public OSSkipConditionAttribute(Microsoft.AspNetCore.Testing.OperatingSystems operatingSystem, params string[] versions) { } + public bool IsMet { get { throw null; } } + public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=false)] + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public partial class RepeatAttribute : System.Attribute + { + public RepeatAttribute(int runCount = 10) { } + public int RunCount { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } + public partial class RepeatContext + { + public RepeatContext(int limit) { } + public static Microsoft.AspNetCore.Testing.RepeatContext Current { get { throw null; } } + public int CurrentIteration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public int Limit { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Method)] + public partial class ReplaceCultureAttribute : Xunit.Sdk.BeforeAfterTestAttribute + { + public ReplaceCultureAttribute() { } + public ReplaceCultureAttribute(string currentCulture, string currentUICulture) { } + public System.Globalization.CultureInfo Culture { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Globalization.CultureInfo UICulture { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public override void After(System.Reflection.MethodInfo methodUnderTest) { } + public override void Before(System.Reflection.MethodInfo methodUnderTest) { } + } + [System.FlagsAttribute] + public enum RuntimeFrameworks + { + None = 0, + Mono = 1, + CLR = 2, + CoreCLR = 4, + } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class, AllowMultiple=false)] + public partial class ShortClassNameAttribute : System.Attribute + { + public ShortClassNameAttribute() { } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=false)] + public partial class SkipOnCIAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition + { + public SkipOnCIAttribute(string issueUrl = "") { } + public bool IsMet { get { throw null; } } + public string IssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string SkipReason { get { throw null; } } + public static string GetIfOnAzdo() { throw null; } + public static string GetTargetHelixQueue() { throw null; } + public static bool OnAzdo() { throw null; } + public static bool OnCI() { throw null; } + public static bool OnHelix() { throw null; } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=false)] + public partial class SkipOnHelixAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition + { + public SkipOnHelixAttribute(string issueUrl) { } + public bool IsMet { get { throw null; } } + public string IssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string Queues { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public string SkipReason { get { throw null; } } + public static string GetTargetHelixQueue() { throw null; } + public static bool OnHelix() { throw null; } + } + public partial class SkippedTestCase : Xunit.Sdk.XunitTestCase + { + [System.ObsoleteAttribute("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")] + public SkippedTestCase() { } + public SkippedTestCase(string skipReason, Xunit.Abstractions.IMessageSink diagnosticMessageSink, Xunit.Sdk.TestMethodDisplay defaultMethodDisplay, Xunit.Sdk.TestMethodDisplayOptions defaultMethodDisplayOptions, Xunit.Abstractions.ITestMethod testMethod, object[] testMethodArguments = null) { } + public override void Deserialize(Xunit.Abstractions.IXunitSerializationInfo data) { } + protected override string GetSkipReason(Xunit.Abstractions.IAttributeInfo factAttribute) { throw null; } + public override void Serialize(Xunit.Abstractions.IXunitSerializationInfo data) { } + } + public static partial class TaskExtensions + { + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.Task TimeoutAfter(this System.Threading.Tasks.Task task, System.TimeSpan timeout, [System.Runtime.CompilerServices.CallerFilePathAttribute]string filePath = null, [System.Runtime.CompilerServices.CallerLineNumberAttribute]int lineNumber = 0) { throw null; } + [System.Diagnostics.DebuggerStepThroughAttribute] + public static System.Threading.Tasks.Task TimeoutAfter(this System.Threading.Tasks.Task task, System.TimeSpan timeout, [System.Runtime.CompilerServices.CallerFilePathAttribute]string filePath = null, [System.Runtime.CompilerServices.CallerLineNumberAttribute]int lineNumber = 0) { throw null; } + } + public sealed partial class TestContext + { + public TestContext(System.Type testClass, object[] constructorArguments, System.Reflection.MethodInfo testMethod, object[] methodArguments, Xunit.Abstractions.ITestOutputHelper output) { } + public object[] ConstructorArguments { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public Microsoft.AspNetCore.Testing.TestFileOutputContext FileOutput { get { throw null; } } + public object[] MethodArguments { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public Xunit.Abstractions.ITestOutputHelper Output { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Type TestClass { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Reflection.MethodInfo TestMethod { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } + public sealed partial class TestFileOutputContext + { + public TestFileOutputContext(Microsoft.AspNetCore.Testing.TestContext parent) { } + public string AssemblyOutputDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string TestClassName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string TestClassOutputDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string TestName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public static string GetAssemblyBaseDirectory(System.Reflection.Assembly assembly, string baseDirectory = null) { throw null; } + public static string GetOutputDirectory(System.Reflection.Assembly assembly) { throw null; } + public static string GetTestClassName(System.Type type) { throw null; } + public static string GetTestMethodName(System.Reflection.MethodInfo method, object[] arguments) { throw null; } + public string GetUniqueFileName(string prefix, string extension) { throw null; } + public static string RemoveIllegalFileChars(string s) { throw null; } + } + public static partial class TestMethodExtensions + { + public static string EvaluateSkipConditions(this Xunit.Abstractions.ITestMethod testMethod) { throw null; } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly, AllowMultiple=false, Inherited=true)] + public partial class TestOutputDirectoryAttribute : System.Attribute + { + public TestOutputDirectoryAttribute(string targetFramework, string baseDirectory = null) { } + public string BaseDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string TargetFramework { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } + [System.ObsoleteAttribute("This API is obsolete and the pattern its usage encouraged should not be used anymore. See https://github.com/aspnet/Extensions/issues/1697 for details.")] + public partial class TestPathUtilities + { + public TestPathUtilities() { } + public static string GetRepoRootDirectory() { throw null; } + public static string GetSolutionRootDirectory(string solution) { throw null; } + } + public static partial class TestPlatformHelper + { + public static bool IsLinux { get { throw null; } } + public static bool IsMac { get { throw null; } } + public static bool IsMono { get { throw null; } } + public static bool IsWindows { get { throw null; } } + } + public static partial class WindowsVersions + { + public const string Win10 = "10.0"; + public const string Win10_19H2 = "10.0.18363"; + public const string Win10_20H1 = "10.0.18990"; + public const string Win10_RS4 = "10.0.17134"; + public const string Win2008R2 = "6.1"; + public const string Win7 = "6.1"; + public const string Win8 = "6.2"; + public const string Win81 = "6.3"; + } +} +namespace Microsoft.AspNetCore.Testing.Tracing +{ + public partial class CollectingEventListener : System.Diagnostics.Tracing.EventListener + { + public CollectingEventListener() { } + public void CollectFrom(System.Diagnostics.Tracing.EventSource eventSource) { } + public void CollectFrom(string eventSourceName) { } + public System.Collections.Generic.IReadOnlyList GetEventsWritten() { throw null; } + protected override void OnEventSourceCreated(System.Diagnostics.Tracing.EventSource eventSource) { } + protected override void OnEventWritten(System.Diagnostics.Tracing.EventWrittenEventArgs eventData) { } + } + public partial class EventAssert + { + public EventAssert(int expectedId, string expectedName, System.Diagnostics.Tracing.EventLevel expectedLevel) { } + public static void Collection(System.Collections.Generic.IEnumerable events, params Microsoft.AspNetCore.Testing.Tracing.EventAssert[] asserts) { } + public static Microsoft.AspNetCore.Testing.Tracing.EventAssert Event(int id, string name, System.Diagnostics.Tracing.EventLevel level) { throw null; } + public Microsoft.AspNetCore.Testing.Tracing.EventAssert Payload(string name, System.Action asserter) { throw null; } + public Microsoft.AspNetCore.Testing.Tracing.EventAssert Payload(string name, object expectedValue) { throw null; } + } + [Xunit.CollectionAttribute("Microsoft.AspNetCore.Testing.Tracing.EventSourceTestCollection")] + public abstract partial class EventSourceTestBase : System.IDisposable + { + public const string CollectionName = "Microsoft.AspNetCore.Testing.Tracing.EventSourceTestCollection"; + public EventSourceTestBase() { } + protected void CollectFrom(System.Diagnostics.Tracing.EventSource eventSource) { } + protected void CollectFrom(string eventSourceName) { } + public void Dispose() { } + protected System.Collections.Generic.IReadOnlyList GetEvents() { throw null; } + } +} diff --git a/src/Testing/src/Microsoft.AspNetCore.Testing.csproj b/src/Testing/src/Microsoft.AspNetCore.Testing.csproj index a27f74f21a15..448351fee566 100644 --- a/src/Testing/src/Microsoft.AspNetCore.Testing.csproj +++ b/src/Testing/src/Microsoft.AspNetCore.Testing.csproj @@ -9,9 +9,10 @@ false true + true + true true - false diff --git a/src/Testing/test/Microsoft.AspNetCore.Testing.Tests.csproj b/src/Testing/test/Microsoft.AspNetCore.Testing.Tests.csproj index 654d48dcb1d4..5a7366503d6f 100644 --- a/src/Testing/test/Microsoft.AspNetCore.Testing.Tests.csproj +++ b/src/Testing/test/Microsoft.AspNetCore.Testing.Tests.csproj @@ -14,6 +14,7 @@ + From 64668496e578851e50ab6705e934e88000c0a88a Mon Sep 17 00:00:00 2001 From: Maher Jendoubi Date: Tue, 21 Jan 2020 17:18:46 +0100 Subject: [PATCH 174/183] Contributing: fixed some typos (dotnet/extensions#2886) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/6a4a1907d4f04e6fdfc5a2331fdd4e7523e08bc5 --- src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs b/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs index d7ce9edb9e61..253e6a4746f0 100644 --- a/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs +++ b/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs @@ -122,7 +122,7 @@ private async Task RunCheckAsync(IServiceScope scope, HealthC var duration = stopwatch.GetElapsedTime(); entry = new HealthReportEntry( status: HealthStatus.Unhealthy, - description: "A timeout occured while running check.", + description: "A timeout occurred while running check.", duration: duration, exception: ex, data: null); From 100ddd7bfb949734a910c8da8d1401c918396f62 Mon Sep 17 00:00:00 2001 From: John Luo Date: Wed, 22 Jan 2020 19:41:29 -0800 Subject: [PATCH 175/183] Regen ref assemblies \n\nCommit migrated from https://github.com/dotnet/extensions/commit/b6e88cca0dedf2b01b60038273f90d412694ae95 --- .../ref/Microsoft.AspNetCore.Testing.net46.cs | 78 +++++++++++-------- ...osoft.AspNetCore.Testing.netstandard2.0.cs | 78 +++++++++++-------- 2 files changed, 92 insertions(+), 64 deletions(-) diff --git a/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs b/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs index a7b9c07a9968..17a922a6c7b7 100644 --- a/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs +++ b/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs @@ -34,7 +34,7 @@ protected override void RunTestCases(System.Collections.Generic.IEnumerable Filters { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string GitHubIssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Collections.Generic.IReadOnlyList Filters { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public string GitHubIssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } } public static partial class FlakyOn { @@ -131,7 +131,7 @@ public partial class FrameworkSkipConditionAttribute : System.Attribute, Microso { public FrameworkSkipConditionAttribute(Microsoft.AspNetCore.Testing.RuntimeFrameworks excludedFrameworks) { } public bool IsMet { get { throw null; } } - public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } } public static partial class HelixQueues { @@ -170,11 +170,18 @@ public partial interface ITestMethodLifecycle System.Threading.Tasks.Task OnTestStartAsync(Microsoft.AspNetCore.Testing.TestContext context, System.Threading.CancellationToken cancellationToken); } [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=true)] + public partial class MaximumOSVersionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition + { + public MaximumOSVersionAttribute(Microsoft.AspNetCore.Testing.OperatingSystems operatingSystem, string maxVersion) { } + public bool IsMet { get { throw null; } } + public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=true)] public partial class MinimumOSVersionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition { public MinimumOSVersionAttribute(Microsoft.AspNetCore.Testing.OperatingSystems operatingSystem, string minVersion) { } public bool IsMet { get { throw null; } } - public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } } [System.FlagsAttribute] public enum OperatingSystems @@ -186,31 +193,33 @@ public enum OperatingSystems [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=true)] public partial class OSSkipConditionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition { + public OSSkipConditionAttribute(Microsoft.AspNetCore.Testing.OperatingSystems operatingSystem) { } + [System.ObsoleteAttribute("Use the Minimum/MaximumOSVersionAttribute for version checks.", true)] public OSSkipConditionAttribute(Microsoft.AspNetCore.Testing.OperatingSystems operatingSystem, params string[] versions) { } public bool IsMet { get { throw null; } } - public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } } [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=false)] [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public partial class RepeatAttribute : System.Attribute { public RepeatAttribute(int runCount = 10) { } - public int RunCount { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public int RunCount { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } } public partial class RepeatContext { public RepeatContext(int limit) { } public static Microsoft.AspNetCore.Testing.RepeatContext Current { get { throw null; } } - public int CurrentIteration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } - public int Limit { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public int CurrentIteration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } + public int Limit { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } } [System.AttributeUsageAttribute(System.AttributeTargets.Method)] public partial class ReplaceCultureAttribute : Xunit.Sdk.BeforeAfterTestAttribute { public ReplaceCultureAttribute() { } public ReplaceCultureAttribute(string currentCulture, string currentUICulture) { } - public System.Globalization.CultureInfo Culture { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public System.Globalization.CultureInfo UICulture { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Globalization.CultureInfo Culture { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public System.Globalization.CultureInfo UICulture { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } public override void After(System.Reflection.MethodInfo methodUnderTest) { } public override void Before(System.Reflection.MethodInfo methodUnderTest) { } } @@ -232,7 +241,7 @@ public partial class SkipOnCIAttribute : System.Attribute, Microsoft.AspNetCore. { public SkipOnCIAttribute(string issueUrl = "") { } public bool IsMet { get { throw null; } } - public string IssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string IssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } public string SkipReason { get { throw null; } } public static string GetIfOnAzdo() { throw null; } public static string GetTargetHelixQueue() { throw null; } @@ -245,8 +254,8 @@ public partial class SkipOnHelixAttribute : System.Attribute, Microsoft.AspNetCo { public SkipOnHelixAttribute(string issueUrl) { } public bool IsMet { get { throw null; } } - public string IssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string Queues { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public string IssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public string Queues { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } public string SkipReason { get { throw null; } } public static string GetTargetHelixQueue() { throw null; } public static bool OnHelix() { throw null; } @@ -263,29 +272,30 @@ public override void Serialize(Xunit.Abstractions.IXunitSerializationInfo data) public static partial class TaskExtensions { [System.Diagnostics.DebuggerStepThroughAttribute] - public static System.Threading.Tasks.Task TimeoutAfter(this System.Threading.Tasks.Task task, System.TimeSpan timeout, [System.Runtime.CompilerServices.CallerFilePathAttribute]string filePath = null, [System.Runtime.CompilerServices.CallerLineNumberAttribute]int lineNumber = 0) { throw null; } + public static System.Threading.Tasks.Task TimeoutAfter(this System.Threading.Tasks.Task task, System.TimeSpan timeout, [System.Runtime.CompilerServices.CallerFilePathAttribute] string filePath = null, [System.Runtime.CompilerServices.CallerLineNumberAttribute] int lineNumber = 0) { throw null; } [System.Diagnostics.DebuggerStepThroughAttribute] - public static System.Threading.Tasks.Task TimeoutAfter(this System.Threading.Tasks.Task task, System.TimeSpan timeout, [System.Runtime.CompilerServices.CallerFilePathAttribute]string filePath = null, [System.Runtime.CompilerServices.CallerLineNumberAttribute]int lineNumber = 0) { throw null; } + public static System.Threading.Tasks.Task TimeoutAfter(this System.Threading.Tasks.Task task, System.TimeSpan timeout, [System.Runtime.CompilerServices.CallerFilePathAttribute] string filePath = null, [System.Runtime.CompilerServices.CallerLineNumberAttribute] int lineNumber = 0) { throw null; } } public sealed partial class TestContext { public TestContext(System.Type testClass, object[] constructorArguments, System.Reflection.MethodInfo testMethod, object[] methodArguments, Xunit.Abstractions.ITestOutputHelper output) { } - public object[] ConstructorArguments { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public object[] ConstructorArguments { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } public Microsoft.AspNetCore.Testing.TestFileOutputContext FileOutput { get { throw null; } } - public object[] MethodArguments { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public Xunit.Abstractions.ITestOutputHelper Output { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public System.Type TestClass { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public System.Reflection.MethodInfo TestMethod { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public object[] MethodArguments { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public Xunit.Abstractions.ITestOutputHelper Output { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public System.Type TestClass { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public System.Reflection.MethodInfo TestMethod { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } } public sealed partial class TestFileOutputContext { public TestFileOutputContext(Microsoft.AspNetCore.Testing.TestContext parent) { } - public string AssemblyOutputDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string TestClassName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string TestClassOutputDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string TestName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string AssemblyOutputDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public string TestClassName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public string TestClassOutputDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public string TestName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } public static string GetAssemblyBaseDirectory(System.Reflection.Assembly assembly, string baseDirectory = null) { throw null; } public static string GetOutputDirectory(System.Reflection.Assembly assembly) { throw null; } + public static bool GetPreserveExistingLogsInOutput(System.Reflection.Assembly assembly) { throw null; } public static string GetTestClassName(System.Type type) { throw null; } public static string GetTestMethodName(System.Reflection.MethodInfo method, object[] arguments) { throw null; } public string GetUniqueFileName(string prefix, string extension) { throw null; } @@ -298,11 +308,12 @@ public static partial class TestMethodExtensions [System.AttributeUsageAttribute(System.AttributeTargets.Assembly, AllowMultiple=false, Inherited=true)] public partial class TestOutputDirectoryAttribute : System.Attribute { - public TestOutputDirectoryAttribute(string targetFramework, string baseDirectory = null) { } - public string BaseDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string TargetFramework { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public TestOutputDirectoryAttribute(string preserveExistingLogsInOutput, string targetFramework, string baseDirectory = null) { } + public string BaseDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public bool PreserveExistingLogsInOutput { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public string TargetFramework { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } } - [System.ObsoleteAttribute("This API is obsolete and the pattern its usage encouraged should not be used anymore. See https://github.com/aspnet/Extensions/issues/1697 for details.")] + [System.ObsoleteAttribute("This API is obsolete and the pattern its usage encouraged should not be used anymore. See https://github.com/dotnet/extensions/issues/1697 for details.")] public partial class TestPathUtilities { public TestPathUtilities() { } @@ -319,9 +330,12 @@ public static partial class TestPlatformHelper public static partial class WindowsVersions { public const string Win10 = "10.0"; + public const string Win10_19H1 = "10.0.18362"; public const string Win10_19H2 = "10.0.18363"; - public const string Win10_20H1 = "10.0.18990"; + public const string Win10_20H1 = "10.0.19033"; public const string Win10_RS4 = "10.0.17134"; + public const string Win10_RS5 = "10.0.17763"; + [System.ObsoleteAttribute("Use Win7 instead.", true)] public const string Win2008R2 = "6.1"; public const string Win7 = "6.1"; public const string Win8 = "6.2"; diff --git a/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs b/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs index a7b9c07a9968..17a922a6c7b7 100644 --- a/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs +++ b/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs @@ -34,7 +34,7 @@ protected override void RunTestCases(System.Collections.Generic.IEnumerable Filters { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string GitHubIssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Collections.Generic.IReadOnlyList Filters { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public string GitHubIssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } } public static partial class FlakyOn { @@ -131,7 +131,7 @@ public partial class FrameworkSkipConditionAttribute : System.Attribute, Microso { public FrameworkSkipConditionAttribute(Microsoft.AspNetCore.Testing.RuntimeFrameworks excludedFrameworks) { } public bool IsMet { get { throw null; } } - public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } } public static partial class HelixQueues { @@ -170,11 +170,18 @@ public partial interface ITestMethodLifecycle System.Threading.Tasks.Task OnTestStartAsync(Microsoft.AspNetCore.Testing.TestContext context, System.Threading.CancellationToken cancellationToken); } [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=true)] + public partial class MaximumOSVersionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition + { + public MaximumOSVersionAttribute(Microsoft.AspNetCore.Testing.OperatingSystems operatingSystem, string maxVersion) { } + public bool IsMet { get { throw null; } } + public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=true)] public partial class MinimumOSVersionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition { public MinimumOSVersionAttribute(Microsoft.AspNetCore.Testing.OperatingSystems operatingSystem, string minVersion) { } public bool IsMet { get { throw null; } } - public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } } [System.FlagsAttribute] public enum OperatingSystems @@ -186,31 +193,33 @@ public enum OperatingSystems [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=true)] public partial class OSSkipConditionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition { + public OSSkipConditionAttribute(Microsoft.AspNetCore.Testing.OperatingSystems operatingSystem) { } + [System.ObsoleteAttribute("Use the Minimum/MaximumOSVersionAttribute for version checks.", true)] public OSSkipConditionAttribute(Microsoft.AspNetCore.Testing.OperatingSystems operatingSystem, params string[] versions) { } public bool IsMet { get { throw null; } } - public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } } [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=false)] [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public partial class RepeatAttribute : System.Attribute { public RepeatAttribute(int runCount = 10) { } - public int RunCount { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public int RunCount { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } } public partial class RepeatContext { public RepeatContext(int limit) { } public static Microsoft.AspNetCore.Testing.RepeatContext Current { get { throw null; } } - public int CurrentIteration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } - public int Limit { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public int CurrentIteration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } + public int Limit { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } } [System.AttributeUsageAttribute(System.AttributeTargets.Method)] public partial class ReplaceCultureAttribute : Xunit.Sdk.BeforeAfterTestAttribute { public ReplaceCultureAttribute() { } public ReplaceCultureAttribute(string currentCulture, string currentUICulture) { } - public System.Globalization.CultureInfo Culture { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public System.Globalization.CultureInfo UICulture { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Globalization.CultureInfo Culture { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public System.Globalization.CultureInfo UICulture { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } public override void After(System.Reflection.MethodInfo methodUnderTest) { } public override void Before(System.Reflection.MethodInfo methodUnderTest) { } } @@ -232,7 +241,7 @@ public partial class SkipOnCIAttribute : System.Attribute, Microsoft.AspNetCore. { public SkipOnCIAttribute(string issueUrl = "") { } public bool IsMet { get { throw null; } } - public string IssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string IssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } public string SkipReason { get { throw null; } } public static string GetIfOnAzdo() { throw null; } public static string GetTargetHelixQueue() { throw null; } @@ -245,8 +254,8 @@ public partial class SkipOnHelixAttribute : System.Attribute, Microsoft.AspNetCo { public SkipOnHelixAttribute(string issueUrl) { } public bool IsMet { get { throw null; } } - public string IssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string Queues { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public string IssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public string Queues { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } public string SkipReason { get { throw null; } } public static string GetTargetHelixQueue() { throw null; } public static bool OnHelix() { throw null; } @@ -263,29 +272,30 @@ public override void Serialize(Xunit.Abstractions.IXunitSerializationInfo data) public static partial class TaskExtensions { [System.Diagnostics.DebuggerStepThroughAttribute] - public static System.Threading.Tasks.Task TimeoutAfter(this System.Threading.Tasks.Task task, System.TimeSpan timeout, [System.Runtime.CompilerServices.CallerFilePathAttribute]string filePath = null, [System.Runtime.CompilerServices.CallerLineNumberAttribute]int lineNumber = 0) { throw null; } + public static System.Threading.Tasks.Task TimeoutAfter(this System.Threading.Tasks.Task task, System.TimeSpan timeout, [System.Runtime.CompilerServices.CallerFilePathAttribute] string filePath = null, [System.Runtime.CompilerServices.CallerLineNumberAttribute] int lineNumber = 0) { throw null; } [System.Diagnostics.DebuggerStepThroughAttribute] - public static System.Threading.Tasks.Task TimeoutAfter(this System.Threading.Tasks.Task task, System.TimeSpan timeout, [System.Runtime.CompilerServices.CallerFilePathAttribute]string filePath = null, [System.Runtime.CompilerServices.CallerLineNumberAttribute]int lineNumber = 0) { throw null; } + public static System.Threading.Tasks.Task TimeoutAfter(this System.Threading.Tasks.Task task, System.TimeSpan timeout, [System.Runtime.CompilerServices.CallerFilePathAttribute] string filePath = null, [System.Runtime.CompilerServices.CallerLineNumberAttribute] int lineNumber = 0) { throw null; } } public sealed partial class TestContext { public TestContext(System.Type testClass, object[] constructorArguments, System.Reflection.MethodInfo testMethod, object[] methodArguments, Xunit.Abstractions.ITestOutputHelper output) { } - public object[] ConstructorArguments { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public object[] ConstructorArguments { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } public Microsoft.AspNetCore.Testing.TestFileOutputContext FileOutput { get { throw null; } } - public object[] MethodArguments { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public Xunit.Abstractions.ITestOutputHelper Output { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public System.Type TestClass { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public System.Reflection.MethodInfo TestMethod { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public object[] MethodArguments { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public Xunit.Abstractions.ITestOutputHelper Output { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public System.Type TestClass { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public System.Reflection.MethodInfo TestMethod { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } } public sealed partial class TestFileOutputContext { public TestFileOutputContext(Microsoft.AspNetCore.Testing.TestContext parent) { } - public string AssemblyOutputDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string TestClassName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string TestClassOutputDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string TestName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string AssemblyOutputDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public string TestClassName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public string TestClassOutputDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public string TestName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } public static string GetAssemblyBaseDirectory(System.Reflection.Assembly assembly, string baseDirectory = null) { throw null; } public static string GetOutputDirectory(System.Reflection.Assembly assembly) { throw null; } + public static bool GetPreserveExistingLogsInOutput(System.Reflection.Assembly assembly) { throw null; } public static string GetTestClassName(System.Type type) { throw null; } public static string GetTestMethodName(System.Reflection.MethodInfo method, object[] arguments) { throw null; } public string GetUniqueFileName(string prefix, string extension) { throw null; } @@ -298,11 +308,12 @@ public static partial class TestMethodExtensions [System.AttributeUsageAttribute(System.AttributeTargets.Assembly, AllowMultiple=false, Inherited=true)] public partial class TestOutputDirectoryAttribute : System.Attribute { - public TestOutputDirectoryAttribute(string targetFramework, string baseDirectory = null) { } - public string BaseDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string TargetFramework { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public TestOutputDirectoryAttribute(string preserveExistingLogsInOutput, string targetFramework, string baseDirectory = null) { } + public string BaseDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public bool PreserveExistingLogsInOutput { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } + public string TargetFramework { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } } - [System.ObsoleteAttribute("This API is obsolete and the pattern its usage encouraged should not be used anymore. See https://github.com/aspnet/Extensions/issues/1697 for details.")] + [System.ObsoleteAttribute("This API is obsolete and the pattern its usage encouraged should not be used anymore. See https://github.com/dotnet/extensions/issues/1697 for details.")] public partial class TestPathUtilities { public TestPathUtilities() { } @@ -319,9 +330,12 @@ public static partial class TestPlatformHelper public static partial class WindowsVersions { public const string Win10 = "10.0"; + public const string Win10_19H1 = "10.0.18362"; public const string Win10_19H2 = "10.0.18363"; - public const string Win10_20H1 = "10.0.18990"; + public const string Win10_20H1 = "10.0.19033"; public const string Win10_RS4 = "10.0.17134"; + public const string Win10_RS5 = "10.0.17763"; + [System.ObsoleteAttribute("Use Win7 instead.", true)] public const string Win2008R2 = "6.1"; public const string Win7 = "6.1"; public const string Win8 = "6.2"; From acbadf5e7d0b4cd1ae6061ba2e06642f16a54e15 Mon Sep 17 00:00:00 2001 From: Nicholas P Nelson Date: Mon, 27 Jan 2020 12:09:29 -0500 Subject: [PATCH 176/183] fix typo from "retruned" to "returned" in HealthReport.Entries XML Comments (dotnet/extensions#2904) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/31b8e7cc01fcb48eca7526c7d8db793493dd58b4 --- src/HealthChecks/Abstractions/src/HealthReport.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HealthChecks/Abstractions/src/HealthReport.cs b/src/HealthChecks/Abstractions/src/HealthReport.cs index 1dacff45e3c2..6a796b0d8230 100644 --- a/src/HealthChecks/Abstractions/src/HealthReport.cs +++ b/src/HealthChecks/Abstractions/src/HealthReport.cs @@ -28,7 +28,7 @@ public HealthReport(IReadOnlyDictionary entries, Time /// /// /// The keys in this dictionary map the name of each executed health check to a for the - /// result data retruned from the corresponding health check. + /// result data returned from the corresponding health check. /// public IReadOnlyDictionary Entries { get; } From 668a867cce2652bad46a4c307c82cac85af98f18 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Tue, 4 Feb 2020 14:51:48 -0800 Subject: [PATCH 177/183] Remove Mono.WebAssembly.Interop \n\nCommit migrated from https://github.com/dotnet/extensions/commit/211046f01bf335241a4084b5cac2c08593835cb9 --- src/JSInterop/JSInterop.slnf | 3 +- .../Directory.Build.props | 9 - .../ref/Mono.WebAssembly.Interop.csproj | 10 -- ...Mono.WebAssembly.Interop.netstandard2.0.cs | 18 -- .../src/InternalCalls.cs | 25 --- .../src/Mono.WebAssembly.Interop.csproj | 18 -- .../src/MonoWebAssemblyJSRuntime.cs | 157 ------------------ 7 files changed, 1 insertion(+), 239 deletions(-) delete mode 100644 src/JSInterop/Mono.WebAssembly.Interop/Directory.Build.props delete mode 100644 src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.csproj delete mode 100644 src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.netstandard2.0.cs delete mode 100644 src/JSInterop/Mono.WebAssembly.Interop/src/InternalCalls.cs delete mode 100644 src/JSInterop/Mono.WebAssembly.Interop/src/Mono.WebAssembly.Interop.csproj delete mode 100644 src/JSInterop/Mono.WebAssembly.Interop/src/MonoWebAssemblyJSRuntime.cs diff --git a/src/JSInterop/JSInterop.slnf b/src/JSInterop/JSInterop.slnf index c6cbd4cb3af2..faae0051a523 100644 --- a/src/JSInterop/JSInterop.slnf +++ b/src/JSInterop/JSInterop.slnf @@ -3,8 +3,7 @@ "path": "..\\..\\Extensions.sln", "projects": [ "src\\JSInterop\\Microsoft.JSInterop\\src\\Microsoft.JSInterop.csproj", - "src\\JSInterop\\Microsoft.JSInterop\\test\\Microsoft.JSInterop.Tests.csproj", - "src\\JSInterop\\Mono.WebAssembly.Interop\\src\\Mono.WebAssembly.Interop.csproj", + "src\\JSInterop\\Microsoft.JSInterop\\test\\Microsoft.JSInterop.Tests.csproj" ] } } diff --git a/src/JSInterop/Mono.WebAssembly.Interop/Directory.Build.props b/src/JSInterop/Mono.WebAssembly.Interop/Directory.Build.props deleted file mode 100644 index ce32dcd999e5..000000000000 --- a/src/JSInterop/Mono.WebAssembly.Interop/Directory.Build.props +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - $(BlazorWasmPreReleaseVersionLabel) - - - diff --git a/src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.csproj b/src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.csproj deleted file mode 100644 index a1ba60570586..000000000000 --- a/src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.csproj +++ /dev/null @@ -1,10 +0,0 @@ - - - - netstandard2.0 - - - - - - diff --git a/src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.netstandard2.0.cs b/src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.netstandard2.0.cs deleted file mode 100644 index 8dd70b946a6e..000000000000 --- a/src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.netstandard2.0.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -namespace Mono.WebAssembly.Interop -{ - public partial class MonoWebAssemblyJSRuntime : Microsoft.JSInterop.JSInProcessRuntime - { - public MonoWebAssemblyJSRuntime() { } - protected override void BeginInvokeJS(long asyncHandle, string identifier, string argsJson) { } - protected override void EndInvokeDotNet(Microsoft.JSInterop.Infrastructure.DotNetInvocationInfo callInfo, in Microsoft.JSInterop.Infrastructure.DotNetInvocationResult dispatchResult) { } - protected static void Initialize(Mono.WebAssembly.Interop.MonoWebAssemblyJSRuntime jsRuntime) { } - protected override string InvokeJS(string identifier, string argsJson) { throw null; } - public TRes InvokeUnmarshalled(string identifier) { throw null; } - public TRes InvokeUnmarshalled(string identifier, T0 arg0) { throw null; } - public TRes InvokeUnmarshalled(string identifier, T0 arg0, T1 arg1) { throw null; } - public TRes InvokeUnmarshalled(string identifier, T0 arg0, T1 arg1, T2 arg2) { throw null; } - } -} diff --git a/src/JSInterop/Mono.WebAssembly.Interop/src/InternalCalls.cs b/src/JSInterop/Mono.WebAssembly.Interop/src/InternalCalls.cs deleted file mode 100644 index 60c0cdc42913..000000000000 --- a/src/JSInterop/Mono.WebAssembly.Interop/src/InternalCalls.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Runtime.CompilerServices; - -namespace WebAssembly.JSInterop -{ - /// - /// Methods that map to the functions compiled into the Mono WebAssembly runtime, - /// as defined by 'mono_add_internal_call' calls in driver.c - /// - internal class InternalCalls - { - // The exact namespace, type, and method names must match the corresponding entries - // in driver.c in the Mono distribution - - // We're passing asyncHandle by ref not because we want it to be writable, but so it gets - // passed as a pointer (4 bytes). We can pass 4-byte values, but not 8-byte ones. - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern string InvokeJSMarshalled(out string exception, ref long asyncHandle, string functionIdentifier, string argsJson); - - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern TRes InvokeJSUnmarshalled(out string exception, string functionIdentifier, T0 arg0, T1 arg1, T2 arg2); - } -} diff --git a/src/JSInterop/Mono.WebAssembly.Interop/src/Mono.WebAssembly.Interop.csproj b/src/JSInterop/Mono.WebAssembly.Interop/src/Mono.WebAssembly.Interop.csproj deleted file mode 100644 index f85c89a6d363..000000000000 --- a/src/JSInterop/Mono.WebAssembly.Interop/src/Mono.WebAssembly.Interop.csproj +++ /dev/null @@ -1,18 +0,0 @@ - - - - netstandard2.0 - Abstractions and features for interop between Mono WebAssembly and JavaScript code. - wasm;javascript;interop - true - true - true - - true - - - - - - - diff --git a/src/JSInterop/Mono.WebAssembly.Interop/src/MonoWebAssemblyJSRuntime.cs b/src/JSInterop/Mono.WebAssembly.Interop/src/MonoWebAssemblyJSRuntime.cs deleted file mode 100644 index 654263a12331..000000000000 --- a/src/JSInterop/Mono.WebAssembly.Interop/src/MonoWebAssemblyJSRuntime.cs +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Text.Json; -using Microsoft.JSInterop; -using Microsoft.JSInterop.Infrastructure; -using WebAssembly.JSInterop; - -namespace Mono.WebAssembly.Interop -{ - /// - /// Provides methods for invoking JavaScript functions for applications running - /// on the Mono WebAssembly runtime. - /// - public class MonoWebAssemblyJSRuntime : JSInProcessRuntime - { - /// - /// Gets the used to perform operations using . - /// - private static MonoWebAssemblyJSRuntime Instance { get; set; } - - /// - /// Initializes the to be used to perform operations using . - /// - /// The instance. - protected static void Initialize(MonoWebAssemblyJSRuntime jsRuntime) - { - if (Instance != null) - { - throw new InvalidOperationException("MonoWebAssemblyJSRuntime has already been initialized."); - } - - Instance = jsRuntime ?? throw new ArgumentNullException(nameof(jsRuntime)); - } - - /// - protected override string InvokeJS(string identifier, string argsJson) - { - var noAsyncHandle = default(long); - var result = InternalCalls.InvokeJSMarshalled(out var exception, ref noAsyncHandle, identifier, argsJson); - return exception != null - ? throw new JSException(exception) - : result; - } - - /// - protected override void BeginInvokeJS(long asyncHandle, string identifier, string argsJson) - { - InternalCalls.InvokeJSMarshalled(out _, ref asyncHandle, identifier, argsJson); - } - - // Invoked via Mono's JS interop mechanism (invoke_method) - private static string InvokeDotNet(string assemblyName, string methodIdentifier, string dotNetObjectId, string argsJson) - { - var callInfo = new DotNetInvocationInfo(assemblyName, methodIdentifier, dotNetObjectId == null ? default : long.Parse(dotNetObjectId), callId: null); - return DotNetDispatcher.Invoke(Instance, callInfo, argsJson); - } - - // Invoked via Mono's JS interop mechanism (invoke_method) - private static void EndInvokeJS(string argsJson) - => DotNetDispatcher.EndInvokeJS(Instance, argsJson); - - // Invoked via Mono's JS interop mechanism (invoke_method) - private static void BeginInvokeDotNet(string callId, string assemblyNameOrDotNetObjectId, string methodIdentifier, string argsJson) - { - // Figure out whether 'assemblyNameOrDotNetObjectId' is the assembly name or the instance ID - // We only need one for any given call. This helps to work around the limitation that we can - // only pass a maximum of 4 args in a call from JS to Mono WebAssembly. - string assemblyName; - long dotNetObjectId; - if (char.IsDigit(assemblyNameOrDotNetObjectId[0])) - { - dotNetObjectId = long.Parse(assemblyNameOrDotNetObjectId); - assemblyName = null; - } - else - { - dotNetObjectId = default; - assemblyName = assemblyNameOrDotNetObjectId; - } - - var callInfo = new DotNetInvocationInfo(assemblyName, methodIdentifier, dotNetObjectId, callId); - DotNetDispatcher.BeginInvokeDotNet(Instance, callInfo, argsJson); - } - - protected override void EndInvokeDotNet(DotNetInvocationInfo callInfo, in DotNetInvocationResult dispatchResult) - { - // For failures, the common case is to call EndInvokeDotNet with the Exception object. - // For these we'll serialize as something that's useful to receive on the JS side. - // If the value is not an Exception, we'll just rely on it being directly JSON-serializable. - var resultOrError = dispatchResult.Success ? dispatchResult.Result : dispatchResult.Exception.ToString(); - - // We pass 0 as the async handle because we don't want the JS-side code to - // send back any notification (we're just providing a result for an existing async call) - var args = JsonSerializer.Serialize(new[] { callInfo.CallId, dispatchResult.Success, resultOrError }, JsonSerializerOptions); - BeginInvokeJS(0, "DotNet.jsCallDispatcher.endInvokeDotNetFromJS", args); - } - - #region Custom MonoWebAssemblyJSRuntime methods - - /// - /// Invokes the JavaScript function registered with the specified identifier. - /// - /// The .NET type corresponding to the function's return value type. - /// The identifier used when registering the target function. - /// The result of the function invocation. - public TRes InvokeUnmarshalled(string identifier) - => InvokeUnmarshalled(identifier, null, null, null); - - /// - /// Invokes the JavaScript function registered with the specified identifier. - /// - /// The type of the first argument. - /// The .NET type corresponding to the function's return value type. - /// The identifier used when registering the target function. - /// The first argument. - /// The result of the function invocation. - public TRes InvokeUnmarshalled(string identifier, T0 arg0) - => InvokeUnmarshalled(identifier, arg0, null, null); - - /// - /// Invokes the JavaScript function registered with the specified identifier. - /// - /// The type of the first argument. - /// The type of the second argument. - /// The .NET type corresponding to the function's return value type. - /// The identifier used when registering the target function. - /// The first argument. - /// The second argument. - /// The result of the function invocation. - public TRes InvokeUnmarshalled(string identifier, T0 arg0, T1 arg1) - => InvokeUnmarshalled(identifier, arg0, arg1, null); - - /// - /// Invokes the JavaScript function registered with the specified identifier. - /// - /// The type of the first argument. - /// The type of the second argument. - /// The type of the third argument. - /// The .NET type corresponding to the function's return value type. - /// The identifier used when registering the target function. - /// The first argument. - /// The second argument. - /// The third argument. - /// The result of the function invocation. - public TRes InvokeUnmarshalled(string identifier, T0 arg0, T1 arg1, T2 arg2) - { - var result = InternalCalls.InvokeJSUnmarshalled(out var exception, identifier, arg0, arg1, arg2); - return exception != null - ? throw new JSException(exception) - : result; - } - - #endregion - } -} From 4aab03bf9ad64519a8369db029c98abfb99c9549 Mon Sep 17 00:00:00 2001 From: Kahbazi Date: Tue, 11 Feb 2020 00:39:13 +0330 Subject: [PATCH 178/183] Add ReloadOnChange to KeyPerFile configuration provider (dotnet/extensions#2808) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/cca1c7ca95348bf2268aec00e8160c21459a457c --- ...ons.Configuration.KeyPerFile.netcoreapp.cs | 7 +- ...Configuration.KeyPerFile.netstandard2.0.cs | 7 +- ...eyPerFileConfigurationBuilderExtensions.cs | 23 ++++ .../src/KeyPerFileConfigurationProvider.cs | 62 +++++++-- .../src/KeyPerFileConfigurationSource.cs | 11 ++ .../test/KeyPerFileTests.cs | 118 +++++++++++++++++- 6 files changed, 210 insertions(+), 18 deletions(-) diff --git a/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.netcoreapp.cs b/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.netcoreapp.cs index 37cefae6cb66..d6e800587ad9 100644 --- a/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.netcoreapp.cs +++ b/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.netcoreapp.cs @@ -6,14 +6,17 @@ namespace Microsoft.Extensions.Configuration public static partial class KeyPerFileConfigurationBuilderExtensions { public static Microsoft.Extensions.Configuration.IConfigurationBuilder AddKeyPerFile(this Microsoft.Extensions.Configuration.IConfigurationBuilder builder, System.Action configureSource) { throw null; } + public static Microsoft.Extensions.Configuration.IConfigurationBuilder AddKeyPerFile(this Microsoft.Extensions.Configuration.IConfigurationBuilder builder, string directoryPath) { throw null; } public static Microsoft.Extensions.Configuration.IConfigurationBuilder AddKeyPerFile(this Microsoft.Extensions.Configuration.IConfigurationBuilder builder, string directoryPath, bool optional) { throw null; } + public static Microsoft.Extensions.Configuration.IConfigurationBuilder AddKeyPerFile(this Microsoft.Extensions.Configuration.IConfigurationBuilder builder, string directoryPath, bool optional, bool reloadOnChange) { throw null; } } } namespace Microsoft.Extensions.Configuration.KeyPerFile { - public partial class KeyPerFileConfigurationProvider : Microsoft.Extensions.Configuration.ConfigurationProvider + public partial class KeyPerFileConfigurationProvider : Microsoft.Extensions.Configuration.ConfigurationProvider, System.IDisposable { public KeyPerFileConfigurationProvider(Microsoft.Extensions.Configuration.KeyPerFile.KeyPerFileConfigurationSource source) { } + public void Dispose() { } public override void Load() { } public override string ToString() { throw null; } } @@ -24,6 +27,8 @@ public KeyPerFileConfigurationSource() { } public System.Func IgnoreCondition { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } public string IgnorePrefix { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } public bool Optional { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } + public int ReloadDelay { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } + public bool ReloadOnChange { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } public Microsoft.Extensions.Configuration.IConfigurationProvider Build(Microsoft.Extensions.Configuration.IConfigurationBuilder builder) { throw null; } } } diff --git a/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.netstandard2.0.cs b/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.netstandard2.0.cs index 37cefae6cb66..d6e800587ad9 100644 --- a/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.netstandard2.0.cs +++ b/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.netstandard2.0.cs @@ -6,14 +6,17 @@ namespace Microsoft.Extensions.Configuration public static partial class KeyPerFileConfigurationBuilderExtensions { public static Microsoft.Extensions.Configuration.IConfigurationBuilder AddKeyPerFile(this Microsoft.Extensions.Configuration.IConfigurationBuilder builder, System.Action configureSource) { throw null; } + public static Microsoft.Extensions.Configuration.IConfigurationBuilder AddKeyPerFile(this Microsoft.Extensions.Configuration.IConfigurationBuilder builder, string directoryPath) { throw null; } public static Microsoft.Extensions.Configuration.IConfigurationBuilder AddKeyPerFile(this Microsoft.Extensions.Configuration.IConfigurationBuilder builder, string directoryPath, bool optional) { throw null; } + public static Microsoft.Extensions.Configuration.IConfigurationBuilder AddKeyPerFile(this Microsoft.Extensions.Configuration.IConfigurationBuilder builder, string directoryPath, bool optional, bool reloadOnChange) { throw null; } } } namespace Microsoft.Extensions.Configuration.KeyPerFile { - public partial class KeyPerFileConfigurationProvider : Microsoft.Extensions.Configuration.ConfigurationProvider + public partial class KeyPerFileConfigurationProvider : Microsoft.Extensions.Configuration.ConfigurationProvider, System.IDisposable { public KeyPerFileConfigurationProvider(Microsoft.Extensions.Configuration.KeyPerFile.KeyPerFileConfigurationSource source) { } + public void Dispose() { } public override void Load() { } public override string ToString() { throw null; } } @@ -24,6 +27,8 @@ public KeyPerFileConfigurationSource() { } public System.Func IgnoreCondition { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } public string IgnorePrefix { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } public bool Optional { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } + public int ReloadDelay { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } + public bool ReloadOnChange { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } public Microsoft.Extensions.Configuration.IConfigurationProvider Build(Microsoft.Extensions.Configuration.IConfigurationBuilder builder) { throw null; } } } diff --git a/src/Configuration.KeyPerFile/src/KeyPerFileConfigurationBuilderExtensions.cs b/src/Configuration.KeyPerFile/src/KeyPerFileConfigurationBuilderExtensions.cs index 435ef9e1554d..e4c8dd58eea5 100644 --- a/src/Configuration.KeyPerFile/src/KeyPerFileConfigurationBuilderExtensions.cs +++ b/src/Configuration.KeyPerFile/src/KeyPerFileConfigurationBuilderExtensions.cs @@ -10,6 +10,16 @@ namespace Microsoft.Extensions.Configuration /// public static class KeyPerFileConfigurationBuilderExtensions { + /// + /// Adds configuration using files from a directory. File names are used as the key, + /// file contents are used as the value. + /// + /// The to add to. + /// The path to the directory. + /// The . + public static IConfigurationBuilder AddKeyPerFile(this IConfigurationBuilder builder, string directoryPath) + => builder.AddKeyPerFile(directoryPath, optional: false, reloadOnChange: false); + /// /// Adds configuration using files from a directory. File names are used as the key, /// file contents are used as the value. @@ -19,6 +29,18 @@ public static class KeyPerFileConfigurationBuilderExtensions /// Whether the directory is optional. /// The . public static IConfigurationBuilder AddKeyPerFile(this IConfigurationBuilder builder, string directoryPath, bool optional) + => builder.AddKeyPerFile(directoryPath, optional, reloadOnChange: false); + + /// + /// Adds configuration using files from a directory. File names are used as the key, + /// file contents are used as the value. + /// + /// The to add to. + /// The path to the directory. + /// Whether the directory is optional. + /// Whether the configuration should be reloaded if the files are changed, added or removed. + /// The . + public static IConfigurationBuilder AddKeyPerFile(this IConfigurationBuilder builder, string directoryPath, bool optional, bool reloadOnChange) => builder.AddKeyPerFile(source => { // Only try to set the file provider if its not optional or the directory exists @@ -27,6 +49,7 @@ public static IConfigurationBuilder AddKeyPerFile(this IConfigurationBuilder bui source.FileProvider = new PhysicalFileProvider(directoryPath); } source.Optional = optional; + source.ReloadOnChange = reloadOnChange; }); /// diff --git a/src/Configuration.KeyPerFile/src/KeyPerFileConfigurationProvider.cs b/src/Configuration.KeyPerFile/src/KeyPerFileConfigurationProvider.cs index 2e33b9dfcd1a..f586896fc275 100644 --- a/src/Configuration.KeyPerFile/src/KeyPerFileConfigurationProvider.cs +++ b/src/Configuration.KeyPerFile/src/KeyPerFileConfigurationProvider.cs @@ -1,14 +1,18 @@ using System; using System.Collections.Generic; using System.IO; +using System.Threading; +using Microsoft.Extensions.Primitives; namespace Microsoft.Extensions.Configuration.KeyPerFile { /// /// A that uses a directory's files as configuration key/values. /// - public class KeyPerFileConfigurationProvider : ConfigurationProvider + public class KeyPerFileConfigurationProvider : ConfigurationProvider, IDisposable { + private readonly IDisposable _changeTokenRegistration; + KeyPerFileConfigurationSource Source { get; set; } /// @@ -16,7 +20,21 @@ public class KeyPerFileConfigurationProvider : ConfigurationProvider /// /// The settings. public KeyPerFileConfigurationProvider(KeyPerFileConfigurationSource source) - => Source = source ?? throw new ArgumentNullException(nameof(source)); + { + Source = source ?? throw new ArgumentNullException(nameof(source)); + + if (Source.ReloadOnChange && Source.FileProvider != null) + { + _changeTokenRegistration = ChangeToken.OnChange( + () => Source.FileProvider.Watch("*"), + () => + { + Thread.Sleep(Source.ReloadDelay); + Load(reload: true); + }); + } + + } private static string NormalizeKey(string key) => key.Replace("__", ConfigurationPath.KeyDelimiter); @@ -27,15 +45,20 @@ private static string TrimNewLine(string value) : value; /// - /// Loads the docker secrets. + /// Loads the configuration values. /// public override void Load() + { + Load(reload: false); + } + + private void Load(bool reload) { var data = new Dictionary(StringComparer.OrdinalIgnoreCase); if (Source.FileProvider == null) { - if (Source.Optional) + if (Source.Optional || reload) // Always optional on reload { Data = data; return; @@ -45,25 +68,32 @@ public override void Load() } var directory = Source.FileProvider.GetDirectoryContents("/"); - if (!directory.Exists && !Source.Optional) + if (!directory.Exists) { + if (Source.Optional || reload) // Always optional on reload + { + Data = data; + return; + } throw new DirectoryNotFoundException("The root directory for the FileProvider doesn't exist and is not optional."); } - - foreach (var file in directory) + else { - if (file.IsDirectory) + foreach (var file in directory) { - continue; - } + if (file.IsDirectory) + { + continue; + } + + using var stream = file.CreateReadStream(); + using var streamReader = new StreamReader(stream); - using (var stream = file.CreateReadStream()) - using (var streamReader = new StreamReader(stream)) - { if (Source.IgnoreCondition == null || !Source.IgnoreCondition(file.Name)) { data.Add(NormalizeKey(file.Name), TrimNewLine(streamReader.ReadToEnd())); } + } } @@ -79,5 +109,11 @@ private string GetDirectoryName() /// The configuration name. public override string ToString() => $"{GetType().Name} for files in '{GetDirectoryName()}' ({(Source.Optional ? "Optional" : "Required")})"; + + /// + public void Dispose() + { + _changeTokenRegistration?.Dispose(); + } } } diff --git a/src/Configuration.KeyPerFile/src/KeyPerFileConfigurationSource.cs b/src/Configuration.KeyPerFile/src/KeyPerFileConfigurationSource.cs index c32e948e828f..2b64d5a8dda7 100644 --- a/src/Configuration.KeyPerFile/src/KeyPerFileConfigurationSource.cs +++ b/src/Configuration.KeyPerFile/src/KeyPerFileConfigurationSource.cs @@ -37,6 +37,17 @@ public KeyPerFileConfigurationSource() /// public bool Optional { get; set; } + /// + /// Determines whether the source will be loaded if the underlying file changes. + /// + public bool ReloadOnChange { get; set; } + + /// + /// Number of milliseconds that reload will wait before calling Load. This helps + /// avoid triggering reload before a file is completely written. Default is 250. + /// + public int ReloadDelay { get; set; } = 250; + /// /// Builds the for this source. /// diff --git a/src/Configuration.KeyPerFile/test/KeyPerFileTests.cs b/src/Configuration.KeyPerFile/test/KeyPerFileTests.cs index 838e62222d6b..794f7abf23fc 100644 --- a/src/Configuration.KeyPerFile/test/KeyPerFileTests.cs +++ b/src/Configuration.KeyPerFile/test/KeyPerFileTests.cs @@ -217,6 +217,79 @@ void ReloadLoop() Assert.Equal("Foo", options.Text); } + [Fact] + public void ReloadConfigWhenReloadOnChangeIsTrue() + { + var testFileProvider = new TestFileProvider( + new TestFile("Secret1", "SecretValue1"), + new TestFile("Secret2", "SecretValue2")); + + var config = new ConfigurationBuilder() + .AddKeyPerFile(o => + { + o.FileProvider = testFileProvider; + o.ReloadOnChange = true; + }).Build(); + + Assert.Equal("SecretValue1", config["Secret1"]); + Assert.Equal("SecretValue2", config["Secret2"]); + + testFileProvider.ChangeFiles( + new TestFile("Secret1", "NewSecretValue1"), + new TestFile("Secret3", "NewSecretValue3")); + + Assert.Equal("NewSecretValue1", config["Secret1"]); + Assert.Null(config["NewSecret2"]); + Assert.Equal("NewSecretValue3", config["Secret3"]); + } + + [Fact] + public void SameConfigWhenReloadOnChangeIsFalse() + { + var testFileProvider = new TestFileProvider( + new TestFile("Secret1", "SecretValue1"), + new TestFile("Secret2", "SecretValue2")); + + var config = new ConfigurationBuilder() + .AddKeyPerFile(o => + { + o.FileProvider = testFileProvider; + o.ReloadOnChange = false; + }).Build(); + + Assert.Equal("SecretValue1", config["Secret1"]); + Assert.Equal("SecretValue2", config["Secret2"]); + + testFileProvider.ChangeFiles( + new TestFile("Secret1", "NewSecretValue1"), + new TestFile("Secret3", "NewSecretValue3")); + + Assert.Equal("SecretValue1", config["Secret1"]); + Assert.Equal("SecretValue2", config["Secret2"]); + } + + [Fact] + public void NoFilesReloadWhenAddedFiles() + { + var testFileProvider = new TestFileProvider(); + + var config = new ConfigurationBuilder() + .AddKeyPerFile(o => + { + o.FileProvider = testFileProvider; + o.ReloadOnChange = true; + }).Build(); + + Assert.Empty(config.AsEnumerable()); + + testFileProvider.ChangeFiles( + new TestFile("Secret1", "SecretValue1"), + new TestFile("Secret2", "SecretValue2")); + + Assert.Equal("SecretValue1", config["Secret1"]); + Assert.Equal("SecretValue2", config["Secret2"]); + } + private sealed class MyOptions { public int Number { get; set; } @@ -227,17 +300,56 @@ private sealed class MyOptions class TestFileProvider : IFileProvider { IDirectoryContents _contents; - + MockChangeToken _changeToken; + public TestFileProvider(params IFileInfo[] files) { _contents = new TestDirectoryContents(files); + _changeToken = new MockChangeToken(); } public IDirectoryContents GetDirectoryContents(string subpath) => _contents; public IFileInfo GetFileInfo(string subpath) => new TestFile("TestDirectory"); - public IChangeToken Watch(string filter) => throw new NotImplementedException(); + public IChangeToken Watch(string filter) => _changeToken; + + internal void ChangeFiles(params IFileInfo[] files) + { + _contents = new TestDirectoryContents(files); + _changeToken.RaiseCallback(); + } + } + + class MockChangeToken : IChangeToken + { + private Action _callback; + + public bool ActiveChangeCallbacks => true; + + public bool HasChanged => true; + + public IDisposable RegisterChangeCallback(Action callback, object state) + { + var disposable = new MockDisposable(); + _callback = () => callback(state); + return disposable; + } + + internal void RaiseCallback() + { + _callback?.Invoke(); + } + } + + class MockDisposable : IDisposable + { + public bool Disposed { get; set; } + + public void Dispose() + { + Disposed = true; + } } class TestDirectoryContents : IDirectoryContents @@ -291,7 +403,7 @@ public TestFile(string name, string contents) public Stream CreateReadStream() { - if(IsDirectory) + if (IsDirectory) { throw new InvalidOperationException("Cannot create stream from directory"); } From 7d1f5d8c0b290043180d345f688049a17c23eddf Mon Sep 17 00:00:00 2001 From: John Luo Date: Sat, 15 Feb 2020 02:13:19 -0800 Subject: [PATCH 179/183] Updates for extensions migrations - Multi-target ObjectPool - Move Embedded.Manifest.Task.Internal.Entry to the public namespace - Remove ref assemblies from AspNetCore.Testing - Skip TestPathUtilitiesTest since it's a pattern we want to migrate away from - Fix FileProviders.Abstractions version in Embedded.*.nuspec - Add workarounds for project references to FileProviders.Embedded --- eng/Build.props | 9 + eng/Dependencies.props | 21 +- eng/ProjectReferences.props | 10 + eng/SharedFramework.External.props | 9 - eng/SharedFramework.Local.props | 9 + eng/Version.Details.xml | 72 ---- eng/Versions.props | 20 +- eng/targets/ResolveReferences.targets | 2 +- ...osoft.AspNetCore.Blazor.Build.Tests.csproj | 2 +- ...crosoft.AspNetCore.Blazor.DevServer.csproj | 3 +- ...t.AspNetCore.Components.Performance.csproj | 3 +- .../Microsoft.AspNetCore.Components.csproj | 2 - .../Microsoft.AspNetCore.Components.csproj | 10 +- .../Server/src/Directory.Build.targets | 6 + ...rosoft.AspNetCore.Components.Server.csproj | 15 +- .../Directory.Build.props | 9 - ...Extensions.Configuration.KeyPerFile.csproj | 4 + .../test/ConfigurationProviderTestBase.cs | 4 +- ...ions.Configuration.KeyPerFile.Tests.csproj | 4 - src/FileProviders/Directory.Build.props | 8 - ...t.Extensions.FileProviders.Embedded.csproj | 5 + ....FileProviders.Embedded.multitarget.nuspec | 6 +- ...s.FileProviders.Embedded.netcoreapp.nuspec | 4 +- .../Manifest.MSBuildTask/src/Entry.cs | 2 +- .../Manifest.MSBuildTask/src/Manifest.cs | 1 - ...ileProviders.Embedded.Manifest.Task.csproj | 1 + .../GenerateEmbeddedResourcesManifestTest.cs | 1 - .../test/SetExtensions.cs | 2 - ...agnostics.HealthChecks.Abstractions.csproj | 1 + ...Extensions.Diagnostics.HealthChecks.csproj | 5 +- ...ions.Diagnostics.HealthChecks.Tests.csproj | 4 +- .../ref/Microsoft.AspNetCore.Hosting.csproj | 1 - .../src/Microsoft.AspNetCore.Hosting.csproj | 3 +- .../ref/Microsoft.AspNetCore.TestHost.csproj | 1 - .../src/Microsoft.AspNetCore.TestHost.csproj | 3 +- ...rosoft.AspNetCore.Http.Abstractions.csproj | 1 - ...rosoft.AspNetCore.Http.Abstractions.csproj | 4 +- ...crosoft.AspNetCore.Http.Performance.csproj | 3 +- ...soft.AspNetCore.Routing.Performance.csproj | 3 +- .../ref/Microsoft.AspNetCore.Routing.csproj | 1 - .../src/Microsoft.AspNetCore.Routing.csproj | 3 +- .../tools/Swaggatherer/Swaggatherer.csproj | 3 +- ...AspNetCore.WebUtilities.Performance.csproj | 2 +- ...oft.AspNetCore.Http.Microbenchmarks.csproj | 3 +- .../src/Microsoft.JSInterop.csproj | 1 + ...xtensions.Localization.Abstractions.csproj | 1 + .../Microsoft.Extensions.Localization.csproj | 1 + .../src/ResourceManagerStringLocalizer.cs | 4 + ...osoft.Extensions.Localization.Tests.csproj | 1 - ....ConcurrencyLimiter.Microbenchmarks.csproj | 3 +- ...osoft.AspNetCore.ConcurrencyLimiter.csproj | 1 - ...osoft.AspNetCore.ConcurrencyLimiter.csproj | 3 +- .../Microsoft.AspNetCore.Diagnostics.csproj | 1 - .../Microsoft.AspNetCore.Diagnostics.csproj | 2 +- ...ore.ResponseCompression.Performance.csproj | 3 +- ...pNetCore.WebSockets.Microbenchmarks.csproj | 3 +- ...ore.ResponseCaching.Microbenchmarks.csproj | 3 +- .../MusicStore.E2ETests.csproj | 3 +- ...crosoft.AspNetCore.Mvc.Abstractions.csproj | 1 - ...crosoft.AspNetCore.Mvc.Abstractions.csproj | 4 +- .../ref/Microsoft.AspNetCore.Mvc.Core.csproj | 3 - .../src/Microsoft.AspNetCore.Mvc.Core.csproj | 6 +- .../Microsoft.AspNetCore.Mvc.Testing.csproj | 1 - .../Microsoft.AspNetCore.Mvc.Testing.csproj | 3 +- ...icrosoft.AspNetCore.Mvc.Performance.csproj | 3 +- .../Microsoft.Extensions.ObjectPool.csproj | 6 +- ...rosoft.Extensions.ObjectPool.netcoreapp.cs | 76 ++++ .../Microsoft.Extensions.ObjectPool.csproj | 3 +- .../test/ProjectTemplates.Tests.csproj | 3 +- .../ref/Microsoft.AspNetCore.Razor.csproj | 1 - .../src/Microsoft.AspNetCore.Razor.csproj | 3 +- ...oft.AspNetCore.Security.Performance.csproj | 3 +- ...AspNetCore.Connections.Abstractions.csproj | 3 - ...AspNetCore.Connections.Abstractions.csproj | 4 +- .../IIS.Performance/IIS.Performance.csproj | 3 +- .../Microsoft.AspNetCore.Server.IIS.csproj | 1 - .../Microsoft.AspNetCore.Server.IIS.csproj | 2 +- ...pNetCore.Server.Kestrel.Performance.csproj | 3 +- .../InMemory.FunctionalTests.csproj | 2 +- .../Microsoft.AspNetCore.Shared.Tests.csproj | 3 +- ...crosoft.AspNetCore.Http.Connections.csproj | 1 - ...crosoft.AspNetCore.Http.Connections.csproj | 3 +- ...soft.AspNetCore.SignalR.Tests.Utils.csproj | 3 +- ....AspNetCore.SignalR.Microbenchmarks.csproj | 6 +- .../benchmarkapps/Crankier/Crankier.csproj | 3 +- .../samples/ClientSample/ClientSample.csproj | 2 +- .../ref/Microsoft.AspNetCore.Testing.csproj | 32 -- .../ref/Microsoft.AspNetCore.Testing.net46.cs | 374 ------------------ ...osoft.AspNetCore.Testing.netstandard2.0.cs | 374 ------------------ .../src/Microsoft.AspNetCore.Testing.csproj | 1 - src/Testing/test/TestPathUtilitiesTest.cs | 2 +- .../src/GetDocumentInsider.csproj | 5 +- .../src/Microsoft.dotnet-openapi.csproj | 2 +- .../src/dotnet-dev-certs.csproj | 2 +- .../src/dotnet-getdocument.csproj | 2 +- .../src/dotnet-sql-cache.csproj | 2 +- .../src/dotnet-user-secrets.csproj | 2 +- .../dotnet-watch/src/dotnet-watch.csproj | 5 +- .../Microsoft.Extensions.WebEncoders.csproj | 1 + 99 files changed, 255 insertions(+), 1019 deletions(-) create mode 100644 src/Components/Server/src/Directory.Build.targets delete mode 100644 src/Configuration.KeyPerFile/Directory.Build.props delete mode 100644 src/FileProviders/Directory.Build.props create mode 100644 src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.netcoreapp.cs delete mode 100644 src/Testing/ref/Microsoft.AspNetCore.Testing.csproj delete mode 100644 src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs delete mode 100644 src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs diff --git a/eng/Build.props b/eng/Build.props index 876c87b6f4f8..e40aa38bd622 100644 --- a/eng/Build.props +++ b/eng/Build.props @@ -109,6 +109,7 @@ $(RepoRoot)src\Components\Web.JS\Microsoft.AspNetCore.Components.Web.JS.npmproj; $(RepoRoot)src\SignalR\**\*.npmproj; $(RepoRoot)src\Middleware\**\*.npmproj; + $(RepoRoot)src\JSInterop\**\*.npmproj; " RestoreInParallel="false" Exclude="@(ProjectToExclude)" /> @@ -151,6 +152,14 @@ $(RepoRoot)src\SignalR\**\*.csproj; $(RepoRoot)src\Components\**\*.csproj; $(RepoRoot)src\Analyzers\**\*.csproj; + $(RepoRoot)src\FileProviders\**\*.csproj; + $(RepoRoot)src\Configuration.KeyPerFile\**\*.csproj; + $(RepoRoot)src\Localization\**\*.csproj; + $(RepoRoot)src\ObjectPool\**\*.csproj; + $(RepoRoot)src\JSInterop\**\*.csproj; + $(RepoRoot)src\WebEncoders\**\*.csproj; + $(RepoRoot)src\HealthChecks\**\*.csproj; + $(RepoRoot)src\Testing\**\*.csproj; $(RepoRoot)src\ProjectTemplates\*\*.csproj; $(RepoRoot)src\ProjectTemplates\testassets\*\*.csproj; " diff --git a/eng/Dependencies.props b/eng/Dependencies.props index 2bedb5af612d..4fc197153cf1 100644 --- a/eng/Dependencies.props +++ b/eng/Dependencies.props @@ -30,10 +30,8 @@ and are generated based on the last package release. - - @@ -41,7 +39,6 @@ and are generated based on the last package release. - @@ -49,20 +46,13 @@ and are generated based on the last package release. - - - - - - - @@ -71,16 +61,10 @@ and are generated based on the last package release. - - - - - - @@ -92,13 +76,16 @@ and are generated based on the last package release. + + + @@ -117,9 +104,7 @@ and are generated based on the last package release. - - diff --git a/eng/ProjectReferences.props b/eng/ProjectReferences.props index 8d1040b1575c..4518d9b44d3f 100644 --- a/eng/ProjectReferences.props +++ b/eng/ProjectReferences.props @@ -144,5 +144,15 @@ + + + + + + + + + + diff --git a/eng/SharedFramework.External.props b/eng/SharedFramework.External.props index 5c0c46740e27..ec6b304d9ec6 100644 --- a/eng/SharedFramework.External.props +++ b/eng/SharedFramework.External.props @@ -17,24 +17,18 @@ - - - - - - @@ -43,13 +37,10 @@ - - - diff --git a/eng/SharedFramework.Local.props b/eng/SharedFramework.Local.props index 5f09d593dea4..87b203638b27 100644 --- a/eng/SharedFramework.Local.props +++ b/eng/SharedFramework.Local.props @@ -26,6 +26,15 @@ + + + + + + + + + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 36195e902f80..fcebe42564b9 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -61,14 +61,6 @@ https://github.com/dotnet/extensions 396aff55e0b4628a7a44375e4b72e5d19a6e37ab - - https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab - - - https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab - https://github.com/dotnet/extensions 396aff55e0b4628a7a44375e4b72e5d19a6e37ab @@ -85,10 +77,6 @@ https://github.com/dotnet/extensions 396aff55e0b4628a7a44375e4b72e5d19a6e37ab - - https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab - https://github.com/dotnet/extensions 396aff55e0b4628a7a44375e4b72e5d19a6e37ab @@ -121,10 +109,6 @@ https://github.com/dotnet/extensions 396aff55e0b4628a7a44375e4b72e5d19a6e37ab - - https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab - https://github.com/dotnet/extensions 396aff55e0b4628a7a44375e4b72e5d19a6e37ab @@ -149,14 +133,6 @@ https://github.com/dotnet/extensions 396aff55e0b4628a7a44375e4b72e5d19a6e37ab - - https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab - - - https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab - https://github.com/dotnet/extensions 396aff55e0b4628a7a44375e4b72e5d19a6e37ab @@ -165,10 +141,6 @@ https://github.com/dotnet/extensions 396aff55e0b4628a7a44375e4b72e5d19a6e37ab - - https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab - https://github.com/dotnet/extensions 396aff55e0b4628a7a44375e4b72e5d19a6e37ab @@ -177,10 +149,6 @@ https://github.com/dotnet/extensions 396aff55e0b4628a7a44375e4b72e5d19a6e37ab - - https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab - https://github.com/dotnet/extensions 396aff55e0b4628a7a44375e4b72e5d19a6e37ab @@ -189,22 +157,10 @@ https://github.com/dotnet/extensions 396aff55e0b4628a7a44375e4b72e5d19a6e37ab - - https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab - https://github.com/dotnet/extensions 396aff55e0b4628a7a44375e4b72e5d19a6e37ab - - https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab - - - https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab - https://github.com/dotnet/extensions 396aff55e0b4628a7a44375e4b72e5d19a6e37ab @@ -245,10 +201,6 @@ https://github.com/dotnet/extensions 396aff55e0b4628a7a44375e4b72e5d19a6e37ab - - https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab - https://github.com/dotnet/extensions 396aff55e0b4628a7a44375e4b72e5d19a6e37ab @@ -261,34 +213,14 @@ https://github.com/dotnet/extensions 396aff55e0b4628a7a44375e4b72e5d19a6e37ab - - https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab - https://github.com/dotnet/extensions 396aff55e0b4628a7a44375e4b72e5d19a6e37ab - - https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab - - - https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab - - - https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab - https://github.com/dotnet/extensions 396aff55e0b4628a7a44375e4b72e5d19a6e37ab - - https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab - https://github.com/dotnet/corefx 5cee7c97d602f294e27c582d4dab81ec388f1d7b @@ -420,10 +352,6 @@ https://github.com/dotnet/arcade f50767f96246063f33a6565d318f3adf9058bace - - https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab - https://github.com/dotnet/roslyn 92790e24cc2b9f9e336ed0365d610e106c01df88 diff --git a/eng/Versions.props b/eng/Versions.props index d399cc33049a..b56e758a880b 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -98,14 +98,10 @@ 5.0.0-preview.1.20114.1 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 5.0.0-preview.1.20114.1 5.0.0-preview.1.20114.1 5.0.0-preview.1.20114.1 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 5.0.0-preview.1.20114.1 5.0.0-preview.1.20114.1 5.0.0-preview.1.20114.1 @@ -114,27 +110,19 @@ 5.0.0-preview.1.20114.1 5.0.0-preview.1.20114.1 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 5.0.0-preview.1.20114.1 5.0.0-preview.1.20114.1 5.0.0-preview.1.20114.1 5.0.0-preview.1.20114.1 5.0.0-preview.1.20114.1 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 5.0.0-preview.1.20114.1 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 5.0.0-preview.1.20114.1 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 5.0.0-preview.1.20114.1 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 5.0.0-preview.1.20114.1 5.0.0-preview.1.20114.1 5.0.0-preview.1.20114.1 @@ -145,17 +133,11 @@ 5.0.0-preview.1.20114.1 5.0.0-preview.1.20114.1 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 5.0.0-preview.1.20114.1 5.0.0-preview.1.20114.1 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 5.0.0-preview.1.20113.3 5.0.0-preview.1.20113.3 @@ -197,7 +179,9 @@ 0.3.0-alpha.19317.1 4.3.0 4.3.2 + 4.3.0 4.5.3 + 4.5.0 1.10.0 5.2.6 diff --git a/eng/targets/ResolveReferences.targets b/eng/targets/ResolveReferences.targets index 4efbe58e9769..67f6a7573770 100644 --- a/eng/targets/ResolveReferences.targets +++ b/eng/targets/ResolveReferences.targets @@ -119,7 +119,7 @@ - diff --git a/src/Components/Blazor/Build/test/Microsoft.AspNetCore.Blazor.Build.Tests.csproj b/src/Components/Blazor/Build/test/Microsoft.AspNetCore.Blazor.Build.Tests.csproj index 25b791e22aa8..d5ae182a8747 100644 --- a/src/Components/Blazor/Build/test/Microsoft.AspNetCore.Blazor.Build.Tests.csproj +++ b/src/Components/Blazor/Build/test/Microsoft.AspNetCore.Blazor.Build.Tests.csproj @@ -29,13 +29,13 @@ - + diff --git a/src/Components/Blazor/DevServer/src/Microsoft.AspNetCore.Blazor.DevServer.csproj b/src/Components/Blazor/DevServer/src/Microsoft.AspNetCore.Blazor.DevServer.csproj index d18ab7c9e442..5546b0db0295 100644 --- a/src/Components/Blazor/DevServer/src/Microsoft.AspNetCore.Blazor.DevServer.csproj +++ b/src/Components/Blazor/DevServer/src/Microsoft.AspNetCore.Blazor.DevServer.csproj @@ -18,8 +18,9 @@ - + + diff --git a/src/Components/Components/perf/Microsoft.AspNetCore.Components.Performance.csproj b/src/Components/Components/perf/Microsoft.AspNetCore.Components.Performance.csproj index a893d64abd8e..f40a0a40987a 100644 --- a/src/Components/Components/perf/Microsoft.AspNetCore.Components.Performance.csproj +++ b/src/Components/Components/perf/Microsoft.AspNetCore.Components.Performance.csproj @@ -10,7 +10,8 @@ - + + diff --git a/src/Components/Components/ref/Microsoft.AspNetCore.Components.csproj b/src/Components/Components/ref/Microsoft.AspNetCore.Components.csproj index 8a32a89b9e93..259b3240f0ab 100644 --- a/src/Components/Components/ref/Microsoft.AspNetCore.Components.csproj +++ b/src/Components/Components/ref/Microsoft.AspNetCore.Components.csproj @@ -7,13 +7,11 @@ - - diff --git a/src/Components/Components/src/Microsoft.AspNetCore.Components.csproj b/src/Components/Components/src/Microsoft.AspNetCore.Components.csproj index 4c0a31a3f6e0..88989ad03ceb 100644 --- a/src/Components/Components/src/Microsoft.AspNetCore.Components.csproj +++ b/src/Components/Components/src/Microsoft.AspNetCore.Components.csproj @@ -16,7 +16,8 @@ - + + @@ -34,12 +35,16 @@ + Projects=" + ../../Analyzers/src/Microsoft.AspNetCore.Components.Analyzers.csproj; + ../../../JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj; + ../../../Security/Authorization/Core/src/Microsoft.AspNetCore.Authorization.csproj"> + @@ -51,7 +56,6 @@ - diff --git a/src/Components/Server/src/Directory.Build.targets b/src/Components/Server/src/Directory.Build.targets new file mode 100644 index 000000000000..09953b9b6c9d --- /dev/null +++ b/src/Components/Server/src/Directory.Build.targets @@ -0,0 +1,6 @@ + + + + + diff --git a/src/Components/Server/src/Microsoft.AspNetCore.Components.Server.csproj b/src/Components/Server/src/Microsoft.AspNetCore.Components.Server.csproj index 04475e354052..57b11b159e10 100644 --- a/src/Components/Server/src/Microsoft.AspNetCore.Components.Server.csproj +++ b/src/Components/Server/src/Microsoft.AspNetCore.Components.Server.csproj @@ -1,5 +1,4 @@ - $(DefaultNetCoreTargetFramework) Runtime server features for ASP.NET Core Components. @@ -10,6 +9,7 @@ CS0436;$(NoWarn) $(DefineConstants);ENABLE_UNSAFE_MSGPACK;SPAN_BUILTIN;MESSAGEPACK_INTERNAL;COMPONENTS_SERVER false + Microsoft.Extensions.FileProviders.Embedded.Manifest.xml @@ -22,7 +22,8 @@ - + + @@ -39,6 +40,15 @@ $(RepoRoot)src\submodules\MessagePack-CSharp\src\MessagePack.UnityClient\Assets\Scripts\MessagePack\ + + + <_FileProviderTaskAssembly>$(ArtifactsDir)bin\Microsoft.Extensions.FileProviders.Embedded.Manifest.Task\$(Configuration)\netstandard2.0\Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.dll + + + + @@ -84,5 +94,4 @@ - \ No newline at end of file diff --git a/src/Configuration.KeyPerFile/Directory.Build.props b/src/Configuration.KeyPerFile/Directory.Build.props deleted file mode 100644 index 63d0c8b102d7..000000000000 --- a/src/Configuration.KeyPerFile/Directory.Build.props +++ /dev/null @@ -1,9 +0,0 @@ - - - - - true - configuration - $(NoWarn);PKG0001 - - diff --git a/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj b/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj index 7f9c5e7eb193..b8b281716e5b 100644 --- a/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj +++ b/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj @@ -6,6 +6,10 @@ $(DefaultNetCoreTargetFramework) true true + true + true + configuration + $(NoWarn);PKG0001 diff --git a/src/Configuration.KeyPerFile/test/ConfigurationProviderTestBase.cs b/src/Configuration.KeyPerFile/test/ConfigurationProviderTestBase.cs index 4609ee2560ff..67cb20c6c380 100644 --- a/src/Configuration.KeyPerFile/test/ConfigurationProviderTestBase.cs +++ b/src/Configuration.KeyPerFile/test/ConfigurationProviderTestBase.cs @@ -239,7 +239,7 @@ protected virtual void AssertConfig( var section3 = config.GetSection("Section3"); Assert.Equal("Section3", section3.Path, StringComparer.InvariantCultureIgnoreCase); Assert.Null(section3.Value); - + var section4 = config.GetSection("Section3:Section4"); Assert.Equal(value344, section4["Key4"], StringComparer.InvariantCultureIgnoreCase); Assert.Equal("Section3:Section4", section4.Path, StringComparer.InvariantCultureIgnoreCase); @@ -758,4 +758,4 @@ protected class TestSection }; } } -} +} \ No newline at end of file diff --git a/src/Configuration.KeyPerFile/test/Microsoft.Extensions.Configuration.KeyPerFile.Tests.csproj b/src/Configuration.KeyPerFile/test/Microsoft.Extensions.Configuration.KeyPerFile.Tests.csproj index 7abceed7028d..053553b984ea 100644 --- a/src/Configuration.KeyPerFile/test/Microsoft.Extensions.Configuration.KeyPerFile.Tests.csproj +++ b/src/Configuration.KeyPerFile/test/Microsoft.Extensions.Configuration.KeyPerFile.Tests.csproj @@ -4,10 +4,6 @@ $(DefaultNetCoreTargetFramework);net472 - - - - diff --git a/src/FileProviders/Directory.Build.props b/src/FileProviders/Directory.Build.props deleted file mode 100644 index 709c47ddbd7a..000000000000 --- a/src/FileProviders/Directory.Build.props +++ /dev/null @@ -1,8 +0,0 @@ - - - - - true - files;filesystem - - diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj index 3d5fe3cf54fa..97d9600f1312 100644 --- a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj @@ -9,6 +9,10 @@ $(MSBuildProjectName).netcoreapp.nuspec true true + true + true + files;filesystem + true @@ -28,6 +32,7 @@ + diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.multitarget.nuspec b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.multitarget.nuspec index 6acde4ec1d73..08148f6430b7 100644 --- a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.multitarget.nuspec +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.multitarget.nuspec @@ -3,11 +3,11 @@ $CommonMetadataElements$ - - + + - + diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.netcoreapp.nuspec b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.netcoreapp.nuspec index 217efb6cea98..4a55fc9230cd 100644 --- a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.netcoreapp.nuspec +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.netcoreapp.nuspec @@ -3,8 +3,8 @@ $CommonMetadataElements$ - - + + diff --git a/src/FileProviders/Manifest.MSBuildTask/src/Entry.cs b/src/FileProviders/Manifest.MSBuildTask/src/Entry.cs index 1a7f18bef81e..93f4c0d277f2 100644 --- a/src/FileProviders/Manifest.MSBuildTask/src/Entry.cs +++ b/src/FileProviders/Manifest.MSBuildTask/src/Entry.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using System.Diagnostics; -namespace Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Internal +namespace Microsoft.Extensions.FileProviders.Embedded.Manifest.Task { /// /// This API supports infrastructure and is not intended to be used diff --git a/src/FileProviders/Manifest.MSBuildTask/src/Manifest.cs b/src/FileProviders/Manifest.MSBuildTask/src/Manifest.cs index 86e99477ff47..d21c2f74bb08 100644 --- a/src/FileProviders/Manifest.MSBuildTask/src/Manifest.cs +++ b/src/FileProviders/Manifest.MSBuildTask/src/Manifest.cs @@ -5,7 +5,6 @@ using System.IO; using System.Linq; using System.Xml.Linq; -using Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Internal; namespace Microsoft.Extensions.FileProviders.Embedded.Manifest.Task { diff --git a/src/FileProviders/Manifest.MSBuildTask/src/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.csproj b/src/FileProviders/Manifest.MSBuildTask/src/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.csproj index cdc4ffdcb0b4..ab693848c501 100644 --- a/src/FileProviders/Manifest.MSBuildTask/src/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.csproj +++ b/src/FileProviders/Manifest.MSBuildTask/src/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.csproj @@ -8,6 +8,7 @@ true false false + false diff --git a/src/FileProviders/Manifest.MSBuildTask/test/GenerateEmbeddedResourcesManifestTest.cs b/src/FileProviders/Manifest.MSBuildTask/test/GenerateEmbeddedResourcesManifestTest.cs index c7285913af11..260d15f7f71c 100644 --- a/src/FileProviders/Manifest.MSBuildTask/test/GenerateEmbeddedResourcesManifestTest.cs +++ b/src/FileProviders/Manifest.MSBuildTask/test/GenerateEmbeddedResourcesManifestTest.cs @@ -10,7 +10,6 @@ using System.Xml.Linq; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; -using Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Internal; using Xunit; namespace Microsoft.Extensions.FileProviders.Embedded.Manifest.Task diff --git a/src/FileProviders/Manifest.MSBuildTask/test/SetExtensions.cs b/src/FileProviders/Manifest.MSBuildTask/test/SetExtensions.cs index 6b2c83a875fc..0a80b7fb76dd 100644 --- a/src/FileProviders/Manifest.MSBuildTask/test/SetExtensions.cs +++ b/src/FileProviders/Manifest.MSBuildTask/test/SetExtensions.cs @@ -1,8 +1,6 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Internal; - namespace Microsoft.Extensions.FileProviders.Embedded.Manifest.Task { internal static class SetExtensions diff --git a/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj b/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj index aeb85d3e7603..fac2cf9a99ee 100644 --- a/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj +++ b/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj @@ -14,6 +14,7 @@ Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck diagnostics;healthchecks true true + true diff --git a/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj b/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj index 35c789a691f2..9e89a9e06e10 100644 --- a/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj +++ b/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj @@ -13,6 +13,7 @@ Microsoft.Extensions.Diagnostics.HealthChecks.IHealthChecksBuilder diagnostics;healthchecks true true + true @@ -20,8 +21,8 @@ Microsoft.Extensions.Diagnostics.HealthChecks.IHealthChecksBuilder - - + + diff --git a/src/HealthChecks/HealthChecks/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests.csproj b/src/HealthChecks/HealthChecks/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests.csproj index 1591585b826e..d5cf94e2e5d7 100644 --- a/src/HealthChecks/HealthChecks/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests.csproj +++ b/src/HealthChecks/HealthChecks/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests.csproj @@ -1,7 +1,5 @@  - - $(DefaultNetCoreTargetFramework);net472 Microsoft.Extensions.Diagnostics.HealthChecks @@ -21,7 +19,7 @@ - + diff --git a/src/Hosting/Hosting/ref/Microsoft.AspNetCore.Hosting.csproj b/src/Hosting/Hosting/ref/Microsoft.AspNetCore.Hosting.csproj index 4c4261611855..959abbb16089 100644 --- a/src/Hosting/Hosting/ref/Microsoft.AspNetCore.Hosting.csproj +++ b/src/Hosting/Hosting/ref/Microsoft.AspNetCore.Hosting.csproj @@ -17,6 +17,5 @@ - diff --git a/src/Hosting/Hosting/src/Microsoft.AspNetCore.Hosting.csproj b/src/Hosting/Hosting/src/Microsoft.AspNetCore.Hosting.csproj index 0e71bb69c995..e8315ef18bbf 100644 --- a/src/Hosting/Hosting/src/Microsoft.AspNetCore.Hosting.csproj +++ b/src/Hosting/Hosting/src/Microsoft.AspNetCore.Hosting.csproj @@ -29,7 +29,8 @@ - + + diff --git a/src/Hosting/TestHost/ref/Microsoft.AspNetCore.TestHost.csproj b/src/Hosting/TestHost/ref/Microsoft.AspNetCore.TestHost.csproj index 7dd0c1a2d2e5..8da7c4260504 100644 --- a/src/Hosting/TestHost/ref/Microsoft.AspNetCore.TestHost.csproj +++ b/src/Hosting/TestHost/ref/Microsoft.AspNetCore.TestHost.csproj @@ -6,7 +6,6 @@ - diff --git a/src/Hosting/TestHost/src/Microsoft.AspNetCore.TestHost.csproj b/src/Hosting/TestHost/src/Microsoft.AspNetCore.TestHost.csproj index 0ef4679eb1c3..d20720452019 100644 --- a/src/Hosting/TestHost/src/Microsoft.AspNetCore.TestHost.csproj +++ b/src/Hosting/TestHost/src/Microsoft.AspNetCore.TestHost.csproj @@ -11,8 +11,9 @@ - + + diff --git a/src/Http/Http.Abstractions/ref/Microsoft.AspNetCore.Http.Abstractions.csproj b/src/Http/Http.Abstractions/ref/Microsoft.AspNetCore.Http.Abstractions.csproj index a3f0a4268f71..fc6109502d25 100644 --- a/src/Http/Http.Abstractions/ref/Microsoft.AspNetCore.Http.Abstractions.csproj +++ b/src/Http/Http.Abstractions/ref/Microsoft.AspNetCore.Http.Abstractions.csproj @@ -6,7 +6,6 @@ - diff --git a/src/Http/Http.Abstractions/src/Microsoft.AspNetCore.Http.Abstractions.csproj b/src/Http/Http.Abstractions/src/Microsoft.AspNetCore.Http.Abstractions.csproj index c5ab085eef0a..775913675132 100644 --- a/src/Http/Http.Abstractions/src/Microsoft.AspNetCore.Http.Abstractions.csproj +++ b/src/Http/Http.Abstractions/src/Microsoft.AspNetCore.Http.Abstractions.csproj @@ -22,8 +22,10 @@ Microsoft.AspNetCore.Http.HttpResponse - + + + diff --git a/src/Http/Http/perf/Microsoft.AspNetCore.Http.Performance.csproj b/src/Http/Http/perf/Microsoft.AspNetCore.Http.Performance.csproj index 6a42bef5fe3e..7f371de3801d 100644 --- a/src/Http/Http/perf/Microsoft.AspNetCore.Http.Performance.csproj +++ b/src/Http/Http/perf/Microsoft.AspNetCore.Http.Performance.csproj @@ -12,7 +12,8 @@ - + + diff --git a/src/Http/Routing/perf/Microsoft.AspNetCore.Routing.Performance.csproj b/src/Http/Routing/perf/Microsoft.AspNetCore.Routing.Performance.csproj index 308dc68011f0..4f68c48487a0 100644 --- a/src/Http/Routing/perf/Microsoft.AspNetCore.Routing.Performance.csproj +++ b/src/Http/Routing/perf/Microsoft.AspNetCore.Routing.Performance.csproj @@ -37,11 +37,12 @@ - + + diff --git a/src/Http/Routing/ref/Microsoft.AspNetCore.Routing.csproj b/src/Http/Routing/ref/Microsoft.AspNetCore.Routing.csproj index 90ff9fea7c37..e8a4a831ba17 100644 --- a/src/Http/Routing/ref/Microsoft.AspNetCore.Routing.csproj +++ b/src/Http/Routing/ref/Microsoft.AspNetCore.Routing.csproj @@ -8,7 +8,6 @@ - diff --git a/src/Http/Routing/src/Microsoft.AspNetCore.Routing.csproj b/src/Http/Routing/src/Microsoft.AspNetCore.Routing.csproj index 7c399fe55754..56072134d088 100644 --- a/src/Http/Routing/src/Microsoft.AspNetCore.Routing.csproj +++ b/src/Http/Routing/src/Microsoft.AspNetCore.Routing.csproj @@ -30,10 +30,11 @@ Microsoft.AspNetCore.Routing.RouteCollection - + + diff --git a/src/Http/Routing/tools/Swaggatherer/Swaggatherer.csproj b/src/Http/Routing/tools/Swaggatherer/Swaggatherer.csproj index cd256b125c08..1d083a55e469 100644 --- a/src/Http/Routing/tools/Swaggatherer/Swaggatherer.csproj +++ b/src/Http/Routing/tools/Swaggatherer/Swaggatherer.csproj @@ -9,7 +9,8 @@ - + + diff --git a/src/Http/WebUtilities/perf/Microsoft.AspNetCore.WebUtilities.Performance/Microsoft.AspNetCore.WebUtilities.Performance.csproj b/src/Http/WebUtilities/perf/Microsoft.AspNetCore.WebUtilities.Performance/Microsoft.AspNetCore.WebUtilities.Performance.csproj index adbd0714f6d4..a170bf6cc118 100644 --- a/src/Http/WebUtilities/perf/Microsoft.AspNetCore.WebUtilities.Performance/Microsoft.AspNetCore.WebUtilities.Performance.csproj +++ b/src/Http/WebUtilities/perf/Microsoft.AspNetCore.WebUtilities.Performance/Microsoft.AspNetCore.WebUtilities.Performance.csproj @@ -13,10 +13,10 @@ - + diff --git a/src/Http/perf/Microbenchmarks/Microsoft.AspNetCore.Http.Microbenchmarks.csproj b/src/Http/perf/Microbenchmarks/Microsoft.AspNetCore.Http.Microbenchmarks.csproj index 08e9b73b5fd8..f4f35eea0067 100644 --- a/src/Http/perf/Microbenchmarks/Microsoft.AspNetCore.Http.Microbenchmarks.csproj +++ b/src/Http/perf/Microbenchmarks/Microsoft.AspNetCore.Http.Microbenchmarks.csproj @@ -7,8 +7,9 @@ - + + diff --git a/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj b/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj index a45484e0d3a7..83acae807fe5 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj +++ b/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj @@ -8,6 +8,7 @@ true true true + true diff --git a/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj b/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj index 4b8816040148..92139f6145fd 100644 --- a/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj +++ b/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj @@ -13,6 +13,7 @@ Microsoft.Extensions.Localization.IStringLocalizer<T> localization true true + true diff --git a/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj b/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj index b6b059ce827f..f69b533885e8 100644 --- a/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj +++ b/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj @@ -10,6 +10,7 @@ localization true true + true diff --git a/src/Localization/Localization/src/ResourceManagerStringLocalizer.cs b/src/Localization/Localization/src/ResourceManagerStringLocalizer.cs index 4f13611a47d9..0f033a522713 100644 --- a/src/Localization/Localization/src/ResourceManagerStringLocalizer.cs +++ b/src/Localization/Localization/src/ResourceManagerStringLocalizer.cs @@ -54,7 +54,9 @@ public ResourceManagerStringLocalizer( /// public ResourceManagerStringLocalizer( ResourceManager resourceManager, +#pragma warning disable PUB0001 // Pubternal type AssemblyWrapper in public API AssemblyWrapper resourceAssemblyWrapper, +#pragma warning restore PUB0001 // Pubternal type AssemblyWrapper in public API string baseName, IResourceNamesCache resourceNamesCache, ILogger logger) @@ -76,7 +78,9 @@ public ResourceManagerStringLocalizer( /// public ResourceManagerStringLocalizer( ResourceManager resourceManager, +#pragma warning disable PUB0001 // Pubternal type IResourceStringProvider in public API IResourceStringProvider resourceStringProvider, +#pragma warning restore PUB0001 // Pubternal type IResourceStringProvider in public API string baseName, IResourceNamesCache resourceNamesCache, ILogger logger) diff --git a/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests/Microsoft.Extensions.Localization.Tests.csproj b/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests/Microsoft.Extensions.Localization.Tests.csproj index 70e7a6de1a9d..747bafc70155 100644 --- a/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests/Microsoft.Extensions.Localization.Tests.csproj +++ b/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests/Microsoft.Extensions.Localization.Tests.csproj @@ -11,7 +11,6 @@ - diff --git a/src/Middleware/ConcurrencyLimiter/perf/Microbenchmarks/Microsoft.AspNetCore.ConcurrencyLimiter.Microbenchmarks.csproj b/src/Middleware/ConcurrencyLimiter/perf/Microbenchmarks/Microsoft.AspNetCore.ConcurrencyLimiter.Microbenchmarks.csproj index 83717156dfb8..2674be405d14 100644 --- a/src/Middleware/ConcurrencyLimiter/perf/Microbenchmarks/Microsoft.AspNetCore.ConcurrencyLimiter.Microbenchmarks.csproj +++ b/src/Middleware/ConcurrencyLimiter/perf/Microbenchmarks/Microsoft.AspNetCore.ConcurrencyLimiter.Microbenchmarks.csproj @@ -12,7 +12,8 @@ - + + diff --git a/src/Middleware/ConcurrencyLimiter/ref/Microsoft.AspNetCore.ConcurrencyLimiter.csproj b/src/Middleware/ConcurrencyLimiter/ref/Microsoft.AspNetCore.ConcurrencyLimiter.csproj index 1b36bc0211bb..4aea744d9094 100644 --- a/src/Middleware/ConcurrencyLimiter/ref/Microsoft.AspNetCore.ConcurrencyLimiter.csproj +++ b/src/Middleware/ConcurrencyLimiter/ref/Microsoft.AspNetCore.ConcurrencyLimiter.csproj @@ -8,6 +8,5 @@ - diff --git a/src/Middleware/ConcurrencyLimiter/src/Microsoft.AspNetCore.ConcurrencyLimiter.csproj b/src/Middleware/ConcurrencyLimiter/src/Microsoft.AspNetCore.ConcurrencyLimiter.csproj index fd2eb47d66a7..5bdff90f9002 100644 --- a/src/Middleware/ConcurrencyLimiter/src/Microsoft.AspNetCore.ConcurrencyLimiter.csproj +++ b/src/Middleware/ConcurrencyLimiter/src/Microsoft.AspNetCore.ConcurrencyLimiter.csproj @@ -12,7 +12,8 @@ - + + diff --git a/src/Middleware/Diagnostics/ref/Microsoft.AspNetCore.Diagnostics.csproj b/src/Middleware/Diagnostics/ref/Microsoft.AspNetCore.Diagnostics.csproj index 1cb4a825a639..eb5f581428e3 100644 --- a/src/Middleware/Diagnostics/ref/Microsoft.AspNetCore.Diagnostics.csproj +++ b/src/Middleware/Diagnostics/ref/Microsoft.AspNetCore.Diagnostics.csproj @@ -13,6 +13,5 @@ - diff --git a/src/Middleware/Diagnostics/src/Microsoft.AspNetCore.Diagnostics.csproj b/src/Middleware/Diagnostics/src/Microsoft.AspNetCore.Diagnostics.csproj index 6a82485ad685..3697cbee0f3c 100644 --- a/src/Middleware/Diagnostics/src/Microsoft.AspNetCore.Diagnostics.csproj +++ b/src/Middleware/Diagnostics/src/Microsoft.AspNetCore.Diagnostics.csproj @@ -14,6 +14,7 @@ + @@ -25,7 +26,6 @@ - diff --git a/src/Middleware/ResponseCompression/perf/Microsoft.AspNetCore.ResponseCompression.Performance.csproj b/src/Middleware/ResponseCompression/perf/Microsoft.AspNetCore.ResponseCompression.Performance.csproj index a4ec7230f61c..5b01d1103395 100644 --- a/src/Middleware/ResponseCompression/perf/Microsoft.AspNetCore.ResponseCompression.Performance.csproj +++ b/src/Middleware/ResponseCompression/perf/Microsoft.AspNetCore.ResponseCompression.Performance.csproj @@ -8,9 +8,10 @@ - + + diff --git a/src/Middleware/perf/Microbenchmarks/Microsoft.AspNetCore.WebSockets.Microbenchmarks.csproj b/src/Middleware/perf/Microbenchmarks/Microsoft.AspNetCore.WebSockets.Microbenchmarks.csproj index 96a0535de79f..81d4c4f6431e 100644 --- a/src/Middleware/perf/Microbenchmarks/Microsoft.AspNetCore.WebSockets.Microbenchmarks.csproj +++ b/src/Middleware/perf/Microbenchmarks/Microsoft.AspNetCore.WebSockets.Microbenchmarks.csproj @@ -7,7 +7,8 @@ - + + diff --git a/src/Middleware/perf/ResponseCaching.Microbenchmarks/Microsoft.AspNetCore.ResponseCaching.Microbenchmarks.csproj b/src/Middleware/perf/ResponseCaching.Microbenchmarks/Microsoft.AspNetCore.ResponseCaching.Microbenchmarks.csproj index 5fc2110b4044..15f456fd2f41 100644 --- a/src/Middleware/perf/ResponseCaching.Microbenchmarks/Microsoft.AspNetCore.ResponseCaching.Microbenchmarks.csproj +++ b/src/Middleware/perf/ResponseCaching.Microbenchmarks/Microsoft.AspNetCore.ResponseCaching.Microbenchmarks.csproj @@ -7,7 +7,8 @@ - + + diff --git a/src/MusicStore/test/MusicStore.E2ETests/MusicStore.E2ETests.csproj b/src/MusicStore/test/MusicStore.E2ETests/MusicStore.E2ETests.csproj index 8a4bebf54668..111d4764aca7 100644 --- a/src/MusicStore/test/MusicStore.E2ETests/MusicStore.E2ETests.csproj +++ b/src/MusicStore/test/MusicStore.E2ETests/MusicStore.E2ETests.csproj @@ -27,7 +27,6 @@ - @@ -36,6 +35,8 @@ + + diff --git a/src/Mvc/Mvc.Abstractions/ref/Microsoft.AspNetCore.Mvc.Abstractions.csproj b/src/Mvc/Mvc.Abstractions/ref/Microsoft.AspNetCore.Mvc.Abstractions.csproj index 86533825ea94..aca1b44ad56e 100644 --- a/src/Mvc/Mvc.Abstractions/ref/Microsoft.AspNetCore.Mvc.Abstractions.csproj +++ b/src/Mvc/Mvc.Abstractions/ref/Microsoft.AspNetCore.Mvc.Abstractions.csproj @@ -7,6 +7,5 @@ - diff --git a/src/Mvc/Mvc.Abstractions/src/Microsoft.AspNetCore.Mvc.Abstractions.csproj b/src/Mvc/Mvc.Abstractions/src/Microsoft.AspNetCore.Mvc.Abstractions.csproj index 4be8e0b01612..8362bd5cbe25 100644 --- a/src/Mvc/Mvc.Abstractions/src/Microsoft.AspNetCore.Mvc.Abstractions.csproj +++ b/src/Mvc/Mvc.Abstractions/src/Microsoft.AspNetCore.Mvc.Abstractions.csproj @@ -20,8 +20,8 @@ Microsoft.AspNetCore.Mvc.IActionResult - - + + diff --git a/src/Mvc/Mvc.Core/ref/Microsoft.AspNetCore.Mvc.Core.csproj b/src/Mvc/Mvc.Core/ref/Microsoft.AspNetCore.Mvc.Core.csproj index 670de5a7d7b3..92acf1544ff2 100644 --- a/src/Mvc/Mvc.Core/ref/Microsoft.AspNetCore.Mvc.Core.csproj +++ b/src/Mvc/Mvc.Core/ref/Microsoft.AspNetCore.Mvc.Core.csproj @@ -18,8 +18,5 @@ - - - diff --git a/src/Mvc/Mvc.Core/src/Microsoft.AspNetCore.Mvc.Core.csproj b/src/Mvc/Mvc.Core/src/Microsoft.AspNetCore.Mvc.Core.csproj index 1d1eaa6448ee..7ffbe3d3f349 100644 --- a/src/Mvc/Mvc.Core/src/Microsoft.AspNetCore.Mvc.Core.csproj +++ b/src/Mvc/Mvc.Core/src/Microsoft.AspNetCore.Mvc.Core.csproj @@ -48,9 +48,9 @@ Microsoft.AspNetCore.Mvc.RouteAttribute - - - + + + diff --git a/src/Mvc/Mvc.Testing/ref/Microsoft.AspNetCore.Mvc.Testing.csproj b/src/Mvc/Mvc.Testing/ref/Microsoft.AspNetCore.Mvc.Testing.csproj index cb3241a4fa2d..eebe8535136c 100644 --- a/src/Mvc/Mvc.Testing/ref/Microsoft.AspNetCore.Mvc.Testing.csproj +++ b/src/Mvc/Mvc.Testing/ref/Microsoft.AspNetCore.Mvc.Testing.csproj @@ -7,7 +7,6 @@ - diff --git a/src/Mvc/Mvc.Testing/src/Microsoft.AspNetCore.Mvc.Testing.csproj b/src/Mvc/Mvc.Testing/src/Microsoft.AspNetCore.Mvc.Testing.csproj index 0df3b51ffa3e..2c851d263bbb 100644 --- a/src/Mvc/Mvc.Testing/src/Microsoft.AspNetCore.Mvc.Testing.csproj +++ b/src/Mvc/Mvc.Testing/src/Microsoft.AspNetCore.Mvc.Testing.csproj @@ -12,9 +12,10 @@ - + + diff --git a/src/Mvc/benchmarks/Microsoft.AspNetCore.Mvc.Performance/Microsoft.AspNetCore.Mvc.Performance.csproj b/src/Mvc/benchmarks/Microsoft.AspNetCore.Mvc.Performance/Microsoft.AspNetCore.Mvc.Performance.csproj index e2a5cad37af6..2c40984a452c 100644 --- a/src/Mvc/benchmarks/Microsoft.AspNetCore.Mvc.Performance/Microsoft.AspNetCore.Mvc.Performance.csproj +++ b/src/Mvc/benchmarks/Microsoft.AspNetCore.Mvc.Performance/Microsoft.AspNetCore.Mvc.Performance.csproj @@ -11,7 +11,8 @@ - + + diff --git a/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.csproj b/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.csproj index 1fbb81a9ca62..be298e7ae324 100644 --- a/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.csproj +++ b/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.csproj @@ -1,10 +1,14 @@ - netstandard2.0 + netstandard2.0;$(DefaultNetCoreTargetFramework) + + + + diff --git a/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.netcoreapp.cs b/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.netcoreapp.cs new file mode 100644 index 000000000000..b3b72bec86e3 --- /dev/null +++ b/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.netcoreapp.cs @@ -0,0 +1,76 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.Extensions.ObjectPool +{ + public partial class DefaultObjectPoolProvider : Microsoft.Extensions.ObjectPool.ObjectPoolProvider + { + public DefaultObjectPoolProvider() { } + public int MaximumRetained { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } + public override Microsoft.Extensions.ObjectPool.ObjectPool Create(Microsoft.Extensions.ObjectPool.IPooledObjectPolicy policy) { throw null; } + } + public partial class DefaultObjectPool : Microsoft.Extensions.ObjectPool.ObjectPool where T : class + { + public DefaultObjectPool(Microsoft.Extensions.ObjectPool.IPooledObjectPolicy policy) { } + public DefaultObjectPool(Microsoft.Extensions.ObjectPool.IPooledObjectPolicy policy, int maximumRetained) { } + public override T Get() { throw null; } + public override void Return(T obj) { } + } + public partial class DefaultPooledObjectPolicy : Microsoft.Extensions.ObjectPool.PooledObjectPolicy where T : class, new() + { + public DefaultPooledObjectPolicy() { } + public override T Create() { throw null; } + public override bool Return(T obj) { throw null; } + } + public partial interface IPooledObjectPolicy + { + T Create(); + bool Return(T obj); + } + public partial class LeakTrackingObjectPoolProvider : Microsoft.Extensions.ObjectPool.ObjectPoolProvider + { + public LeakTrackingObjectPoolProvider(Microsoft.Extensions.ObjectPool.ObjectPoolProvider inner) { } + public override Microsoft.Extensions.ObjectPool.ObjectPool Create(Microsoft.Extensions.ObjectPool.IPooledObjectPolicy policy) { throw null; } + } + public partial class LeakTrackingObjectPool : Microsoft.Extensions.ObjectPool.ObjectPool where T : class + { + public LeakTrackingObjectPool(Microsoft.Extensions.ObjectPool.ObjectPool inner) { } + public override T Get() { throw null; } + public override void Return(T obj) { } + } + public static partial class ObjectPool + { + public static Microsoft.Extensions.ObjectPool.ObjectPool Create(Microsoft.Extensions.ObjectPool.IPooledObjectPolicy policy = null) where T : class, new() { throw null; } + } + public abstract partial class ObjectPoolProvider + { + protected ObjectPoolProvider() { } + public Microsoft.Extensions.ObjectPool.ObjectPool Create() where T : class, new() { throw null; } + public abstract Microsoft.Extensions.ObjectPool.ObjectPool Create(Microsoft.Extensions.ObjectPool.IPooledObjectPolicy policy) where T : class; + } + public static partial class ObjectPoolProviderExtensions + { + public static Microsoft.Extensions.ObjectPool.ObjectPool CreateStringBuilderPool(this Microsoft.Extensions.ObjectPool.ObjectPoolProvider provider) { throw null; } + public static Microsoft.Extensions.ObjectPool.ObjectPool CreateStringBuilderPool(this Microsoft.Extensions.ObjectPool.ObjectPoolProvider provider, int initialCapacity, int maximumRetainedCapacity) { throw null; } + } + public abstract partial class ObjectPool where T : class + { + protected ObjectPool() { } + public abstract T Get(); + public abstract void Return(T obj); + } + public abstract partial class PooledObjectPolicy : Microsoft.Extensions.ObjectPool.IPooledObjectPolicy + { + protected PooledObjectPolicy() { } + public abstract T Create(); + public abstract bool Return(T obj); + } + public partial class StringBuilderPooledObjectPolicy : Microsoft.Extensions.ObjectPool.PooledObjectPolicy + { + public StringBuilderPooledObjectPolicy() { } + public int InitialCapacity { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } + public int MaximumRetainedCapacity { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } + public override System.Text.StringBuilder Create() { throw null; } + public override bool Return(System.Text.StringBuilder obj) { throw null; } + } +} diff --git a/src/ObjectPool/src/Microsoft.Extensions.ObjectPool.csproj b/src/ObjectPool/src/Microsoft.Extensions.ObjectPool.csproj index c193ad6e1f16..ab3be847adf7 100644 --- a/src/ObjectPool/src/Microsoft.Extensions.ObjectPool.csproj +++ b/src/ObjectPool/src/Microsoft.Extensions.ObjectPool.csproj @@ -2,12 +2,13 @@ A simple object pool implementation. - netstandard2.0 + netstandard2.0;$(DefaultNetCoreTargetFramework) $(NoWarn);CS1591 true pooling true true + true diff --git a/src/ProjectTemplates/test/ProjectTemplates.Tests.csproj b/src/ProjectTemplates/test/ProjectTemplates.Tests.csproj index 037552d49270..943414049671 100644 --- a/src/ProjectTemplates/test/ProjectTemplates.Tests.csproj +++ b/src/ProjectTemplates/test/ProjectTemplates.Tests.csproj @@ -33,7 +33,8 @@ - + + diff --git a/src/Razor/Razor/ref/Microsoft.AspNetCore.Razor.csproj b/src/Razor/Razor/ref/Microsoft.AspNetCore.Razor.csproj index 57241da57879..e32a58b3d22a 100644 --- a/src/Razor/Razor/ref/Microsoft.AspNetCore.Razor.csproj +++ b/src/Razor/Razor/ref/Microsoft.AspNetCore.Razor.csproj @@ -6,6 +6,5 @@ - diff --git a/src/Razor/Razor/src/Microsoft.AspNetCore.Razor.csproj b/src/Razor/Razor/src/Microsoft.AspNetCore.Razor.csproj index c13602372b5f..d849db7d5a7a 100644 --- a/src/Razor/Razor/src/Microsoft.AspNetCore.Razor.csproj +++ b/src/Razor/Razor/src/Microsoft.AspNetCore.Razor.csproj @@ -21,7 +21,8 @@ Microsoft.AspNetCore.Razor.TagHelpers.ITagHelper - + + diff --git a/src/Security/benchmarks/Microsoft.AspNetCore.Security.Performance/Microsoft.AspNetCore.Security.Performance.csproj b/src/Security/benchmarks/Microsoft.AspNetCore.Security.Performance/Microsoft.AspNetCore.Security.Performance.csproj index c6bb27153640..65871e5a7837 100644 --- a/src/Security/benchmarks/Microsoft.AspNetCore.Security.Performance/Microsoft.AspNetCore.Security.Performance.csproj +++ b/src/Security/benchmarks/Microsoft.AspNetCore.Security.Performance/Microsoft.AspNetCore.Security.Performance.csproj @@ -12,10 +12,11 @@ - + + diff --git a/src/Servers/Connections.Abstractions/ref/Microsoft.AspNetCore.Connections.Abstractions.csproj b/src/Servers/Connections.Abstractions/ref/Microsoft.AspNetCore.Connections.Abstractions.csproj index 290b58c7fe0c..8678427a8f4b 100644 --- a/src/Servers/Connections.Abstractions/ref/Microsoft.AspNetCore.Connections.Abstractions.csproj +++ b/src/Servers/Connections.Abstractions/ref/Microsoft.AspNetCore.Connections.Abstractions.csproj @@ -6,20 +6,17 @@ - - - diff --git a/src/Servers/Connections.Abstractions/src/Microsoft.AspNetCore.Connections.Abstractions.csproj b/src/Servers/Connections.Abstractions/src/Microsoft.AspNetCore.Connections.Abstractions.csproj index 43f6d4b5748e..6b0ec1e47817 100644 --- a/src/Servers/Connections.Abstractions/src/Microsoft.AspNetCore.Connections.Abstractions.csproj +++ b/src/Servers/Connections.Abstractions/src/Microsoft.AspNetCore.Connections.Abstractions.csproj @@ -13,8 +13,10 @@ - + + + diff --git a/src/Servers/IIS/IIS/benchmarks/IIS.Performance/IIS.Performance.csproj b/src/Servers/IIS/IIS/benchmarks/IIS.Performance/IIS.Performance.csproj index 9435906632b3..2abb302273f5 100644 --- a/src/Servers/IIS/IIS/benchmarks/IIS.Performance/IIS.Performance.csproj +++ b/src/Servers/IIS/IIS/benchmarks/IIS.Performance/IIS.Performance.csproj @@ -33,8 +33,9 @@ - + + diff --git a/src/Servers/IIS/IIS/ref/Microsoft.AspNetCore.Server.IIS.csproj b/src/Servers/IIS/IIS/ref/Microsoft.AspNetCore.Server.IIS.csproj index a88930de54cc..746230e19c86 100644 --- a/src/Servers/IIS/IIS/ref/Microsoft.AspNetCore.Server.IIS.csproj +++ b/src/Servers/IIS/IIS/ref/Microsoft.AspNetCore.Server.IIS.csproj @@ -12,7 +12,6 @@ - diff --git a/src/Servers/IIS/IIS/src/Microsoft.AspNetCore.Server.IIS.csproj b/src/Servers/IIS/IIS/src/Microsoft.AspNetCore.Server.IIS.csproj index a95e4ec56ad2..1645d8fd2619 100644 --- a/src/Servers/IIS/IIS/src/Microsoft.AspNetCore.Server.IIS.csproj +++ b/src/Servers/IIS/IIS/src/Microsoft.AspNetCore.Server.IIS.csproj @@ -14,6 +14,7 @@ + @@ -39,7 +40,6 @@ - diff --git a/src/Servers/Kestrel/perf/Kestrel.Performance/Microsoft.AspNetCore.Server.Kestrel.Performance.csproj b/src/Servers/Kestrel/perf/Kestrel.Performance/Microsoft.AspNetCore.Server.Kestrel.Performance.csproj index df7255d954f9..07c9d49f68ad 100644 --- a/src/Servers/Kestrel/perf/Kestrel.Performance/Microsoft.AspNetCore.Server.Kestrel.Performance.csproj +++ b/src/Servers/Kestrel/perf/Kestrel.Performance/Microsoft.AspNetCore.Server.Kestrel.Performance.csproj @@ -23,11 +23,12 @@ - + + diff --git a/src/Servers/Kestrel/test/InMemory.FunctionalTests/InMemory.FunctionalTests.csproj b/src/Servers/Kestrel/test/InMemory.FunctionalTests/InMemory.FunctionalTests.csproj index e77498d9516f..b4442f73ba1a 100644 --- a/src/Servers/Kestrel/test/InMemory.FunctionalTests/InMemory.FunctionalTests.csproj +++ b/src/Servers/Kestrel/test/InMemory.FunctionalTests/InMemory.FunctionalTests.csproj @@ -17,13 +17,13 @@ + - diff --git a/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj b/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj index 76051ab18149..ff221e120566 100644 --- a/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj +++ b/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj @@ -18,6 +18,7 @@ + @@ -25,6 +26,7 @@ + @@ -36,7 +38,6 @@ - diff --git a/src/SignalR/common/Http.Connections/ref/Microsoft.AspNetCore.Http.Connections.csproj b/src/SignalR/common/Http.Connections/ref/Microsoft.AspNetCore.Http.Connections.csproj index 8b474e9124bb..882787914093 100644 --- a/src/SignalR/common/Http.Connections/ref/Microsoft.AspNetCore.Http.Connections.csproj +++ b/src/SignalR/common/Http.Connections/ref/Microsoft.AspNetCore.Http.Connections.csproj @@ -11,7 +11,6 @@ - diff --git a/src/SignalR/common/Http.Connections/src/Microsoft.AspNetCore.Http.Connections.csproj b/src/SignalR/common/Http.Connections/src/Microsoft.AspNetCore.Http.Connections.csproj index d19e84efe503..4e739f5c377e 100644 --- a/src/SignalR/common/Http.Connections/src/Microsoft.AspNetCore.Http.Connections.csproj +++ b/src/SignalR/common/Http.Connections/src/Microsoft.AspNetCore.Http.Connections.csproj @@ -31,8 +31,9 @@ - + + diff --git a/src/SignalR/common/testassets/Tests.Utils/Microsoft.AspNetCore.SignalR.Tests.Utils.csproj b/src/SignalR/common/testassets/Tests.Utils/Microsoft.AspNetCore.SignalR.Tests.Utils.csproj index ce47db0ac82b..578f42c20b94 100644 --- a/src/SignalR/common/testassets/Tests.Utils/Microsoft.AspNetCore.SignalR.Tests.Utils.csproj +++ b/src/SignalR/common/testassets/Tests.Utils/Microsoft.AspNetCore.SignalR.Tests.Utils.csproj @@ -20,7 +20,8 @@ - + + diff --git a/src/SignalR/perf/Microbenchmarks/Microsoft.AspNetCore.SignalR.Microbenchmarks.csproj b/src/SignalR/perf/Microbenchmarks/Microsoft.AspNetCore.SignalR.Microbenchmarks.csproj index 3f892a8cdf40..ff022626496b 100644 --- a/src/SignalR/perf/Microbenchmarks/Microsoft.AspNetCore.SignalR.Microbenchmarks.csproj +++ b/src/SignalR/perf/Microbenchmarks/Microsoft.AspNetCore.SignalR.Microbenchmarks.csproj @@ -22,7 +22,6 @@ - @@ -32,10 +31,13 @@ - + + + + diff --git a/src/SignalR/perf/benchmarkapps/Crankier/Crankier.csproj b/src/SignalR/perf/benchmarkapps/Crankier/Crankier.csproj index 23ae6b002733..001d8138de94 100644 --- a/src/SignalR/perf/benchmarkapps/Crankier/Crankier.csproj +++ b/src/SignalR/perf/benchmarkapps/Crankier/Crankier.csproj @@ -9,7 +9,6 @@ - @@ -17,6 +16,8 @@ + + diff --git a/src/SignalR/samples/ClientSample/ClientSample.csproj b/src/SignalR/samples/ClientSample/ClientSample.csproj index 4c8d780015e1..bdbe30c2be95 100644 --- a/src/SignalR/samples/ClientSample/ClientSample.csproj +++ b/src/SignalR/samples/ClientSample/ClientSample.csproj @@ -7,13 +7,13 @@ + - diff --git a/src/Testing/ref/Microsoft.AspNetCore.Testing.csproj b/src/Testing/ref/Microsoft.AspNetCore.Testing.csproj deleted file mode 100644 index 4606a5411106..000000000000 --- a/src/Testing/ref/Microsoft.AspNetCore.Testing.csproj +++ /dev/null @@ -1,32 +0,0 @@ - - - - netstandard2.0;net46 - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs b/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs deleted file mode 100644 index 17a922a6c7b7..000000000000 --- a/src/Testing/ref/Microsoft.AspNetCore.Testing.net46.cs +++ /dev/null @@ -1,374 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -namespace Microsoft.AspNetCore.Testing -{ - public partial class AspNetTestAssemblyRunner : Xunit.Sdk.XunitTestAssemblyRunner - { - public AspNetTestAssemblyRunner(Xunit.Abstractions.ITestAssembly testAssembly, System.Collections.Generic.IEnumerable testCases, Xunit.Abstractions.IMessageSink diagnosticMessageSink, Xunit.Abstractions.IMessageSink executionMessageSink, Xunit.Abstractions.ITestFrameworkExecutionOptions executionOptions) : base (default(Xunit.Abstractions.ITestAssembly), default(System.Collections.Generic.IEnumerable), default(Xunit.Abstractions.IMessageSink), default(Xunit.Abstractions.IMessageSink), default(Xunit.Abstractions.ITestFrameworkExecutionOptions)) { } - [System.Diagnostics.DebuggerStepThroughAttribute] - protected override System.Threading.Tasks.Task AfterTestAssemblyStartingAsync() { throw null; } - protected override System.Threading.Tasks.Task BeforeTestAssemblyFinishedAsync() { throw null; } - protected override System.Threading.Tasks.Task RunTestCollectionAsync(Xunit.Sdk.IMessageBus messageBus, Xunit.Abstractions.ITestCollection testCollection, System.Collections.Generic.IEnumerable testCases, System.Threading.CancellationTokenSource cancellationTokenSource) { throw null; } - } - public partial class AspNetTestCollectionRunner : Xunit.Sdk.XunitTestCollectionRunner - { - public AspNetTestCollectionRunner(System.Collections.Generic.Dictionary assemblyFixtureMappings, Xunit.Abstractions.ITestCollection testCollection, System.Collections.Generic.IEnumerable testCases, Xunit.Abstractions.IMessageSink diagnosticMessageSink, Xunit.Sdk.IMessageBus messageBus, Xunit.Sdk.ITestCaseOrderer testCaseOrderer, Xunit.Sdk.ExceptionAggregator aggregator, System.Threading.CancellationTokenSource cancellationTokenSource) : base (default(Xunit.Abstractions.ITestCollection), default(System.Collections.Generic.IEnumerable), default(Xunit.Abstractions.IMessageSink), default(Xunit.Sdk.IMessageBus), default(Xunit.Sdk.ITestCaseOrderer), default(Xunit.Sdk.ExceptionAggregator), default(System.Threading.CancellationTokenSource)) { } - [System.Diagnostics.DebuggerStepThroughAttribute] - protected override System.Threading.Tasks.Task AfterTestCollectionStartingAsync() { throw null; } - protected override System.Threading.Tasks.Task BeforeTestCollectionFinishedAsync() { throw null; } - protected override System.Threading.Tasks.Task RunTestClassAsync(Xunit.Abstractions.ITestClass testClass, Xunit.Abstractions.IReflectionTypeInfo @class, System.Collections.Generic.IEnumerable testCases) { throw null; } - } - public partial class AspNetTestFramework : Xunit.Sdk.XunitTestFramework - { - public AspNetTestFramework(Xunit.Abstractions.IMessageSink messageSink) : base (default(Xunit.Abstractions.IMessageSink)) { } - protected override Xunit.Abstractions.ITestFrameworkExecutor CreateExecutor(System.Reflection.AssemblyName assemblyName) { throw null; } - } - public partial class AspNetTestFrameworkExecutor : Xunit.Sdk.XunitTestFrameworkExecutor - { - public AspNetTestFrameworkExecutor(System.Reflection.AssemblyName assemblyName, Xunit.Abstractions.ISourceInformationProvider sourceInformationProvider, Xunit.Abstractions.IMessageSink diagnosticMessageSink) : base (default(System.Reflection.AssemblyName), default(Xunit.Abstractions.ISourceInformationProvider), default(Xunit.Abstractions.IMessageSink)) { } - [System.Diagnostics.DebuggerStepThroughAttribute] - protected override void RunTestCases(System.Collections.Generic.IEnumerable testCases, Xunit.Abstractions.IMessageSink executionMessageSink, Xunit.Abstractions.ITestFrameworkExecutionOptions executionOptions) { } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Assembly, AllowMultiple=true)] - public partial class AssemblyFixtureAttribute : System.Attribute - { - public AssemblyFixtureAttribute(System.Type fixtureType) { } - public System.Type FixtureType { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Method, AllowMultiple=false)] - [Xunit.Sdk.XunitTestCaseDiscovererAttribute("Microsoft.AspNetCore.Testing.ConditionalFactDiscoverer", "Microsoft.AspNetCore.Testing")] - public partial class ConditionalFactAttribute : Xunit.FactAttribute - { - public ConditionalFactAttribute() { } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Method, AllowMultiple=false)] - [Xunit.Sdk.XunitTestCaseDiscovererAttribute("Microsoft.AspNetCore.Testing.ConditionalTheoryDiscoverer", "Microsoft.AspNetCore.Testing")] - public partial class ConditionalTheoryAttribute : Xunit.TheoryAttribute - { - public ConditionalTheoryAttribute() { } - } - public partial class CultureReplacer : System.IDisposable - { - public CultureReplacer(System.Globalization.CultureInfo culture, System.Globalization.CultureInfo uiCulture) { } - public CultureReplacer(string culture = "en-GB", string uiCulture = "en-US") { } - public static System.Globalization.CultureInfo DefaultCulture { get { throw null; } } - public static string DefaultCultureName { get { throw null; } } - public static string DefaultUICultureName { get { throw null; } } - public void Dispose() { } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Method, Inherited=true, AllowMultiple=false)] - public sealed partial class DockerOnlyAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition - { - public DockerOnlyAttribute() { } - public bool IsMet { get { throw null; } } - public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=true)] - public partial class EnvironmentVariableSkipConditionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition - { - public EnvironmentVariableSkipConditionAttribute(string variableName, params string[] values) { } - public bool IsMet { get { throw null; } } - public bool RunOnMatch { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } - public string SkipReason { get { throw null; } } - } - public static partial class ExceptionAssert - { - public static System.ArgumentException ThrowsArgument(System.Action testCode, string paramName, string exceptionMessage) { throw null; } - public static System.Threading.Tasks.Task ThrowsArgumentAsync(System.Func testCode, string paramName, string exceptionMessage) { throw null; } - public static System.ArgumentNullException ThrowsArgumentNull(System.Action testCode, string paramName) { throw null; } - public static System.ArgumentException ThrowsArgumentNullOrEmpty(System.Action testCode, string paramName) { throw null; } - public static System.Threading.Tasks.Task ThrowsArgumentNullOrEmptyAsync(System.Func testCode, string paramName) { throw null; } - public static System.ArgumentException ThrowsArgumentNullOrEmptyString(System.Action testCode, string paramName) { throw null; } - public static System.Threading.Tasks.Task ThrowsArgumentNullOrEmptyStringAsync(System.Func testCode, string paramName) { throw null; } - public static System.ArgumentOutOfRangeException ThrowsArgumentOutOfRange(System.Action testCode, string paramName, string exceptionMessage, object actualValue = null) { throw null; } - [System.Diagnostics.DebuggerStepThroughAttribute] - public static System.Threading.Tasks.Task ThrowsAsync(System.Func testCode, string exceptionMessage) where TException : System.Exception { throw null; } - public static TException Throws(System.Action testCode) where TException : System.Exception { throw null; } - public static TException Throws(System.Action testCode, string exceptionMessage) where TException : System.Exception { throw null; } - public static TException Throws(System.Func testCode, string exceptionMessage) where TException : System.Exception { throw null; } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method)] - [Xunit.Sdk.TraitDiscovererAttribute("Microsoft.AspNetCore.Testing.FlakyTraitDiscoverer", "Microsoft.AspNetCore.Testing")] - public sealed partial class FlakyAttribute : System.Attribute, Xunit.Sdk.ITraitAttribute - { - public FlakyAttribute(string gitHubIssueUrl, string firstFilter, params string[] additionalFilters) { } - public System.Collections.Generic.IReadOnlyList Filters { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - public string GitHubIssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - } - public static partial class FlakyOn - { - public const string All = "All"; - public static partial class AzP - { - public const string All = "AzP:All"; - public const string Linux = "AzP:OS:Linux"; - public const string macOS = "AzP:OS:Darwin"; - public const string Windows = "AzP:OS:Windows_NT"; - } - public static partial class Helix - { - public const string All = "Helix:Queue:All"; - public const string Centos7Amd64 = "Helix:Queue:Centos.7.Amd64.Open"; - public const string Debian8Amd64 = "Helix:Queue:Debian.8.Amd64.Open"; - public const string Debian9Amd64 = "Helix:Queue:Debian.9.Amd64.Open"; - public const string Fedora27Amd64 = "Helix:Queue:Fedora.27.Amd64.Open"; - public const string Fedora28Amd64 = "Helix:Queue:Fedora.28.Amd64.Open"; - public const string macOS1012Amd64 = "Helix:Queue:OSX.1012.Amd64.Open"; - public const string Redhat7Amd64 = "Helix:Queue:Redhat.7.Amd64.Open"; - public const string Ubuntu1604Amd64 = "Helix:Queue:Ubuntu.1604.Amd64.Open"; - public const string Ubuntu1810Amd64 = "Helix:Queue:Ubuntu.1810.Amd64.Open"; - public const string Windows10Amd64 = "Helix:Queue:Windows.10.Amd64.ClientRS4.VS2017.Open"; - } - } - public partial class FlakyTraitDiscoverer : Xunit.Sdk.ITraitDiscoverer - { - public FlakyTraitDiscoverer() { } - public System.Collections.Generic.IEnumerable> GetTraits(Xunit.Abstractions.IAttributeInfo traitAttribute) { throw null; } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Method, AllowMultiple=false)] - public partial class FrameworkSkipConditionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition - { - public FrameworkSkipConditionAttribute(Microsoft.AspNetCore.Testing.RuntimeFrameworks excludedFrameworks) { } - public bool IsMet { get { throw null; } } - public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } - } - public static partial class HelixQueues - { - public const string Centos7Amd64 = "Centos.7.Amd64.Open"; - public const string Debian8Amd64 = "Debian.8.Amd64.Open"; - public const string Debian9Amd64 = "Debian.9.Amd64.Open"; - public const string Fedora27Amd64 = "Fedora.27.Amd64.Open"; - public const string Fedora28Amd64 = "Fedora.28.Amd64.Open"; - public const string macOS1012Amd64 = "OSX.1012.Amd64.Open"; - public const string Redhat7Amd64 = "Redhat.7.Amd64.Open"; - public const string Ubuntu1604Amd64 = "Ubuntu.1604.Amd64.Open"; - public const string Ubuntu1810Amd64 = "Ubuntu.1810.Amd64.Open"; - public const string Windows10Amd64 = "Windows.10.Amd64.ClientRS4.VS2017.Open"; - } - public static partial class HttpClientSlim - { - [System.Diagnostics.DebuggerStepThroughAttribute] - public static System.Threading.Tasks.Task GetSocket(System.Uri requestUri) { throw null; } - [System.Diagnostics.DebuggerStepThroughAttribute] - public static System.Threading.Tasks.Task GetStringAsync(string requestUri, bool validateCertificate = true) { throw null; } - [System.Diagnostics.DebuggerStepThroughAttribute] - public static System.Threading.Tasks.Task GetStringAsync(System.Uri requestUri, bool validateCertificate = true) { throw null; } - [System.Diagnostics.DebuggerStepThroughAttribute] - public static System.Threading.Tasks.Task PostAsync(string requestUri, System.Net.Http.HttpContent content, bool validateCertificate = true) { throw null; } - [System.Diagnostics.DebuggerStepThroughAttribute] - public static System.Threading.Tasks.Task PostAsync(System.Uri requestUri, System.Net.Http.HttpContent content, bool validateCertificate = true) { throw null; } - } - public partial interface ITestCondition - { - bool IsMet { get; } - string SkipReason { get; } - } - public partial interface ITestMethodLifecycle - { - System.Threading.Tasks.Task OnTestEndAsync(Microsoft.AspNetCore.Testing.TestContext context, System.Exception exception, System.Threading.CancellationToken cancellationToken); - System.Threading.Tasks.Task OnTestStartAsync(Microsoft.AspNetCore.Testing.TestContext context, System.Threading.CancellationToken cancellationToken); - } - [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=true)] - public partial class MaximumOSVersionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition - { - public MaximumOSVersionAttribute(Microsoft.AspNetCore.Testing.OperatingSystems operatingSystem, string maxVersion) { } - public bool IsMet { get { throw null; } } - public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=true)] - public partial class MinimumOSVersionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition - { - public MinimumOSVersionAttribute(Microsoft.AspNetCore.Testing.OperatingSystems operatingSystem, string minVersion) { } - public bool IsMet { get { throw null; } } - public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } - } - [System.FlagsAttribute] - public enum OperatingSystems - { - Linux = 1, - MacOSX = 2, - Windows = 4, - } - [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=true)] - public partial class OSSkipConditionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition - { - public OSSkipConditionAttribute(Microsoft.AspNetCore.Testing.OperatingSystems operatingSystem) { } - [System.ObsoleteAttribute("Use the Minimum/MaximumOSVersionAttribute for version checks.", true)] - public OSSkipConditionAttribute(Microsoft.AspNetCore.Testing.OperatingSystems operatingSystem, params string[] versions) { } - public bool IsMet { get { throw null; } } - public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=false)] - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] - public partial class RepeatAttribute : System.Attribute - { - public RepeatAttribute(int runCount = 10) { } - public int RunCount { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - } - public partial class RepeatContext - { - public RepeatContext(int limit) { } - public static Microsoft.AspNetCore.Testing.RepeatContext Current { get { throw null; } } - public int CurrentIteration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } - public int Limit { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Method)] - public partial class ReplaceCultureAttribute : Xunit.Sdk.BeforeAfterTestAttribute - { - public ReplaceCultureAttribute() { } - public ReplaceCultureAttribute(string currentCulture, string currentUICulture) { } - public System.Globalization.CultureInfo Culture { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - public System.Globalization.CultureInfo UICulture { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - public override void After(System.Reflection.MethodInfo methodUnderTest) { } - public override void Before(System.Reflection.MethodInfo methodUnderTest) { } - } - [System.FlagsAttribute] - public enum RuntimeFrameworks - { - None = 0, - Mono = 1, - CLR = 2, - CoreCLR = 4, - } - [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class, AllowMultiple=false)] - public partial class ShortClassNameAttribute : System.Attribute - { - public ShortClassNameAttribute() { } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=false)] - public partial class SkipOnCIAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition - { - public SkipOnCIAttribute(string issueUrl = "") { } - public bool IsMet { get { throw null; } } - public string IssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - public string SkipReason { get { throw null; } } - public static string GetIfOnAzdo() { throw null; } - public static string GetTargetHelixQueue() { throw null; } - public static bool OnAzdo() { throw null; } - public static bool OnCI() { throw null; } - public static bool OnHelix() { throw null; } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=false)] - public partial class SkipOnHelixAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition - { - public SkipOnHelixAttribute(string issueUrl) { } - public bool IsMet { get { throw null; } } - public string IssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - public string Queues { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } - public string SkipReason { get { throw null; } } - public static string GetTargetHelixQueue() { throw null; } - public static bool OnHelix() { throw null; } - } - public partial class SkippedTestCase : Xunit.Sdk.XunitTestCase - { - [System.ObsoleteAttribute("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")] - public SkippedTestCase() { } - public SkippedTestCase(string skipReason, Xunit.Abstractions.IMessageSink diagnosticMessageSink, Xunit.Sdk.TestMethodDisplay defaultMethodDisplay, Xunit.Sdk.TestMethodDisplayOptions defaultMethodDisplayOptions, Xunit.Abstractions.ITestMethod testMethod, object[] testMethodArguments = null) { } - public override void Deserialize(Xunit.Abstractions.IXunitSerializationInfo data) { } - protected override string GetSkipReason(Xunit.Abstractions.IAttributeInfo factAttribute) { throw null; } - public override void Serialize(Xunit.Abstractions.IXunitSerializationInfo data) { } - } - public static partial class TaskExtensions - { - [System.Diagnostics.DebuggerStepThroughAttribute] - public static System.Threading.Tasks.Task TimeoutAfter(this System.Threading.Tasks.Task task, System.TimeSpan timeout, [System.Runtime.CompilerServices.CallerFilePathAttribute] string filePath = null, [System.Runtime.CompilerServices.CallerLineNumberAttribute] int lineNumber = 0) { throw null; } - [System.Diagnostics.DebuggerStepThroughAttribute] - public static System.Threading.Tasks.Task TimeoutAfter(this System.Threading.Tasks.Task task, System.TimeSpan timeout, [System.Runtime.CompilerServices.CallerFilePathAttribute] string filePath = null, [System.Runtime.CompilerServices.CallerLineNumberAttribute] int lineNumber = 0) { throw null; } - } - public sealed partial class TestContext - { - public TestContext(System.Type testClass, object[] constructorArguments, System.Reflection.MethodInfo testMethod, object[] methodArguments, Xunit.Abstractions.ITestOutputHelper output) { } - public object[] ConstructorArguments { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - public Microsoft.AspNetCore.Testing.TestFileOutputContext FileOutput { get { throw null; } } - public object[] MethodArguments { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - public Xunit.Abstractions.ITestOutputHelper Output { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - public System.Type TestClass { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - public System.Reflection.MethodInfo TestMethod { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - } - public sealed partial class TestFileOutputContext - { - public TestFileOutputContext(Microsoft.AspNetCore.Testing.TestContext parent) { } - public string AssemblyOutputDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - public string TestClassName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - public string TestClassOutputDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - public string TestName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - public static string GetAssemblyBaseDirectory(System.Reflection.Assembly assembly, string baseDirectory = null) { throw null; } - public static string GetOutputDirectory(System.Reflection.Assembly assembly) { throw null; } - public static bool GetPreserveExistingLogsInOutput(System.Reflection.Assembly assembly) { throw null; } - public static string GetTestClassName(System.Type type) { throw null; } - public static string GetTestMethodName(System.Reflection.MethodInfo method, object[] arguments) { throw null; } - public string GetUniqueFileName(string prefix, string extension) { throw null; } - public static string RemoveIllegalFileChars(string s) { throw null; } - } - public static partial class TestMethodExtensions - { - public static string EvaluateSkipConditions(this Xunit.Abstractions.ITestMethod testMethod) { throw null; } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Assembly, AllowMultiple=false, Inherited=true)] - public partial class TestOutputDirectoryAttribute : System.Attribute - { - public TestOutputDirectoryAttribute(string preserveExistingLogsInOutput, string targetFramework, string baseDirectory = null) { } - public string BaseDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - public bool PreserveExistingLogsInOutput { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - public string TargetFramework { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - } - [System.ObsoleteAttribute("This API is obsolete and the pattern its usage encouraged should not be used anymore. See https://github.com/dotnet/extensions/issues/1697 for details.")] - public partial class TestPathUtilities - { - public TestPathUtilities() { } - public static string GetRepoRootDirectory() { throw null; } - public static string GetSolutionRootDirectory(string solution) { throw null; } - } - public static partial class TestPlatformHelper - { - public static bool IsLinux { get { throw null; } } - public static bool IsMac { get { throw null; } } - public static bool IsMono { get { throw null; } } - public static bool IsWindows { get { throw null; } } - } - public static partial class WindowsVersions - { - public const string Win10 = "10.0"; - public const string Win10_19H1 = "10.0.18362"; - public const string Win10_19H2 = "10.0.18363"; - public const string Win10_20H1 = "10.0.19033"; - public const string Win10_RS4 = "10.0.17134"; - public const string Win10_RS5 = "10.0.17763"; - [System.ObsoleteAttribute("Use Win7 instead.", true)] - public const string Win2008R2 = "6.1"; - public const string Win7 = "6.1"; - public const string Win8 = "6.2"; - public const string Win81 = "6.3"; - } -} -namespace Microsoft.AspNetCore.Testing.Tracing -{ - public partial class CollectingEventListener : System.Diagnostics.Tracing.EventListener - { - public CollectingEventListener() { } - public void CollectFrom(System.Diagnostics.Tracing.EventSource eventSource) { } - public void CollectFrom(string eventSourceName) { } - public System.Collections.Generic.IReadOnlyList GetEventsWritten() { throw null; } - protected override void OnEventSourceCreated(System.Diagnostics.Tracing.EventSource eventSource) { } - protected override void OnEventWritten(System.Diagnostics.Tracing.EventWrittenEventArgs eventData) { } - } - public partial class EventAssert - { - public EventAssert(int expectedId, string expectedName, System.Diagnostics.Tracing.EventLevel expectedLevel) { } - public static void Collection(System.Collections.Generic.IEnumerable events, params Microsoft.AspNetCore.Testing.Tracing.EventAssert[] asserts) { } - public static Microsoft.AspNetCore.Testing.Tracing.EventAssert Event(int id, string name, System.Diagnostics.Tracing.EventLevel level) { throw null; } - public Microsoft.AspNetCore.Testing.Tracing.EventAssert Payload(string name, System.Action asserter) { throw null; } - public Microsoft.AspNetCore.Testing.Tracing.EventAssert Payload(string name, object expectedValue) { throw null; } - } - [Xunit.CollectionAttribute("Microsoft.AspNetCore.Testing.Tracing.EventSourceTestCollection")] - public abstract partial class EventSourceTestBase : System.IDisposable - { - public const string CollectionName = "Microsoft.AspNetCore.Testing.Tracing.EventSourceTestCollection"; - public EventSourceTestBase() { } - protected void CollectFrom(System.Diagnostics.Tracing.EventSource eventSource) { } - protected void CollectFrom(string eventSourceName) { } - public void Dispose() { } - protected System.Collections.Generic.IReadOnlyList GetEvents() { throw null; } - } -} diff --git a/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs b/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs deleted file mode 100644 index 17a922a6c7b7..000000000000 --- a/src/Testing/ref/Microsoft.AspNetCore.Testing.netstandard2.0.cs +++ /dev/null @@ -1,374 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -namespace Microsoft.AspNetCore.Testing -{ - public partial class AspNetTestAssemblyRunner : Xunit.Sdk.XunitTestAssemblyRunner - { - public AspNetTestAssemblyRunner(Xunit.Abstractions.ITestAssembly testAssembly, System.Collections.Generic.IEnumerable testCases, Xunit.Abstractions.IMessageSink diagnosticMessageSink, Xunit.Abstractions.IMessageSink executionMessageSink, Xunit.Abstractions.ITestFrameworkExecutionOptions executionOptions) : base (default(Xunit.Abstractions.ITestAssembly), default(System.Collections.Generic.IEnumerable), default(Xunit.Abstractions.IMessageSink), default(Xunit.Abstractions.IMessageSink), default(Xunit.Abstractions.ITestFrameworkExecutionOptions)) { } - [System.Diagnostics.DebuggerStepThroughAttribute] - protected override System.Threading.Tasks.Task AfterTestAssemblyStartingAsync() { throw null; } - protected override System.Threading.Tasks.Task BeforeTestAssemblyFinishedAsync() { throw null; } - protected override System.Threading.Tasks.Task RunTestCollectionAsync(Xunit.Sdk.IMessageBus messageBus, Xunit.Abstractions.ITestCollection testCollection, System.Collections.Generic.IEnumerable testCases, System.Threading.CancellationTokenSource cancellationTokenSource) { throw null; } - } - public partial class AspNetTestCollectionRunner : Xunit.Sdk.XunitTestCollectionRunner - { - public AspNetTestCollectionRunner(System.Collections.Generic.Dictionary assemblyFixtureMappings, Xunit.Abstractions.ITestCollection testCollection, System.Collections.Generic.IEnumerable testCases, Xunit.Abstractions.IMessageSink diagnosticMessageSink, Xunit.Sdk.IMessageBus messageBus, Xunit.Sdk.ITestCaseOrderer testCaseOrderer, Xunit.Sdk.ExceptionAggregator aggregator, System.Threading.CancellationTokenSource cancellationTokenSource) : base (default(Xunit.Abstractions.ITestCollection), default(System.Collections.Generic.IEnumerable), default(Xunit.Abstractions.IMessageSink), default(Xunit.Sdk.IMessageBus), default(Xunit.Sdk.ITestCaseOrderer), default(Xunit.Sdk.ExceptionAggregator), default(System.Threading.CancellationTokenSource)) { } - [System.Diagnostics.DebuggerStepThroughAttribute] - protected override System.Threading.Tasks.Task AfterTestCollectionStartingAsync() { throw null; } - protected override System.Threading.Tasks.Task BeforeTestCollectionFinishedAsync() { throw null; } - protected override System.Threading.Tasks.Task RunTestClassAsync(Xunit.Abstractions.ITestClass testClass, Xunit.Abstractions.IReflectionTypeInfo @class, System.Collections.Generic.IEnumerable testCases) { throw null; } - } - public partial class AspNetTestFramework : Xunit.Sdk.XunitTestFramework - { - public AspNetTestFramework(Xunit.Abstractions.IMessageSink messageSink) : base (default(Xunit.Abstractions.IMessageSink)) { } - protected override Xunit.Abstractions.ITestFrameworkExecutor CreateExecutor(System.Reflection.AssemblyName assemblyName) { throw null; } - } - public partial class AspNetTestFrameworkExecutor : Xunit.Sdk.XunitTestFrameworkExecutor - { - public AspNetTestFrameworkExecutor(System.Reflection.AssemblyName assemblyName, Xunit.Abstractions.ISourceInformationProvider sourceInformationProvider, Xunit.Abstractions.IMessageSink diagnosticMessageSink) : base (default(System.Reflection.AssemblyName), default(Xunit.Abstractions.ISourceInformationProvider), default(Xunit.Abstractions.IMessageSink)) { } - [System.Diagnostics.DebuggerStepThroughAttribute] - protected override void RunTestCases(System.Collections.Generic.IEnumerable testCases, Xunit.Abstractions.IMessageSink executionMessageSink, Xunit.Abstractions.ITestFrameworkExecutionOptions executionOptions) { } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Assembly, AllowMultiple=true)] - public partial class AssemblyFixtureAttribute : System.Attribute - { - public AssemblyFixtureAttribute(System.Type fixtureType) { } - public System.Type FixtureType { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Method, AllowMultiple=false)] - [Xunit.Sdk.XunitTestCaseDiscovererAttribute("Microsoft.AspNetCore.Testing.ConditionalFactDiscoverer", "Microsoft.AspNetCore.Testing")] - public partial class ConditionalFactAttribute : Xunit.FactAttribute - { - public ConditionalFactAttribute() { } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Method, AllowMultiple=false)] - [Xunit.Sdk.XunitTestCaseDiscovererAttribute("Microsoft.AspNetCore.Testing.ConditionalTheoryDiscoverer", "Microsoft.AspNetCore.Testing")] - public partial class ConditionalTheoryAttribute : Xunit.TheoryAttribute - { - public ConditionalTheoryAttribute() { } - } - public partial class CultureReplacer : System.IDisposable - { - public CultureReplacer(System.Globalization.CultureInfo culture, System.Globalization.CultureInfo uiCulture) { } - public CultureReplacer(string culture = "en-GB", string uiCulture = "en-US") { } - public static System.Globalization.CultureInfo DefaultCulture { get { throw null; } } - public static string DefaultCultureName { get { throw null; } } - public static string DefaultUICultureName { get { throw null; } } - public void Dispose() { } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Method, Inherited=true, AllowMultiple=false)] - public sealed partial class DockerOnlyAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition - { - public DockerOnlyAttribute() { } - public bool IsMet { get { throw null; } } - public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=true)] - public partial class EnvironmentVariableSkipConditionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition - { - public EnvironmentVariableSkipConditionAttribute(string variableName, params string[] values) { } - public bool IsMet { get { throw null; } } - public bool RunOnMatch { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } - public string SkipReason { get { throw null; } } - } - public static partial class ExceptionAssert - { - public static System.ArgumentException ThrowsArgument(System.Action testCode, string paramName, string exceptionMessage) { throw null; } - public static System.Threading.Tasks.Task ThrowsArgumentAsync(System.Func testCode, string paramName, string exceptionMessage) { throw null; } - public static System.ArgumentNullException ThrowsArgumentNull(System.Action testCode, string paramName) { throw null; } - public static System.ArgumentException ThrowsArgumentNullOrEmpty(System.Action testCode, string paramName) { throw null; } - public static System.Threading.Tasks.Task ThrowsArgumentNullOrEmptyAsync(System.Func testCode, string paramName) { throw null; } - public static System.ArgumentException ThrowsArgumentNullOrEmptyString(System.Action testCode, string paramName) { throw null; } - public static System.Threading.Tasks.Task ThrowsArgumentNullOrEmptyStringAsync(System.Func testCode, string paramName) { throw null; } - public static System.ArgumentOutOfRangeException ThrowsArgumentOutOfRange(System.Action testCode, string paramName, string exceptionMessage, object actualValue = null) { throw null; } - [System.Diagnostics.DebuggerStepThroughAttribute] - public static System.Threading.Tasks.Task ThrowsAsync(System.Func testCode, string exceptionMessage) where TException : System.Exception { throw null; } - public static TException Throws(System.Action testCode) where TException : System.Exception { throw null; } - public static TException Throws(System.Action testCode, string exceptionMessage) where TException : System.Exception { throw null; } - public static TException Throws(System.Func testCode, string exceptionMessage) where TException : System.Exception { throw null; } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method)] - [Xunit.Sdk.TraitDiscovererAttribute("Microsoft.AspNetCore.Testing.FlakyTraitDiscoverer", "Microsoft.AspNetCore.Testing")] - public sealed partial class FlakyAttribute : System.Attribute, Xunit.Sdk.ITraitAttribute - { - public FlakyAttribute(string gitHubIssueUrl, string firstFilter, params string[] additionalFilters) { } - public System.Collections.Generic.IReadOnlyList Filters { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - public string GitHubIssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - } - public static partial class FlakyOn - { - public const string All = "All"; - public static partial class AzP - { - public const string All = "AzP:All"; - public const string Linux = "AzP:OS:Linux"; - public const string macOS = "AzP:OS:Darwin"; - public const string Windows = "AzP:OS:Windows_NT"; - } - public static partial class Helix - { - public const string All = "Helix:Queue:All"; - public const string Centos7Amd64 = "Helix:Queue:Centos.7.Amd64.Open"; - public const string Debian8Amd64 = "Helix:Queue:Debian.8.Amd64.Open"; - public const string Debian9Amd64 = "Helix:Queue:Debian.9.Amd64.Open"; - public const string Fedora27Amd64 = "Helix:Queue:Fedora.27.Amd64.Open"; - public const string Fedora28Amd64 = "Helix:Queue:Fedora.28.Amd64.Open"; - public const string macOS1012Amd64 = "Helix:Queue:OSX.1012.Amd64.Open"; - public const string Redhat7Amd64 = "Helix:Queue:Redhat.7.Amd64.Open"; - public const string Ubuntu1604Amd64 = "Helix:Queue:Ubuntu.1604.Amd64.Open"; - public const string Ubuntu1810Amd64 = "Helix:Queue:Ubuntu.1810.Amd64.Open"; - public const string Windows10Amd64 = "Helix:Queue:Windows.10.Amd64.ClientRS4.VS2017.Open"; - } - } - public partial class FlakyTraitDiscoverer : Xunit.Sdk.ITraitDiscoverer - { - public FlakyTraitDiscoverer() { } - public System.Collections.Generic.IEnumerable> GetTraits(Xunit.Abstractions.IAttributeInfo traitAttribute) { throw null; } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Method, AllowMultiple=false)] - public partial class FrameworkSkipConditionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition - { - public FrameworkSkipConditionAttribute(Microsoft.AspNetCore.Testing.RuntimeFrameworks excludedFrameworks) { } - public bool IsMet { get { throw null; } } - public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } - } - public static partial class HelixQueues - { - public const string Centos7Amd64 = "Centos.7.Amd64.Open"; - public const string Debian8Amd64 = "Debian.8.Amd64.Open"; - public const string Debian9Amd64 = "Debian.9.Amd64.Open"; - public const string Fedora27Amd64 = "Fedora.27.Amd64.Open"; - public const string Fedora28Amd64 = "Fedora.28.Amd64.Open"; - public const string macOS1012Amd64 = "OSX.1012.Amd64.Open"; - public const string Redhat7Amd64 = "Redhat.7.Amd64.Open"; - public const string Ubuntu1604Amd64 = "Ubuntu.1604.Amd64.Open"; - public const string Ubuntu1810Amd64 = "Ubuntu.1810.Amd64.Open"; - public const string Windows10Amd64 = "Windows.10.Amd64.ClientRS4.VS2017.Open"; - } - public static partial class HttpClientSlim - { - [System.Diagnostics.DebuggerStepThroughAttribute] - public static System.Threading.Tasks.Task GetSocket(System.Uri requestUri) { throw null; } - [System.Diagnostics.DebuggerStepThroughAttribute] - public static System.Threading.Tasks.Task GetStringAsync(string requestUri, bool validateCertificate = true) { throw null; } - [System.Diagnostics.DebuggerStepThroughAttribute] - public static System.Threading.Tasks.Task GetStringAsync(System.Uri requestUri, bool validateCertificate = true) { throw null; } - [System.Diagnostics.DebuggerStepThroughAttribute] - public static System.Threading.Tasks.Task PostAsync(string requestUri, System.Net.Http.HttpContent content, bool validateCertificate = true) { throw null; } - [System.Diagnostics.DebuggerStepThroughAttribute] - public static System.Threading.Tasks.Task PostAsync(System.Uri requestUri, System.Net.Http.HttpContent content, bool validateCertificate = true) { throw null; } - } - public partial interface ITestCondition - { - bool IsMet { get; } - string SkipReason { get; } - } - public partial interface ITestMethodLifecycle - { - System.Threading.Tasks.Task OnTestEndAsync(Microsoft.AspNetCore.Testing.TestContext context, System.Exception exception, System.Threading.CancellationToken cancellationToken); - System.Threading.Tasks.Task OnTestStartAsync(Microsoft.AspNetCore.Testing.TestContext context, System.Threading.CancellationToken cancellationToken); - } - [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=true)] - public partial class MaximumOSVersionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition - { - public MaximumOSVersionAttribute(Microsoft.AspNetCore.Testing.OperatingSystems operatingSystem, string maxVersion) { } - public bool IsMet { get { throw null; } } - public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=true)] - public partial class MinimumOSVersionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition - { - public MinimumOSVersionAttribute(Microsoft.AspNetCore.Testing.OperatingSystems operatingSystem, string minVersion) { } - public bool IsMet { get { throw null; } } - public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } - } - [System.FlagsAttribute] - public enum OperatingSystems - { - Linux = 1, - MacOSX = 2, - Windows = 4, - } - [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=true)] - public partial class OSSkipConditionAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition - { - public OSSkipConditionAttribute(Microsoft.AspNetCore.Testing.OperatingSystems operatingSystem) { } - [System.ObsoleteAttribute("Use the Minimum/MaximumOSVersionAttribute for version checks.", true)] - public OSSkipConditionAttribute(Microsoft.AspNetCore.Testing.OperatingSystems operatingSystem, params string[] versions) { } - public bool IsMet { get { throw null; } } - public string SkipReason { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=false)] - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] - public partial class RepeatAttribute : System.Attribute - { - public RepeatAttribute(int runCount = 10) { } - public int RunCount { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - } - public partial class RepeatContext - { - public RepeatContext(int limit) { } - public static Microsoft.AspNetCore.Testing.RepeatContext Current { get { throw null; } } - public int CurrentIteration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } - public int Limit { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Method)] - public partial class ReplaceCultureAttribute : Xunit.Sdk.BeforeAfterTestAttribute - { - public ReplaceCultureAttribute() { } - public ReplaceCultureAttribute(string currentCulture, string currentUICulture) { } - public System.Globalization.CultureInfo Culture { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - public System.Globalization.CultureInfo UICulture { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - public override void After(System.Reflection.MethodInfo methodUnderTest) { } - public override void Before(System.Reflection.MethodInfo methodUnderTest) { } - } - [System.FlagsAttribute] - public enum RuntimeFrameworks - { - None = 0, - Mono = 1, - CLR = 2, - CoreCLR = 4, - } - [System.AttributeUsageAttribute(System.AttributeTargets.Assembly | System.AttributeTargets.Class, AllowMultiple=false)] - public partial class ShortClassNameAttribute : System.Attribute - { - public ShortClassNameAttribute() { } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=false)] - public partial class SkipOnCIAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition - { - public SkipOnCIAttribute(string issueUrl = "") { } - public bool IsMet { get { throw null; } } - public string IssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - public string SkipReason { get { throw null; } } - public static string GetIfOnAzdo() { throw null; } - public static string GetTargetHelixQueue() { throw null; } - public static bool OnAzdo() { throw null; } - public static bool OnCI() { throw null; } - public static bool OnHelix() { throw null; } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=false)] - public partial class SkipOnHelixAttribute : System.Attribute, Microsoft.AspNetCore.Testing.ITestCondition - { - public SkipOnHelixAttribute(string issueUrl) { } - public bool IsMet { get { throw null; } } - public string IssueUrl { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - public string Queues { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } - public string SkipReason { get { throw null; } } - public static string GetTargetHelixQueue() { throw null; } - public static bool OnHelix() { throw null; } - } - public partial class SkippedTestCase : Xunit.Sdk.XunitTestCase - { - [System.ObsoleteAttribute("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")] - public SkippedTestCase() { } - public SkippedTestCase(string skipReason, Xunit.Abstractions.IMessageSink diagnosticMessageSink, Xunit.Sdk.TestMethodDisplay defaultMethodDisplay, Xunit.Sdk.TestMethodDisplayOptions defaultMethodDisplayOptions, Xunit.Abstractions.ITestMethod testMethod, object[] testMethodArguments = null) { } - public override void Deserialize(Xunit.Abstractions.IXunitSerializationInfo data) { } - protected override string GetSkipReason(Xunit.Abstractions.IAttributeInfo factAttribute) { throw null; } - public override void Serialize(Xunit.Abstractions.IXunitSerializationInfo data) { } - } - public static partial class TaskExtensions - { - [System.Diagnostics.DebuggerStepThroughAttribute] - public static System.Threading.Tasks.Task TimeoutAfter(this System.Threading.Tasks.Task task, System.TimeSpan timeout, [System.Runtime.CompilerServices.CallerFilePathAttribute] string filePath = null, [System.Runtime.CompilerServices.CallerLineNumberAttribute] int lineNumber = 0) { throw null; } - [System.Diagnostics.DebuggerStepThroughAttribute] - public static System.Threading.Tasks.Task TimeoutAfter(this System.Threading.Tasks.Task task, System.TimeSpan timeout, [System.Runtime.CompilerServices.CallerFilePathAttribute] string filePath = null, [System.Runtime.CompilerServices.CallerLineNumberAttribute] int lineNumber = 0) { throw null; } - } - public sealed partial class TestContext - { - public TestContext(System.Type testClass, object[] constructorArguments, System.Reflection.MethodInfo testMethod, object[] methodArguments, Xunit.Abstractions.ITestOutputHelper output) { } - public object[] ConstructorArguments { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - public Microsoft.AspNetCore.Testing.TestFileOutputContext FileOutput { get { throw null; } } - public object[] MethodArguments { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - public Xunit.Abstractions.ITestOutputHelper Output { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - public System.Type TestClass { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - public System.Reflection.MethodInfo TestMethod { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - } - public sealed partial class TestFileOutputContext - { - public TestFileOutputContext(Microsoft.AspNetCore.Testing.TestContext parent) { } - public string AssemblyOutputDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - public string TestClassName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - public string TestClassOutputDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - public string TestName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - public static string GetAssemblyBaseDirectory(System.Reflection.Assembly assembly, string baseDirectory = null) { throw null; } - public static string GetOutputDirectory(System.Reflection.Assembly assembly) { throw null; } - public static bool GetPreserveExistingLogsInOutput(System.Reflection.Assembly assembly) { throw null; } - public static string GetTestClassName(System.Type type) { throw null; } - public static string GetTestMethodName(System.Reflection.MethodInfo method, object[] arguments) { throw null; } - public string GetUniqueFileName(string prefix, string extension) { throw null; } - public static string RemoveIllegalFileChars(string s) { throw null; } - } - public static partial class TestMethodExtensions - { - public static string EvaluateSkipConditions(this Xunit.Abstractions.ITestMethod testMethod) { throw null; } - } - [System.AttributeUsageAttribute(System.AttributeTargets.Assembly, AllowMultiple=false, Inherited=true)] - public partial class TestOutputDirectoryAttribute : System.Attribute - { - public TestOutputDirectoryAttribute(string preserveExistingLogsInOutput, string targetFramework, string baseDirectory = null) { } - public string BaseDirectory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - public bool PreserveExistingLogsInOutput { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - public string TargetFramework { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } } - } - [System.ObsoleteAttribute("This API is obsolete and the pattern its usage encouraged should not be used anymore. See https://github.com/dotnet/extensions/issues/1697 for details.")] - public partial class TestPathUtilities - { - public TestPathUtilities() { } - public static string GetRepoRootDirectory() { throw null; } - public static string GetSolutionRootDirectory(string solution) { throw null; } - } - public static partial class TestPlatformHelper - { - public static bool IsLinux { get { throw null; } } - public static bool IsMac { get { throw null; } } - public static bool IsMono { get { throw null; } } - public static bool IsWindows { get { throw null; } } - } - public static partial class WindowsVersions - { - public const string Win10 = "10.0"; - public const string Win10_19H1 = "10.0.18362"; - public const string Win10_19H2 = "10.0.18363"; - public const string Win10_20H1 = "10.0.19033"; - public const string Win10_RS4 = "10.0.17134"; - public const string Win10_RS5 = "10.0.17763"; - [System.ObsoleteAttribute("Use Win7 instead.", true)] - public const string Win2008R2 = "6.1"; - public const string Win7 = "6.1"; - public const string Win8 = "6.2"; - public const string Win81 = "6.3"; - } -} -namespace Microsoft.AspNetCore.Testing.Tracing -{ - public partial class CollectingEventListener : System.Diagnostics.Tracing.EventListener - { - public CollectingEventListener() { } - public void CollectFrom(System.Diagnostics.Tracing.EventSource eventSource) { } - public void CollectFrom(string eventSourceName) { } - public System.Collections.Generic.IReadOnlyList GetEventsWritten() { throw null; } - protected override void OnEventSourceCreated(System.Diagnostics.Tracing.EventSource eventSource) { } - protected override void OnEventWritten(System.Diagnostics.Tracing.EventWrittenEventArgs eventData) { } - } - public partial class EventAssert - { - public EventAssert(int expectedId, string expectedName, System.Diagnostics.Tracing.EventLevel expectedLevel) { } - public static void Collection(System.Collections.Generic.IEnumerable events, params Microsoft.AspNetCore.Testing.Tracing.EventAssert[] asserts) { } - public static Microsoft.AspNetCore.Testing.Tracing.EventAssert Event(int id, string name, System.Diagnostics.Tracing.EventLevel level) { throw null; } - public Microsoft.AspNetCore.Testing.Tracing.EventAssert Payload(string name, System.Action asserter) { throw null; } - public Microsoft.AspNetCore.Testing.Tracing.EventAssert Payload(string name, object expectedValue) { throw null; } - } - [Xunit.CollectionAttribute("Microsoft.AspNetCore.Testing.Tracing.EventSourceTestCollection")] - public abstract partial class EventSourceTestBase : System.IDisposable - { - public const string CollectionName = "Microsoft.AspNetCore.Testing.Tracing.EventSourceTestCollection"; - public EventSourceTestBase() { } - protected void CollectFrom(System.Diagnostics.Tracing.EventSource eventSource) { } - protected void CollectFrom(string eventSourceName) { } - public void Dispose() { } - protected System.Collections.Generic.IReadOnlyList GetEvents() { throw null; } - } -} diff --git a/src/Testing/src/Microsoft.AspNetCore.Testing.csproj b/src/Testing/src/Microsoft.AspNetCore.Testing.csproj index 448351fee566..96336a47a8b0 100644 --- a/src/Testing/src/Microsoft.AspNetCore.Testing.csproj +++ b/src/Testing/src/Microsoft.AspNetCore.Testing.csproj @@ -9,7 +9,6 @@ false true - true true true diff --git a/src/Testing/test/TestPathUtilitiesTest.cs b/src/Testing/test/TestPathUtilitiesTest.cs index 024f476f07f6..ff3c18e5525d 100644 --- a/src/Testing/test/TestPathUtilitiesTest.cs +++ b/src/Testing/test/TestPathUtilitiesTest.cs @@ -12,7 +12,7 @@ public class TestPathUtilitiesTest // Entire test pending removal - see https://github.com/dotnet/extensions/issues/1697 #pragma warning disable 0618 - [Fact] + [Fact(Skip="https://github.com/dotnet/extensions/issues/1697")] public void GetSolutionRootDirectory_ResolvesSolutionRoot() { // Directory.GetCurrentDirectory() gives: diff --git a/src/Tools/GetDocumentInsider/src/GetDocumentInsider.csproj b/src/Tools/GetDocumentInsider/src/GetDocumentInsider.csproj index ebaffde463fd..7f61a987b348 100644 --- a/src/Tools/GetDocumentInsider/src/GetDocumentInsider.csproj +++ b/src/Tools/GetDocumentInsider/src/GetDocumentInsider.csproj @@ -15,10 +15,9 @@ + - - - + diff --git a/src/Tools/Microsoft.dotnet-openapi/src/Microsoft.dotnet-openapi.csproj b/src/Tools/Microsoft.dotnet-openapi/src/Microsoft.dotnet-openapi.csproj index 35e9c96c788c..78f7133975cb 100644 --- a/src/Tools/Microsoft.dotnet-openapi/src/Microsoft.dotnet-openapi.csproj +++ b/src/Tools/Microsoft.dotnet-openapi/src/Microsoft.dotnet-openapi.csproj @@ -11,13 +11,13 @@ + - diff --git a/src/Tools/dotnet-dev-certs/src/dotnet-dev-certs.csproj b/src/Tools/dotnet-dev-certs/src/dotnet-dev-certs.csproj index b8a4f38c0c5f..29f83b864305 100644 --- a/src/Tools/dotnet-dev-certs/src/dotnet-dev-certs.csproj +++ b/src/Tools/dotnet-dev-certs/src/dotnet-dev-certs.csproj @@ -13,12 +13,12 @@ + - diff --git a/src/Tools/dotnet-getdocument/src/dotnet-getdocument.csproj b/src/Tools/dotnet-getdocument/src/dotnet-getdocument.csproj index aa31d55d4e72..1ec6fe51ca29 100644 --- a/src/Tools/dotnet-getdocument/src/dotnet-getdocument.csproj +++ b/src/Tools/dotnet-getdocument/src/dotnet-getdocument.csproj @@ -12,6 +12,7 @@ + @@ -19,7 +20,6 @@ - diff --git a/src/Tools/dotnet-sql-cache/src/dotnet-sql-cache.csproj b/src/Tools/dotnet-sql-cache/src/dotnet-sql-cache.csproj index 6fc54a7a871a..1ff940b9f4e8 100644 --- a/src/Tools/dotnet-sql-cache/src/dotnet-sql-cache.csproj +++ b/src/Tools/dotnet-sql-cache/src/dotnet-sql-cache.csproj @@ -10,11 +10,11 @@ + - diff --git a/src/Tools/dotnet-user-secrets/src/dotnet-user-secrets.csproj b/src/Tools/dotnet-user-secrets/src/dotnet-user-secrets.csproj index 7fce951f0ca7..1ef774e1c5e7 100644 --- a/src/Tools/dotnet-user-secrets/src/dotnet-user-secrets.csproj +++ b/src/Tools/dotnet-user-secrets/src/dotnet-user-secrets.csproj @@ -14,13 +14,13 @@ + - diff --git a/src/Tools/dotnet-watch/src/dotnet-watch.csproj b/src/Tools/dotnet-watch/src/dotnet-watch.csproj index 3c209122117c..ee7fc09d1509 100644 --- a/src/Tools/dotnet-watch/src/dotnet-watch.csproj +++ b/src/Tools/dotnet-watch/src/dotnet-watch.csproj @@ -13,13 +13,10 @@ + - - - - diff --git a/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj b/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj index 364f4d3d8663..c4fbf99fa6d9 100644 --- a/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj +++ b/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj @@ -9,6 +9,7 @@ aspnetcore true true + true From a5fc9defc422350f9a51e81b7c4bed33f45f0f3e Mon Sep 17 00:00:00 2001 From: John Luo Date: Thu, 20 Feb 2020 16:53:07 -0800 Subject: [PATCH 180/183] Regenerate ref assemblies --- .../Microsoft.AspNetCore.Components.csproj | 14 ++++++---- .../ref/Microsoft.AspNetCore.Hosting.csproj | 26 ++++++++++--------- ...rosoft.AspNetCore.Http.Abstractions.csproj | 7 +++-- .../ref/Microsoft.AspNetCore.Routing.csproj | 13 +++++----- .../Microsoft.AspNetCore.Diagnostics.csproj | 18 +++++++------ ...crosoft.AspNetCore.Mvc.Abstractions.csproj | 6 +++-- .../ref/Microsoft.AspNetCore.Mvc.Core.csproj | 25 +++++++++--------- .../ref/Microsoft.AspNetCore.Razor.csproj | 4 ++- ...AspNetCore.Connections.Abstractions.csproj | 14 +++++----- .../Microsoft.AspNetCore.Server.IIS.csproj | 17 ++++++------ ...crosoft.AspNetCore.Http.Connections.csproj | 15 ++++++----- 11 files changed, 89 insertions(+), 70 deletions(-) diff --git a/src/Components/Components/ref/Microsoft.AspNetCore.Components.csproj b/src/Components/Components/ref/Microsoft.AspNetCore.Components.csproj index 8ccea268bd19..50f888c26a64 100644 --- a/src/Components/Components/ref/Microsoft.AspNetCore.Components.csproj +++ b/src/Components/Components/ref/Microsoft.AspNetCore.Components.csproj @@ -6,13 +6,17 @@ - - - + + + + + - - + + + + diff --git a/src/Hosting/Hosting/ref/Microsoft.AspNetCore.Hosting.csproj b/src/Hosting/Hosting/ref/Microsoft.AspNetCore.Hosting.csproj index 959abbb16089..72e71d1c3195 100644 --- a/src/Hosting/Hosting/ref/Microsoft.AspNetCore.Hosting.csproj +++ b/src/Hosting/Hosting/ref/Microsoft.AspNetCore.Hosting.csproj @@ -5,17 +5,19 @@ - - - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/src/Http/Http.Abstractions/ref/Microsoft.AspNetCore.Http.Abstractions.csproj b/src/Http/Http.Abstractions/ref/Microsoft.AspNetCore.Http.Abstractions.csproj index fc6109502d25..169218db016b 100644 --- a/src/Http/Http.Abstractions/ref/Microsoft.AspNetCore.Http.Abstractions.csproj +++ b/src/Http/Http.Abstractions/ref/Microsoft.AspNetCore.Http.Abstractions.csproj @@ -5,7 +5,10 @@ - - + + + + + diff --git a/src/Http/Routing/ref/Microsoft.AspNetCore.Routing.csproj b/src/Http/Routing/ref/Microsoft.AspNetCore.Routing.csproj index e8a4a831ba17..750e814c6933 100644 --- a/src/Http/Routing/ref/Microsoft.AspNetCore.Routing.csproj +++ b/src/Http/Routing/ref/Microsoft.AspNetCore.Routing.csproj @@ -5,11 +5,12 @@ - - - - - - + + + + + + + diff --git a/src/Middleware/Diagnostics/ref/Microsoft.AspNetCore.Diagnostics.csproj b/src/Middleware/Diagnostics/ref/Microsoft.AspNetCore.Diagnostics.csproj index eb5f581428e3..e11a9b7266f6 100644 --- a/src/Middleware/Diagnostics/ref/Microsoft.AspNetCore.Diagnostics.csproj +++ b/src/Middleware/Diagnostics/ref/Microsoft.AspNetCore.Diagnostics.csproj @@ -5,13 +5,15 @@ - - - - - - - - + + + + + + + + + + diff --git a/src/Mvc/Mvc.Abstractions/ref/Microsoft.AspNetCore.Mvc.Abstractions.csproj b/src/Mvc/Mvc.Abstractions/ref/Microsoft.AspNetCore.Mvc.Abstractions.csproj index aca1b44ad56e..ce0671621e4d 100644 --- a/src/Mvc/Mvc.Abstractions/ref/Microsoft.AspNetCore.Mvc.Abstractions.csproj +++ b/src/Mvc/Mvc.Abstractions/ref/Microsoft.AspNetCore.Mvc.Abstractions.csproj @@ -5,7 +5,9 @@ - - + + + + diff --git a/src/Mvc/Mvc.Core/ref/Microsoft.AspNetCore.Mvc.Core.csproj b/src/Mvc/Mvc.Core/ref/Microsoft.AspNetCore.Mvc.Core.csproj index 92acf1544ff2..89cb1de0c566 100644 --- a/src/Mvc/Mvc.Core/ref/Microsoft.AspNetCore.Mvc.Core.csproj +++ b/src/Mvc/Mvc.Core/ref/Microsoft.AspNetCore.Mvc.Core.csproj @@ -6,17 +6,18 @@ - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/src/Razor/Razor/ref/Microsoft.AspNetCore.Razor.csproj b/src/Razor/Razor/ref/Microsoft.AspNetCore.Razor.csproj index e32a58b3d22a..891462486402 100644 --- a/src/Razor/Razor/ref/Microsoft.AspNetCore.Razor.csproj +++ b/src/Razor/Razor/ref/Microsoft.AspNetCore.Razor.csproj @@ -5,6 +5,8 @@ - + + + diff --git a/src/Servers/Connections.Abstractions/ref/Microsoft.AspNetCore.Connections.Abstractions.csproj b/src/Servers/Connections.Abstractions/ref/Microsoft.AspNetCore.Connections.Abstractions.csproj index 52ba312b1931..262c012733c2 100644 --- a/src/Servers/Connections.Abstractions/ref/Microsoft.AspNetCore.Connections.Abstractions.csproj +++ b/src/Servers/Connections.Abstractions/ref/Microsoft.AspNetCore.Connections.Abstractions.csproj @@ -6,18 +6,18 @@ - - - + + + - - + + - - + + diff --git a/src/Servers/IIS/IIS/ref/Microsoft.AspNetCore.Server.IIS.csproj b/src/Servers/IIS/IIS/ref/Microsoft.AspNetCore.Server.IIS.csproj index 746230e19c86..a470dc92b4ba 100644 --- a/src/Servers/IIS/IIS/ref/Microsoft.AspNetCore.Server.IIS.csproj +++ b/src/Servers/IIS/IIS/ref/Microsoft.AspNetCore.Server.IIS.csproj @@ -6,13 +6,14 @@ - - - - - - - - + + + + + + + + + diff --git a/src/SignalR/common/Http.Connections/ref/Microsoft.AspNetCore.Http.Connections.csproj b/src/SignalR/common/Http.Connections/ref/Microsoft.AspNetCore.Http.Connections.csproj index 882787914093..f0707f786afb 100644 --- a/src/SignalR/common/Http.Connections/ref/Microsoft.AspNetCore.Http.Connections.csproj +++ b/src/SignalR/common/Http.Connections/ref/Microsoft.AspNetCore.Http.Connections.csproj @@ -5,12 +5,13 @@ - - - - - - - + + + + + + + + From 5f52370264f137ced39b46a926243d9f7525648d Mon Sep 17 00:00:00 2001 From: John Luo Date: Thu, 20 Feb 2020 18:12:00 -0800 Subject: [PATCH 181/183] Merge fixes --- eng/Build.props | 8 ++++++++ eng/ProjectReferences.props | 2 +- .../Microsoft.Extensions.Configuration.KeyPerFile.csproj | 1 - .../Microsoft.Extensions.FileProviders.Embedded.csproj | 1 - ...xtensions.Diagnostics.HealthChecks.Abstractions.csproj | 1 - .../Microsoft.Extensions.Diagnostics.HealthChecks.csproj | 1 - .../Microsoft.JSInterop/src/Microsoft.JSInterop.csproj | 1 - .../Microsoft.Extensions.Localization.Abstractions.csproj | 1 - .../src/Microsoft.Extensions.Localization.csproj | 1 - src/ObjectPool/src/Microsoft.Extensions.ObjectPool.csproj | 1 - .../src/Microsoft.Extensions.WebEncoders.csproj | 1 - 11 files changed, 9 insertions(+), 10 deletions(-) diff --git a/eng/Build.props b/eng/Build.props index 05533d91f41b..4c0542db15da 100644 --- a/eng/Build.props +++ b/eng/Build.props @@ -190,6 +190,14 @@ $(RepoRoot)src\Azure\**\src\*.csproj; $(RepoRoot)src\SignalR\**\src\*.csproj; $(RepoRoot)src\Components\**\src\*.csproj; + $(RepoRoot)src\FileProviders\**\src\*.csproj; + $(RepoRoot)src\Configuration.KeyPerFile\**\src\*.csproj; + $(RepoRoot)src\Localization\**\src\*.csproj; + $(RepoRoot)src\ObjectPool\**\src\*.csproj; + $(RepoRoot)src\JSInterop\**\src\*.csproj; + $(RepoRoot)src\WebEncoders\**\src\*.csproj; + $(RepoRoot)src\HealthChecks\**\src\*.csproj; + $(RepoRoot)src\Testing\**\src\*.csproj; " Exclude=" @(ProjectToBuild); diff --git a/eng/ProjectReferences.props b/eng/ProjectReferences.props index 61d87526d837..f71e3d1d72a7 100644 --- a/eng/ProjectReferences.props +++ b/eng/ProjectReferences.props @@ -63,6 +63,7 @@ + @@ -152,6 +153,5 @@ - diff --git a/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj b/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj index b8b281716e5b..af1ecb684fe7 100644 --- a/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj +++ b/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj @@ -5,7 +5,6 @@ netstandard2.0;$(DefaultNetCoreTargetFramework) $(DefaultNetCoreTargetFramework) true - true true true configuration diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj index 97d9600f1312..13000f081449 100644 --- a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj @@ -8,7 +8,6 @@ $(DefaultNetCoreTargetFramework) $(MSBuildProjectName).netcoreapp.nuspec true - true true true files;filesystem diff --git a/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj b/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj index fac2cf9a99ee..659c39adc1bb 100644 --- a/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj +++ b/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj @@ -13,7 +13,6 @@ Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck true diagnostics;healthchecks true - true true diff --git a/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj b/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj index 9e89a9e06e10..a737e01d4547 100644 --- a/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj +++ b/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj @@ -12,7 +12,6 @@ Microsoft.Extensions.Diagnostics.HealthChecks.IHealthChecksBuilder true diagnostics;healthchecks true - true true diff --git a/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj b/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj index 83acae807fe5..2c8e56a9eb05 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj +++ b/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj @@ -7,7 +7,6 @@ javascript;interop true true - true true diff --git a/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj b/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj index 92139f6145fd..4bf8ba532987 100644 --- a/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj +++ b/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj @@ -12,7 +12,6 @@ Microsoft.Extensions.Localization.IStringLocalizer<T> true localization true - true true diff --git a/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj b/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj index f69b533885e8..c3ffbeb772d7 100644 --- a/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj +++ b/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj @@ -9,7 +9,6 @@ true localization true - true true diff --git a/src/ObjectPool/src/Microsoft.Extensions.ObjectPool.csproj b/src/ObjectPool/src/Microsoft.Extensions.ObjectPool.csproj index ab3be847adf7..b2cc5954ce25 100644 --- a/src/ObjectPool/src/Microsoft.Extensions.ObjectPool.csproj +++ b/src/ObjectPool/src/Microsoft.Extensions.ObjectPool.csproj @@ -7,7 +7,6 @@ true pooling true - true true diff --git a/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj b/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj index c4fbf99fa6d9..9b8efbd16f5e 100644 --- a/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj +++ b/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj @@ -8,7 +8,6 @@ true aspnetcore true - true true From 2f6cdcf6672a11e7d5d65077d5b13ea182b0148a Mon Sep 17 00:00:00 2001 From: John Luo Date: Thu, 20 Feb 2020 21:58:16 -0800 Subject: [PATCH 182/183] More merge fixes --- ...Extensions.Configuration.KeyPerFile.csproj | 9 +++++---- ...t.Extensions.FileProviders.Embedded.csproj | 7 +++++-- ...agnostics.HealthChecks.Abstractions.csproj | 3 +-- ...Extensions.Diagnostics.HealthChecks.csproj | 15 +++++++++------ .../ref/Microsoft.JSInterop.csproj | 6 ++++-- ...xtensions.Localization.Abstractions.csproj | 3 +-- .../Microsoft.Extensions.Localization.csproj | 19 +++++++++++-------- .../Microsoft.Extensions.ObjectPool.csproj | 5 +++-- .../Microsoft.AspNetCore.Shared.Tests.csproj | 1 - .../Microsoft.Extensions.WebEncoders.csproj | 11 ++++++----- 10 files changed, 45 insertions(+), 34 deletions(-) diff --git a/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj b/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj index 8dbe6d3ba11c..b554afa71551 100644 --- a/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj +++ b/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj @@ -2,15 +2,16 @@ netstandard2.0;$(DefaultNetCoreTargetFramework) + $(DefaultNetCoreTargetFramework) - - + + - - + + diff --git a/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.csproj b/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.csproj index 89cba5ba5723..fcca2039f78a 100644 --- a/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.csproj +++ b/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.csproj @@ -2,13 +2,16 @@ netstandard2.0;$(DefaultNetCoreTargetFramework) + $(DefaultNetCoreTargetFramework) - + + - + + diff --git a/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj index 6b67a0868621..8be8d278dbd4 100644 --- a/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj +++ b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj @@ -2,13 +2,12 @@ netstandard2.0;$(DefaultNetCoreTargetFramework) + $(DefaultNetCoreTargetFramework) - - diff --git a/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj b/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj index 83dae521fbbf..d6b79361e735 100644 --- a/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj +++ b/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj @@ -2,17 +2,20 @@ netstandard2.0;$(DefaultNetCoreTargetFramework) + $(DefaultNetCoreTargetFramework) - - - + + + + - - - + + + + diff --git a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj index ceb24636ab4d..52192f210074 100644 --- a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj +++ b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj @@ -2,13 +2,15 @@ netstandard2.0;$(DefaultNetCoreTargetFramework) + $(DefaultNetCoreTargetFramework) - + + - + diff --git a/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.csproj b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.csproj index af97cae5940d..a6ffa6ada607 100644 --- a/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.csproj +++ b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.csproj @@ -2,13 +2,12 @@ netstandard2.0;$(DefaultNetCoreTargetFramework) + $(DefaultNetCoreTargetFramework) - - diff --git a/src/Localization/Localization/ref/Microsoft.Extensions.Localization.csproj b/src/Localization/Localization/ref/Microsoft.Extensions.Localization.csproj index 67e8f38f8949..b20502c89315 100644 --- a/src/Localization/Localization/ref/Microsoft.Extensions.Localization.csproj +++ b/src/Localization/Localization/ref/Microsoft.Extensions.Localization.csproj @@ -2,19 +2,22 @@ netstandard2.0;$(DefaultNetCoreTargetFramework) + $(DefaultNetCoreTargetFramework) - - - - + + + + + - - - - + + + + + diff --git a/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.csproj b/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.csproj index be298e7ae324..2755e1d1cf69 100644 --- a/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.csproj +++ b/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.csproj @@ -2,13 +2,14 @@ netstandard2.0;$(DefaultNetCoreTargetFramework) + $(DefaultNetCoreTargetFramework) - + - + diff --git a/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj b/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj index a18aa0246b0a..bc6eb225c123 100644 --- a/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj +++ b/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj @@ -12,7 +12,6 @@ - diff --git a/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj b/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj index 5beee97dd6fd..4fbb9b15e3e8 100644 --- a/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj +++ b/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj @@ -2,16 +2,17 @@ netstandard2.0;$(DefaultNetCoreTargetFramework) + $(DefaultNetCoreTargetFramework) - - - + + + - - + + From b27670c72df66186e4a35eb190d63c8fd2ad64f3 Mon Sep 17 00:00:00 2001 From: John Luo Date: Fri, 21 Feb 2020 11:55:19 -0800 Subject: [PATCH 183/183] Fix ref pack after merge --- src/Framework/ref/Microsoft.AspNetCore.App.Ref.csproj | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Framework/ref/Microsoft.AspNetCore.App.Ref.csproj b/src/Framework/ref/Microsoft.AspNetCore.App.Ref.csproj index ffd0f18293fa..a1898152e22b 100644 --- a/src/Framework/ref/Microsoft.AspNetCore.App.Ref.csproj +++ b/src/Framework/ref/Microsoft.AspNetCore.App.Ref.csproj @@ -117,6 +117,12 @@ This package is an internal implementation of the .NET Core SDK and is not meant + + + + + + @@ -126,12 +132,15 @@ This package is an internal implementation of the .NET Core SDK and is not meant + + <_ReferencedExtensionsRefAssembliesToExclude Include="@(_ReferencedExtensionsRefAssemblies)" Exclude="@(_DuplicatedExtensionsRefAssemblies)" /> +