未验证 提交 18d4c934 编写于 作者: S Sam Harwell 提交者: GitHub

Merge pull request #33971 from sharwell/test-perf

Improve test performance
......@@ -33,9 +33,8 @@ internal abstract class AbstractCommandHandlerTestState : IDisposable
XElement workspaceElement,
IList<Type> excludedTypes = null,
ComposableCatalog extraParts = null,
bool useMinimumCatalog = false,
string workspaceKind = null)
: this(workspaceElement, GetExportProvider(useMinimumCatalog, excludedTypes, extraParts), workspaceKind)
: this(workspaceElement, GetExportProvider(excludedTypes, extraParts), workspaceKind)
{
}
......@@ -131,20 +130,16 @@ public T GetService<T>()
return Workspace.GetService<T>();
}
private static ExportProvider GetExportProvider(bool useMinimumCatalog, IList<Type> excludedTypes, ComposableCatalog extraParts)
internal static ExportProvider GetExportProvider(IList<Type> excludedTypes, ComposableCatalog extraParts)
{
excludedTypes = excludedTypes ?? Type.EmptyTypes;
if (excludedTypes.Count == 0 && (extraParts == null || extraParts.Parts.Count == 0))
{
return useMinimumCatalog
? TestExportProvider.MinimumExportProviderFactoryWithCSharpAndVisualBasic.CreateExportProvider()
: TestExportProvider.ExportProviderFactoryWithCSharpAndVisualBasic.CreateExportProvider();
return TestExportProvider.ExportProviderFactoryWithCSharpAndVisualBasic.CreateExportProvider();
}
var baseCatalog = useMinimumCatalog
? TestExportProvider.MinimumCatalogWithCSharpAndVisualBasic
: TestExportProvider.EntireAssemblyCatalogWithCSharpAndVisualBasic;
var baseCatalog = TestExportProvider.EntireAssemblyCatalogWithCSharpAndVisualBasic;
var filteredCatalog = baseCatalog.WithoutPartsOfTypes(excludedTypes);
......
......@@ -116,11 +116,6 @@ private static Type[] GetNeutralAndCSharpAndVisualBasicTypes()
.ToArray();
}
private static IExportProviderFactory CreateExportProviderFactoryWithCSharpAndVisualBasic()
{
return ExportProviderCache.GetOrCreateExportProviderFactory(EntireAssemblyCatalogWithCSharpAndVisualBasic);
}
private static ComposableCatalog CreateAssemblyCatalogWithCSharpAndVisualBasic()
{
return ExportProviderCache
......
// Copyright (c) Microsoft. 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.Windows.Threading;
namespace Roslyn.Test.Utilities
{
public sealed class StaTaskScheduler : IDisposable
{
/// <summary>Gets a <see cref="StaTaskScheduler"/> for the current <see cref="AppDomain"/>.</summary>
/// <remarks>We use a count of 1, because the editor ends up re-using <see cref="DispatcherObject"/>
/// instances between tests, so we need to always use the same thread for our Sta tests.</remarks>
public static StaTaskScheduler DefaultSta { get; } = new StaTaskScheduler();
/// <summary>The STA threads used by the scheduler.</summary>
public Thread StaThread { get; }
public bool IsRunningInScheduler => StaThread.ManagedThreadId == Thread.CurrentThread.ManagedThreadId;
/// <summary>Initializes a new instance of the <see cref="StaTaskScheduler"/> class.</summary>
public StaTaskScheduler()
{
using (var threadStartedEvent = new ManualResetEventSlim(initialState: false))
{
DispatcherSynchronizationContext synchronizationContext = null;
StaThread = new Thread(() =>
{
var oldContext = SynchronizationContext.Current;
try
{
// All WPF Tests need a DispatcherSynchronizationContext and we dont want to block pending keyboard
// or mouse input from the user. So use background priority which is a single level below user input.
synchronizationContext = new DispatcherSynchronizationContext();
// xUnit creates its own synchronization context and wraps any existing context so that messages are
// still pumped as necessary. So we are safe setting it here, where we are not safe setting it in test.
SynchronizationContext.SetSynchronizationContext(synchronizationContext);
threadStartedEvent.Set();
Dispatcher.Run();
}
finally
{
SynchronizationContext.SetSynchronizationContext(oldContext);
}
});
StaThread.Name = $"{nameof(StaTaskScheduler)} thread";
StaThread.IsBackground = true;
StaThread.SetApartmentState(ApartmentState.STA);
StaThread.Start();
threadStartedEvent.Wait();
DispatcherSynchronizationContext = synchronizationContext;
}
// Work around the WeakEventTable Shutdown race conditions
AppContext.SetSwitch("Switch.MS.Internal.DoNotInvokeInWeakEventTableShutdownListener", isEnabled: true);
}
public DispatcherSynchronizationContext DispatcherSynchronizationContext
{
get;
}
/// <summary>
/// Cleans up the scheduler by indicating that no more tasks will be queued.
/// This method blocks until all threads successfully shutdown.
/// </summary>
public void Dispose()
{
if (StaThread.IsAlive)
{
DispatcherSynchronizationContext.Post(_ => Dispatcher.ExitAllFrames(), null);
StaThread.Join();
}
}
}
}
......@@ -25,11 +25,6 @@ namespace Roslyn.Test.Utilities
/// </summary>
public sealed class WpfTestRunner : XunitTestRunner
{
/// <summary>
/// A long timeout used to avoid hangs in tests, where a test failure manifests as an operation never occurring.
/// </summary>
private static readonly TimeSpan HangMitigatingTimeout = TimeSpan.FromMinutes(1);
private static string s_wpfFactRequirementReason;
public WpfTestSharedData SharedData { get; }
......@@ -54,71 +49,33 @@ public sealed class WpfTestRunner : XunitTestRunner
protected override Task<decimal> InvokeTestMethodAsync(ExceptionAggregator aggregator)
{
SharedData.ExecutingTest(TestMethod);
DispatcherSynchronizationContext synchronizationContext = null;
Dispatcher dispatcher = null;
Thread staThread;
using (var staThreadStartedEvent = new ManualResetEventSlim(initialState: false))
{
staThread = new Thread((ThreadStart)(() =>
{
// All WPF Tests need a DispatcherSynchronizationContext and we dont want to block pending keyboard
// or mouse input from the user. So use background priority which is a single level below user input.
synchronizationContext = new DispatcherSynchronizationContext();
dispatcher = Dispatcher.CurrentDispatcher;
// xUnit creates its own synchronization context and wraps any existing context so that messages are
// still pumped as necessary. So we are safe setting it here, where we are not safe setting it in test.
SynchronizationContext.SetSynchronizationContext(synchronizationContext);
staThreadStartedEvent.Set();
Dispatcher.Run();
}));
staThread.Name = $"{nameof(WpfTestRunner)} {TestMethod.Name}";
staThread.SetApartmentState(ApartmentState.STA);
staThread.Start();
staThreadStartedEvent.Wait();
Debug.Assert(synchronizationContext != null);
}
var taskScheduler = new SynchronizationContextTaskScheduler(synchronizationContext);
var sta = StaTaskScheduler.DefaultSta;
var task = Task.Factory.StartNew(async () =>
{
Debug.Assert(SynchronizationContext.Current is DispatcherSynchronizationContext);
Debug.Assert(sta.StaThread == Thread.CurrentThread);
using (await SharedData.TestSerializationGate.DisposableWaitAsync(CancellationToken.None))
{
// Reset our flag ensuring that part of this test actually needs WpfFact
s_wpfFactRequirementReason = null;
// Just call back into the normal xUnit dispatch process now that we are on an STA Thread with no synchronization context.
var invoker = new XunitTestInvoker(Test, MessageBus, TestClass, ConstructorArguments, TestMethod, TestMethodArguments, BeforeAfterAttributes, aggregator, CancellationTokenSource);
return await invoker.RunAsync();
}
}, CancellationTokenSource.Token, TaskCreationOptions.None, taskScheduler).Unwrap();
return Task.Run(
async () =>
{
try
{
return await task.ConfigureAwait(false);
Debug.Assert(SynchronizationContext.Current is DispatcherSynchronizationContext);
// Reset our flag ensuring that part of this test actually needs WpfFact
s_wpfFactRequirementReason = null;
// Just call back into the normal xUnit dispatch process now that we are on an STA Thread with no synchronization context.
var invoker = new XunitTestInvoker(Test, MessageBus, TestClass, ConstructorArguments, TestMethod, TestMethodArguments, BeforeAfterAttributes, aggregator, CancellationTokenSource);
return invoker.RunAsync().JoinUsingDispatcher(CancellationTokenSource.Token);
}
finally
{
// Make sure to shut down the dispatcher. Certain framework types listed for the dispatcher
// shutdown to perform cleanup actions. In the absence of an explicit shutdown, these actions
// are delayed and run during AppDomain or process shutdown, where they can lead to crashes of
// the test process.
dispatcher.InvokeShutdown();
// Join the STA thread, which ensures shutdown is complete.
staThread.Join(HangMitigatingTimeout);
// Cleanup the synchronization context even if the test is failing exceptionally
SynchronizationContext.SetSynchronizationContext(null);
}
});
}
}, CancellationTokenSource.Token, TaskCreationOptions.None, new SynchronizationContextTaskScheduler(sta.DispatcherSynchronizationContext));
return task.Unwrap();
}
/// <summary>
......
......@@ -13,6 +13,7 @@ Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces
Imports Microsoft.CodeAnalysis.SignatureHelp
Imports Microsoft.CodeAnalysis.Test.Utilities
Imports Microsoft.VisualStudio.Commanding
Imports Microsoft.VisualStudio.Composition
Imports Microsoft.VisualStudio.Language.Intellisense
Imports Microsoft.VisualStudio.Text
Imports Microsoft.VisualStudio.Text.Editor
......@@ -35,6 +36,36 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense
Friend ReadOnly IntelliSenseCommandHandler As IntelliSenseCommandHandler
Private ReadOnly SessionTestState As IIntelliSenseTestState
Private Shared s_lazyEntireAssemblyCatalogWithCSharpAndVisualBasicWithoutCompletionTestParts As Lazy(Of ComposableCatalog) =
New Lazy(Of ComposableCatalog)(Function()
Return TestExportProvider.EntireAssemblyCatalogWithCSharpAndVisualBasic.
WithoutPartsOfTypes({
GetType(IIntelliSensePresenter(Of ICompletionPresenterSession, ICompletionSession)),
GetType(IIntelliSensePresenter(Of ISignatureHelpPresenterSession, ISignatureHelpSession)),
GetType(FormatCommandHandler)}).
WithParts({
GetType(TestCompletionPresenter),
GetType(TestSignatureHelpPresenter),
GetType(IntelliSenseTestState)})
End Function)
Private Shared ReadOnly Property EntireAssemblyCatalogWithCSharpAndVisualBasicWithoutCompletionTestParts As ComposableCatalog
Get
Return s_lazyEntireAssemblyCatalogWithCSharpAndVisualBasicWithoutCompletionTestParts.Value
End Get
End Property
Private Shared s_lazyExportProviderFactoryWithCSharpAndVisualBasicWithoutCompletionTestParts As Lazy(Of IExportProviderFactory) =
New Lazy(Of IExportProviderFactory)(Function()
Return ExportProviderCache.GetOrCreateExportProviderFactory(EntireAssemblyCatalogWithCSharpAndVisualBasicWithoutCompletionTestParts)
End Function)
Private Shared ReadOnly Property ExportProviderFactoryWithCSharpAndVisualBasicWithoutCompletionTestParts As IExportProviderFactory
Get
Return s_lazyExportProviderFactoryWithCSharpAndVisualBasicWithoutCompletionTestParts.Value
End Get
End Property
Friend ReadOnly Property CurrentSignatureHelpPresenterSession As TestSignatureHelpPresenterSession
Get
Return SessionTestState.CurrentSignatureHelpPresenterSession
......@@ -53,7 +84,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense
Optional extraExportedTypes As List(Of Type) = Nothing,
Optional includeFormatCommandHandler As Boolean = False,
Optional workspaceKind As String = Nothing)
MyBase.New(workspaceElement, CombineExcludedTypes(excludedTypes, includeFormatCommandHandler), ExportProviderCache.CreateTypeCatalog(CombineExtraTypes(If(extraExportedTypes, New List(Of Type)))), workspaceKind:=workspaceKind)
MyBase.New(workspaceElement, GetExportProvider(excludedTypes, extraExportedTypes, includeFormatCommandHandler), workspaceKind:=workspaceKind)
Dim languageServices = Me.Workspace.CurrentSolution.Projects.First().LanguageServices
Dim language = languageServices.Language
......@@ -84,6 +115,20 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense
Nothing)
End Sub
Private Overloads Shared Function GetExportProvider(excludedTypes As List(Of Type),
extraExportedTypes As List(Of Type),
includeFormatCommandHandler As Boolean) As ExportProvider
If (excludedTypes Is Nothing OrElse excludedTypes.Count = 0) AndAlso
(extraExportedTypes Is Nothing OrElse extraExportedTypes.Count = 0) AndAlso
Not includeFormatCommandHandler Then
Return ExportProviderFactoryWithCSharpAndVisualBasicWithoutCompletionTestParts.CreateExportProvider()
End If
Dim combinedExcludedTypes = CombineExcludedTypes(excludedTypes, includeFormatCommandHandler)
Dim extraParts = ExportProviderCache.CreateTypeCatalog(CombineExtraTypes(If(extraExportedTypes, New List(Of Type))))
Return GetExportProvider(combinedExcludedTypes, extraParts)
End Function
Private Shared Function CombineExcludedTypes(excludedTypes As IList(Of Type), includeFormatCommandHandler As Boolean) As IList(Of Type)
Dim result = New List(Of Type) From {
GetType(IIntelliSensePresenter(Of ICompletionPresenterSession, ICompletionSession)),
......
......@@ -23,7 +23,7 @@ internal sealed class EventHookupTestState : AbstractCommandHandlerTestState
private Mutex _testSessionHookupMutex;
public EventHookupTestState(XElement workspaceElement, IDictionary<OptionKey, object> options)
: base(workspaceElement, excludedTypes: null, GetExtraParts(), false)
: base(workspaceElement, excludedTypes: null, GetExtraParts())
{
_commandHandler = new EventHookupCommandHandler(
Workspace.ExportProvider.GetExportedValue<IThreadingContext>(),
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册