Add an example
This commit is contained in:
parent
9dfe2a0a81
commit
ae925049bb
|
|
@ -21,15 +21,22 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
|
|||
[Export(typeof(VisualStudioDocumentTrackerFactory))]
|
||||
internal class DefaultVisualStudioDocumentTrackerFactory : VisualStudioDocumentTrackerFactory, IWpfTextViewConnectionListener
|
||||
{
|
||||
private readonly ForegroundDispatcher _foregroundDispatcher;
|
||||
private readonly ProjectSnapshotManager _projectManager;
|
||||
private readonly TextBufferProjectService _projectService;
|
||||
private readonly Workspace _workspace;
|
||||
|
||||
[ImportingConstructor]
|
||||
public DefaultVisualStudioDocumentTrackerFactory(
|
||||
ForegroundDispatcher foregroundDispatcher,
|
||||
TextBufferProjectService projectService,
|
||||
[Import(typeof(VisualStudioWorkspace))] Workspace workspace)
|
||||
{
|
||||
if (foregroundDispatcher == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(foregroundDispatcher));
|
||||
}
|
||||
|
||||
if (projectService == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectService));
|
||||
|
|
@ -39,7 +46,8 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
|
|||
{
|
||||
throw new ArgumentNullException(nameof(workspace));
|
||||
}
|
||||
|
||||
|
||||
_foregroundDispatcher = foregroundDispatcher;
|
||||
_projectService = projectService;
|
||||
_workspace = workspace;
|
||||
|
||||
|
|
@ -48,10 +56,16 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
|
|||
|
||||
// This is only for testing. We want to avoid using the actual Roslyn GetService methods in unit tests.
|
||||
internal DefaultVisualStudioDocumentTrackerFactory(
|
||||
ForegroundDispatcher foregroundDispatcher,
|
||||
ProjectSnapshotManager projectManager,
|
||||
TextBufferProjectService projectService,
|
||||
[Import(typeof(VisualStudioWorkspace))] Workspace workspace)
|
||||
{
|
||||
if (foregroundDispatcher == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(foregroundDispatcher));
|
||||
}
|
||||
|
||||
if (projectManager == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectManager));
|
||||
|
|
@ -67,6 +81,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
|
|||
throw new ArgumentNullException(nameof(workspace));
|
||||
}
|
||||
|
||||
_foregroundDispatcher = foregroundDispatcher;
|
||||
_projectManager = projectManager;
|
||||
_projectService = projectService;
|
||||
_workspace = workspace;
|
||||
|
|
@ -81,6 +96,8 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
|
|||
throw new ArgumentNullException(nameof(textView));
|
||||
}
|
||||
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
// While it's definitely possible to have multiple Razor text buffers attached to the same text view, there's
|
||||
// no real scenario for it. This method always returns the tracker for the first Razor text buffer, but the
|
||||
// other functionality for this class will maintain state correctly for the other buffers.
|
||||
|
|
@ -113,7 +130,9 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
|
|||
{
|
||||
throw new ArgumentNullException(nameof(subjectBuffers));
|
||||
}
|
||||
|
||||
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
for (var i = 0; i < subjectBuffers.Count; i++)
|
||||
{
|
||||
var textBuffer = subjectBuffers[i];
|
||||
|
|
@ -148,6 +167,8 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
|
|||
throw new ArgumentNullException(nameof(subjectBuffers));
|
||||
}
|
||||
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
// This means a Razor buffer has be detached from this ITextView or the ITextView is closing. Since we keep a
|
||||
// list of all of the open text views for each text buffer, we need to update the tracker.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ using Xunit;
|
|||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
|
||||
{
|
||||
public class DefaultVisualStudioDocumentTrackerFactoryTest
|
||||
public class DefaultVisualStudioDocumentTrackerFactoryTest : ForegroundDispatcherTestBase
|
||||
{
|
||||
private ProjectSnapshotManager ProjectManager { get; } = Mock.Of<ProjectSnapshotManager>(
|
||||
p => p.FindProject(It.IsAny<string>()) == Mock.Of<ProjectSnapshot>() &&
|
||||
|
|
@ -32,11 +32,11 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
|
|||
|
||||
private IContentType NonRazorContentType { get; } = Mock.Of<IContentType>(c => c.IsOfType(It.IsAny<string>()) == false);
|
||||
|
||||
[Fact]
|
||||
[ForegroundFact]
|
||||
public void SubjectBuffersConnected_ForNonRazorTextBuffer_DoesNothing()
|
||||
{
|
||||
// Arrange
|
||||
var factory = new DefaultVisualStudioDocumentTrackerFactory(ProjectManager, ProjectService, Workspace);
|
||||
var factory = new DefaultVisualStudioDocumentTrackerFactory(Dispatcher, ProjectManager, ProjectService, Workspace);
|
||||
|
||||
var textView = Mock.Of<IWpfTextView>();
|
||||
|
||||
|
|
@ -52,11 +52,11 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
|
|||
Assert.False(buffers[0].Properties.ContainsProperty(typeof(VisualStudioDocumentTracker)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ForegroundFact]
|
||||
public void SubjectBuffersConnected_ForRazorTextBufferWithoutTracker_CreatesTrackerAndTracksTextView()
|
||||
{
|
||||
// Arrange
|
||||
var factory = new DefaultVisualStudioDocumentTrackerFactory(ProjectManager, ProjectService, Workspace);
|
||||
var factory = new DefaultVisualStudioDocumentTrackerFactory(Dispatcher, ProjectManager, ProjectService, Workspace);
|
||||
|
||||
var textView = Mock.Of<IWpfTextView>();
|
||||
|
||||
|
|
@ -74,11 +74,11 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
|
|||
Assert.Equal(buffers[0], tracker.TextBuffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ForegroundFact]
|
||||
public void SubjectBuffersConnected_ForRazorTextBufferWithoutTracker_CreatesTrackerAndTracksTextView_ForMultipleBuffers()
|
||||
{
|
||||
// Arrange
|
||||
var factory = new DefaultVisualStudioDocumentTrackerFactory(ProjectManager, ProjectService, Workspace);
|
||||
var factory = new DefaultVisualStudioDocumentTrackerFactory(Dispatcher, ProjectManager, ProjectService, Workspace);
|
||||
|
||||
var textView = Mock.Of<IWpfTextView>();
|
||||
|
||||
|
|
@ -104,11 +104,11 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
|
|||
Assert.Equal(buffers[2], tracker.TextBuffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ForegroundFact]
|
||||
public void SubjectBuffersConnected_ForRazorTextBufferWithTracker_DoesNotAddDuplicateTextViewEntry()
|
||||
{
|
||||
// Arrange
|
||||
var factory = new DefaultVisualStudioDocumentTrackerFactory(ProjectManager, ProjectService, Workspace);
|
||||
var factory = new DefaultVisualStudioDocumentTrackerFactory(Dispatcher, ProjectManager, ProjectService, Workspace);
|
||||
|
||||
var textView = Mock.Of<IWpfTextView>();
|
||||
|
||||
|
|
@ -130,11 +130,11 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
|
|||
Assert.Collection(tracker.TextViews, v => Assert.Same(v, textView));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ForegroundFact]
|
||||
public void SubjectBuffersConnected_ForRazorTextBufferWithTracker_AddsEntryForADifferentTextView()
|
||||
{
|
||||
// Arrange
|
||||
var factory = new DefaultVisualStudioDocumentTrackerFactory(ProjectManager, ProjectService, Workspace);
|
||||
var factory = new DefaultVisualStudioDocumentTrackerFactory(Dispatcher, ProjectManager, ProjectService, Workspace);
|
||||
|
||||
var textView1 = Mock.Of<IWpfTextView>();
|
||||
var textView2 = Mock.Of<IWpfTextView>();
|
||||
|
|
@ -157,11 +157,11 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
|
|||
Assert.Collection(tracker.TextViews, v => Assert.Same(v, textView1), v => Assert.Same(v, textView2));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ForegroundFact]
|
||||
public void SubjectBuffersDisconnected_ForAnyTextBufferWithTracker_RemovesTextView()
|
||||
{
|
||||
// Arrange
|
||||
var factory = new DefaultVisualStudioDocumentTrackerFactory(ProjectManager, ProjectService, Workspace);
|
||||
var factory = new DefaultVisualStudioDocumentTrackerFactory(Dispatcher, ProjectManager, ProjectService, Workspace);
|
||||
|
||||
var textView1 = Mock.Of<IWpfTextView>();
|
||||
var textView2 = Mock.Of<IWpfTextView>();
|
||||
|
|
@ -194,11 +194,11 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
|
|||
Assert.Collection(tracker.TextViews, v => Assert.Same(v, textView1));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ForegroundFact]
|
||||
public void SubjectBuffersDisconnected_ForAnyTextBufferWithoutTracker_DoesNothing()
|
||||
{
|
||||
// Arrange
|
||||
var factory = new DefaultVisualStudioDocumentTrackerFactory(ProjectManager, ProjectService, Workspace);
|
||||
var factory = new DefaultVisualStudioDocumentTrackerFactory(Dispatcher, ProjectManager, ProjectService, Workspace);
|
||||
|
||||
var textView = Mock.Of<IWpfTextView>();
|
||||
|
||||
|
|
@ -214,11 +214,11 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
|
|||
Assert.False(buffers[0].Properties.ContainsProperty(typeof(VisualStudioDocumentTracker)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ForegroundFact]
|
||||
public void GetTracker_ForRazorTextBufferWithTracker_ReturnsTheFirstTracker()
|
||||
{
|
||||
// Arrange
|
||||
var factory = new DefaultVisualStudioDocumentTrackerFactory(ProjectManager, ProjectService, Workspace);
|
||||
var factory = new DefaultVisualStudioDocumentTrackerFactory(Dispatcher, ProjectManager, ProjectService, Workspace);
|
||||
|
||||
var buffers = new Collection<ITextBuffer>()
|
||||
{
|
||||
|
|
@ -241,11 +241,11 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
|
|||
Assert.Same(tracker, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ForegroundFact]
|
||||
public void GetTracker_WithoutRazorBuffer_ReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
var factory = new DefaultVisualStudioDocumentTrackerFactory(ProjectManager, ProjectService, Workspace);
|
||||
var factory = new DefaultVisualStudioDocumentTrackerFactory(Dispatcher, ProjectManager, ProjectService, Workspace);
|
||||
|
||||
var buffers = new Collection<ITextBuffer>();
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ namespace Xunit
|
|||
{
|
||||
// Similar to WpfFactAttribute https://github.com/xunit/samples.xunit/blob/969d9f7e887836f01a6c525324bf3db55658c28f/STAExamples/WpfFactAttribute.cs
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
||||
[XunitTestCaseDiscoverer("Xunit.ForegroundFactAttribute", "Microsoft.VisualStudio.LanguageServices.Razor")]
|
||||
[XunitTestCaseDiscoverer("Xunit.ForegroundFactDiscoverer", "Microsoft.VisualStudio.LanguageServices.Razor.Test")]
|
||||
internal class ForegroundFactAttribute : FactAttribute
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,117 +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.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Threading;
|
||||
using System.Linq;
|
||||
using Xunit.Abstractions;
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace Xunit
|
||||
{
|
||||
internal class ForegroundFactTestCase : LongLivedMarshalByRefObject, IXunitTestCase
|
||||
internal class ForegroundFactDiscoverer : IXunitTestCaseDiscoverer
|
||||
{
|
||||
private IXunitTestCase _inner;
|
||||
readonly FactDiscoverer _inner;
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[Obsolete("Called by the de-serializer", error: true)]
|
||||
public ForegroundFactTestCase()
|
||||
public ForegroundFactDiscoverer(IMessageSink diagnosticMessageSink)
|
||||
{
|
||||
_inner = new FactDiscoverer(diagnosticMessageSink);
|
||||
}
|
||||
|
||||
public ForegroundFactTestCase(IXunitTestCase testCase)
|
||||
public IEnumerable<IXunitTestCase> Discover(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo factAttribute)
|
||||
{
|
||||
_inner = testCase;
|
||||
}
|
||||
|
||||
public string DisplayName => _inner.DisplayName;
|
||||
|
||||
public IMethodInfo Method => _inner.Method;
|
||||
|
||||
public string SkipReason => _inner.SkipReason;
|
||||
|
||||
public ISourceInformation SourceInformation
|
||||
{
|
||||
get => _inner.SourceInformation;
|
||||
set => _inner.SourceInformation = value;
|
||||
}
|
||||
|
||||
public ITestMethod TestMethod => _inner.TestMethod;
|
||||
|
||||
public object[] TestMethodArguments => _inner.TestMethodArguments;
|
||||
|
||||
public Dictionary<string, List<string>> Traits => _inner.Traits;
|
||||
|
||||
public string UniqueID => _inner.UniqueID;
|
||||
|
||||
public void Deserialize(IXunitSerializationInfo info)
|
||||
{
|
||||
_inner = info.GetValue<IXunitTestCase>("InnerTestCase");
|
||||
}
|
||||
|
||||
public void Serialize(IXunitSerializationInfo info)
|
||||
{
|
||||
info.AddValue("InnerTestCase", _inner);
|
||||
}
|
||||
|
||||
public Task<RunSummary> RunAsync(
|
||||
IMessageSink diagnosticMessageSink,
|
||||
IMessageBus messageBus,
|
||||
object[] constructorArguments,
|
||||
ExceptionAggregator aggregator,
|
||||
CancellationTokenSource cancellationTokenSource)
|
||||
{
|
||||
var tcs = new TaskCompletionSource<RunSummary>();
|
||||
var thread = new Thread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext());
|
||||
|
||||
var worker = _inner.RunAsync(diagnosticMessageSink, messageBus, constructorArguments, aggregator, cancellationTokenSource);
|
||||
|
||||
Exception caught = null;
|
||||
var frame = new DispatcherFrame();
|
||||
Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await worker;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
caught = ex;
|
||||
}
|
||||
finally
|
||||
{
|
||||
frame.Continue = false;
|
||||
}
|
||||
});
|
||||
|
||||
Dispatcher.PushFrame(frame);
|
||||
|
||||
if (caught == null)
|
||||
{
|
||||
tcs.SetException(caught);
|
||||
}
|
||||
else
|
||||
{
|
||||
tcs.SetResult(worker.Result);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
tcs.SetException(e);
|
||||
}
|
||||
});
|
||||
|
||||
thread.SetApartmentState(ApartmentState.STA);
|
||||
thread.Start();
|
||||
|
||||
return tcs.Task;
|
||||
return _inner.Discover(discoveryOptions, testMethod, factAttribute).Select(t => new ForegroundFactTestCase(t));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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.ComponentModel;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Threading;
|
||||
using Xunit.Abstractions;
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace Xunit
|
||||
{
|
||||
internal class ForegroundFactTestCase : LongLivedMarshalByRefObject, IXunitTestCase
|
||||
{
|
||||
private IXunitTestCase _inner;
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[Obsolete("Called by the de-serializer", error: true)]
|
||||
public ForegroundFactTestCase()
|
||||
{
|
||||
}
|
||||
|
||||
public ForegroundFactTestCase(IXunitTestCase testCase)
|
||||
{
|
||||
_inner = testCase;
|
||||
}
|
||||
|
||||
public string DisplayName => _inner.DisplayName;
|
||||
|
||||
public IMethodInfo Method => _inner.Method;
|
||||
|
||||
public string SkipReason => _inner.SkipReason;
|
||||
|
||||
public ISourceInformation SourceInformation
|
||||
{
|
||||
get => _inner.SourceInformation;
|
||||
set => _inner.SourceInformation = value;
|
||||
}
|
||||
|
||||
public ITestMethod TestMethod => _inner.TestMethod;
|
||||
|
||||
public object[] TestMethodArguments => _inner.TestMethodArguments;
|
||||
|
||||
public Dictionary<string, List<string>> Traits => _inner.Traits;
|
||||
|
||||
public string UniqueID => _inner.UniqueID;
|
||||
|
||||
public void Deserialize(IXunitSerializationInfo info)
|
||||
{
|
||||
_inner = info.GetValue<IXunitTestCase>("InnerTestCase");
|
||||
}
|
||||
|
||||
public void Serialize(IXunitSerializationInfo info)
|
||||
{
|
||||
info.AddValue("InnerTestCase", _inner);
|
||||
}
|
||||
|
||||
public Task<RunSummary> RunAsync(
|
||||
IMessageSink diagnosticMessageSink,
|
||||
IMessageBus messageBus,
|
||||
object[] constructorArguments,
|
||||
ExceptionAggregator aggregator,
|
||||
CancellationTokenSource cancellationTokenSource)
|
||||
{
|
||||
var tcs = new TaskCompletionSource<RunSummary>();
|
||||
var thread = new Thread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext());
|
||||
|
||||
var worker = _inner.RunAsync(diagnosticMessageSink, messageBus, constructorArguments, aggregator, cancellationTokenSource);
|
||||
|
||||
Exception caught = null;
|
||||
var frame = new DispatcherFrame();
|
||||
Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await worker;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
caught = ex;
|
||||
}
|
||||
finally
|
||||
{
|
||||
frame.Continue = false;
|
||||
}
|
||||
});
|
||||
|
||||
Dispatcher.PushFrame(frame);
|
||||
|
||||
if (caught == null)
|
||||
{
|
||||
tcs.SetResult(worker.Result);
|
||||
}
|
||||
else
|
||||
{
|
||||
tcs.SetException(caught);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
tcs.SetException(e);
|
||||
}
|
||||
});
|
||||
|
||||
thread.SetApartmentState(ApartmentState.STA);
|
||||
thread.Start();
|
||||
|
||||
return tcs.Task;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue