Update DefaultRazorEditorFactoryService to be per-workspace.

- Updated the a `VisualStudioWorkspaceAccessor` API in windows to enable the factory to retrieve a workspace given a text buffer.
- Added a way to add test services to `AdhocWorkspace` so we can test against services being retrieved from a `Workspace`. This will be much more common once we rely on services coming from `TextBuffer`s in our other tooling pieces.
- Added tests for the default workspace provider.

#1989
This commit is contained in:
N. Taylor Mullen 2018-01-30 12:30:54 -08:00
parent 5e454a36fa
commit 6d2460ae7f
11 changed files with 539 additions and 63 deletions

View File

@ -14,10 +14,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
internal class DefaultRazorEditorFactoryService : RazorEditorFactoryService
{
private static readonly object RazorTextBufferInitializationKey = new object();
private readonly VisualStudioDocumentTrackerFactory _documentTrackerFactory;
private readonly VisualStudioRazorParserFactory _parserFactory;
private readonly BraceSmartIndenterFactory _braceSmartIndenterFactory;
private readonly VisualStudioWorkspaceAccessor _workspaceAccessor;
[ImportingConstructor]
public DefaultRazorEditorFactoryService(VisualStudioWorkspaceAccessor workspaceAccessor)
@ -27,36 +24,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
throw new ArgumentNullException(nameof(workspaceAccessor));
}
var razorLanguageServices = workspaceAccessor.Workspace.Services.GetLanguageServices(RazorLanguage.Name);
_documentTrackerFactory = razorLanguageServices.GetRequiredService<VisualStudioDocumentTrackerFactory>();
_parserFactory = razorLanguageServices.GetRequiredService<VisualStudioRazorParserFactory>();
_braceSmartIndenterFactory = razorLanguageServices.GetRequiredService<BraceSmartIndenterFactory>();
}
// Internal for testing
internal DefaultRazorEditorFactoryService(
VisualStudioDocumentTrackerFactory documentTrackerFactory,
VisualStudioRazorParserFactory parserFactory,
BraceSmartIndenterFactory braceSmartIndenterFactory)
{
if (documentTrackerFactory == null)
{
throw new ArgumentNullException(nameof(documentTrackerFactory));
}
if (parserFactory == null)
{
throw new ArgumentNullException(nameof(parserFactory));
}
if (braceSmartIndenterFactory == null)
{
throw new ArgumentNullException(nameof(braceSmartIndenterFactory));
}
_documentTrackerFactory = documentTrackerFactory;
_parserFactory = parserFactory;
_braceSmartIndenterFactory = braceSmartIndenterFactory;
_workspaceAccessor = workspaceAccessor;
}
public override bool TryGetDocumentTracker(ITextBuffer textBuffer, out VisualStudioDocumentTracker documentTracker)
@ -72,7 +40,12 @@ namespace Microsoft.VisualStudio.Editor.Razor
return false;
}
EnsureTextBufferInitialized(textBuffer);
var textBufferInitialized = TryInitializeTextBuffer(textBuffer);
if (!textBufferInitialized)
{
documentTracker = null;
return false;
}
if (!textBuffer.Properties.TryGetProperty(typeof(VisualStudioDocumentTracker), out documentTracker))
{
@ -96,7 +69,12 @@ namespace Microsoft.VisualStudio.Editor.Razor
return false;
}
EnsureTextBufferInitialized(textBuffer);
var textBufferInitialized = TryInitializeTextBuffer(textBuffer);
if (!textBufferInitialized)
{
parser = null;
return false;
}
if (!textBuffer.Properties.TryGetProperty(typeof(VisualStudioRazorParser), out parser))
{
@ -120,7 +98,12 @@ namespace Microsoft.VisualStudio.Editor.Razor
return false;
}
EnsureTextBufferInitialized(textBuffer);
var textBufferInitialized = TryInitializeTextBuffer(textBuffer);
if (!textBufferInitialized)
{
braceSmartIndenter = null;
return false;
}
if (!textBuffer.Properties.TryGetProperty(typeof(BraceSmartIndenter), out braceSmartIndenter))
{
@ -132,24 +115,37 @@ namespace Microsoft.VisualStudio.Editor.Razor
}
// Internal for testing
internal void EnsureTextBufferInitialized(ITextBuffer textBuffer)
internal bool TryInitializeTextBuffer(ITextBuffer textBuffer)
{
if (textBuffer.Properties.ContainsProperty(RazorTextBufferInitializationKey))
{
// Buffer already initialized.
return;
return true;
}
var tracker = _documentTrackerFactory.Create(textBuffer);
if (!_workspaceAccessor.TryGetWorkspace(textBuffer, out var workspace))
{
// Could not locate workspace for given text buffer.
return false;
}
var razorLanguageServices = workspace.Services.GetLanguageServices(RazorLanguage.Name);
var documentTrackerFactory = razorLanguageServices.GetRequiredService<VisualStudioDocumentTrackerFactory>();
var parserFactory = razorLanguageServices.GetRequiredService<VisualStudioRazorParserFactory>();
var braceSmartIndenterFactory = razorLanguageServices.GetRequiredService<BraceSmartIndenterFactory>();
var tracker = documentTrackerFactory.Create(textBuffer);
textBuffer.Properties[typeof(VisualStudioDocumentTracker)] = tracker;
var parser = _parserFactory.Create(tracker);
var parser = parserFactory.Create(tracker);
textBuffer.Properties[typeof(VisualStudioRazorParser)] = parser;
var braceSmartIndenter = _braceSmartIndenterFactory.Create(tracker);
var braceSmartIndenter = braceSmartIndenterFactory.Create(tracker);
textBuffer.Properties[typeof(BraceSmartIndenter)] = braceSmartIndenter;
textBuffer.Properties.AddProperty(RazorTextBufferInitializationKey, RazorTextBufferInitializationKey);
return true;
}
}
}

View File

@ -2,11 +2,14 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.CodeAnalysis;
using Microsoft.VisualStudio.Text;
namespace Microsoft.VisualStudio.Editor.Razor
{
internal abstract class VisualStudioWorkspaceAccessor
{
public abstract Workspace Workspace { get; }
public abstract bool TryGetWorkspace(ITextBuffer textBuffer, out Workspace workspace);
}
}

View File

@ -1,9 +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.
using System;
using System.ComponentModel.Composition;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.Editor.Razor;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Projection;
namespace Microsoft.VisualStudio.LanguageServices.Razor
{
@ -11,12 +16,105 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
[Export(typeof(VisualStudioWorkspaceAccessor))]
internal class DefaultVisualStudioWorkspaceAccessor : VisualStudioWorkspaceAccessor
{
private readonly IBufferGraphFactoryService _bufferGraphService;
private readonly TextBufferProjectService _projectService;
private readonly Workspace _defaultWorkspace;
[ImportingConstructor]
public DefaultVisualStudioWorkspaceAccessor([Import(typeof(VisualStudioWorkspace))] Workspace workspace)
public DefaultVisualStudioWorkspaceAccessor(
IBufferGraphFactoryService bufferGraphService,
TextBufferProjectService projectService,
[Import(typeof(VisualStudioWorkspace))] Workspace defaultWorkspace)
{
Workspace = workspace;
if (bufferGraphService == null)
{
throw new ArgumentNullException(nameof(bufferGraphService));
}
if (projectService == null)
{
throw new ArgumentNullException(nameof(projectService));
}
if (defaultWorkspace == null)
{
throw new ArgumentNullException(nameof(defaultWorkspace));
}
_bufferGraphService = bufferGraphService;
_projectService = projectService;
_defaultWorkspace = defaultWorkspace;
}
public override Workspace Workspace { get; }
public override Workspace Workspace => _defaultWorkspace;
public override bool TryGetWorkspace(ITextBuffer textBuffer, out Workspace workspace)
{
if (textBuffer == null)
{
throw new ArgumentNullException(nameof(textBuffer));
}
// We do a best effort approach in this method to get the workspace that belongs to the TextBuffer.
// The approaches we take to find the workspace are:
//
// 1. Look for a C# projection buffer associated with the Razor buffer. If we can find one we let
// Roslyn take control of finding the Workspace (projectionBuffer.GetWorkspace()). If not,
// fall back to determining if we can use the default workspace.
// 2. Look to see if this ITextBuffer is associated with a host project. If we find that our Razor
// buffer has a host project, we make the assumption that we should use the default VisualStudioWorkspace.
if (TryGetWorkspaceFromProjectionBuffer(textBuffer, out workspace))
{
return true;
}
if (TryGetWorkspaceFromHostProject(textBuffer, out workspace))
{
return true;
}
workspace = null;
return false;
}
// Internal virtual for testing
internal virtual bool TryGetWorkspaceFromProjectionBuffer(ITextBuffer textBuffer, out Workspace workspace)
{
var graph = _bufferGraphService.CreateBufferGraph(textBuffer);
var projectedCSharpBuffer = graph.GetTextBuffers(buffer => buffer.ContentType.IsOfType("CSharp")).FirstOrDefault();
if (projectedCSharpBuffer == null)
{
workspace = null;
return false;
}
workspace = projectedCSharpBuffer.GetWorkspace();
if (workspace == null)
{
// Couldn't resolve a workspace for the projected csharp buffer.
return false;
}
return true;
}
// Internal virtual for testing
internal virtual bool TryGetWorkspaceFromHostProject(ITextBuffer textBuffer, out Workspace workspace)
{
var project = _projectService.GetHostProject(textBuffer);
if (project == null)
{
// Could not locate a project for the given text buffer.
workspace = null;
return false;
}
// We have a host project, assume default workspace.
workspace = _defaultWorkspace;
return true;
}
}
}

View File

@ -4,6 +4,7 @@
using System.ComponentModel.Composition;
using Microsoft.CodeAnalysis;
using Microsoft.VisualStudio.Editor.Razor;
using Microsoft.VisualStudio.Text;
using MonoDevelop.Ide.TypeSystem;
namespace Microsoft.VisualStudio.Mac.LanguageServices.Razor
@ -18,5 +19,10 @@ namespace Microsoft.VisualStudio.Mac.LanguageServices.Razor
}
public override Workspace Workspace { get; }
public override bool TryGetWorkspace(ITextBuffer textBuffer, out Workspace workspace)
{
throw new System.NotImplementedException();
}
}
}

View File

@ -6,6 +6,7 @@
<ItemGroup>
<ProjectReference Include="..\Microsoft.AspNetCore.Razor.Test.Common\Microsoft.AspNetCore.Razor.Test.Common.csproj" />
<ProjectReference Include="..\..\src\Microsoft.CodeAnalysis.Razor\Microsoft.CodeAnalysis.Razor.csproj" />
</ItemGroup>
<ItemGroup>

View File

@ -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.Generic;
using System.Linq;
using Microsoft.CodeAnalysis.Razor;
namespace Microsoft.CodeAnalysis.Host
{
internal class TestRazorLanguageServices : HostLanguageServices
{
private readonly HostWorkspaceServices _workspaceServices;
private readonly IEnumerable<ILanguageService> _languageServices;
public TestRazorLanguageServices(HostWorkspaceServices workspaceServices, IEnumerable<ILanguageService> languageServices)
{
if (workspaceServices == null)
{
throw new ArgumentNullException(nameof(workspaceServices));
}
if (languageServices == null)
{
throw new ArgumentNullException(nameof(languageServices));
}
_workspaceServices = workspaceServices;
_languageServices = languageServices;
}
public override HostWorkspaceServices WorkspaceServices => _workspaceServices;
public override string Language => RazorLanguage.Name;
public override TLanguageService GetService<TLanguageService>()
{
var service = _languageServices.OfType<TLanguageService>().FirstOrDefault();
if (service == null)
{
throw new InvalidOperationException($"Test Razor language services not configured properly, missing language service '{typeof(TLanguageService).FullName}'.");
}
return service;
}
}
}

View File

@ -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.Collections.Generic;
using System.Linq;
namespace Microsoft.CodeAnalysis.Host
{
public class TestServices : HostServices
{
private readonly IEnumerable<IWorkspaceService> _workspaceServices;
private readonly IEnumerable<ILanguageService> _razorLanguageServices;
private TestServices(IEnumerable<IWorkspaceService> workspaceServices, IEnumerable<ILanguageService> razorLanguageServices)
{
if (workspaceServices == null)
{
throw new ArgumentNullException(nameof(workspaceServices));
}
if (razorLanguageServices == null)
{
throw new ArgumentNullException(nameof(razorLanguageServices));
}
_workspaceServices = workspaceServices;
_razorLanguageServices = razorLanguageServices;
}
protected override HostWorkspaceServices CreateWorkspaceServices(Workspace workspace)
{
if (workspace == null)
{
throw new ArgumentNullException(nameof(workspace));
}
return new TestWorkspaceServices(this, _workspaceServices, _razorLanguageServices, workspace);
}
public static HostServices Create(IEnumerable<ILanguageService> razorLanguageServices)
=> Create(Enumerable.Empty<IWorkspaceService>(), razorLanguageServices);
public static HostServices Create(IEnumerable<IWorkspaceService> workspaceServices, IEnumerable<ILanguageService> razorLanguageServices)
=> new TestServices(workspaceServices, razorLanguageServices);
}
}

View File

@ -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.CodeAnalysis.Host;
namespace Microsoft.CodeAnalysis
{
@ -9,11 +10,13 @@ namespace Microsoft.CodeAnalysis
{
private static readonly object WorkspaceLock = new object();
public static Workspace Create(Action<AdhocWorkspace> configure = null)
public static Workspace Create(Action<AdhocWorkspace> configure = null) => Create(services: null, configure: configure);
public static Workspace Create(HostServices services, Action<AdhocWorkspace> configure = null)
{
lock (WorkspaceLock)
{
var workspace = new AdhocWorkspace();
var workspace = services == null ? new AdhocWorkspace() : new AdhocWorkspace(services);
configure?.Invoke(workspace);
return workspace;

View File

@ -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;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis.Razor;
namespace Microsoft.CodeAnalysis.Host
{
internal class TestWorkspaceServices : HostWorkspaceServices
{
private static readonly Workspace DefaultWorkspace = TestWorkspace.Create();
private readonly HostServices _hostServices;
private readonly IEnumerable<IWorkspaceService> _workspaceServices;
private readonly TestRazorLanguageServices _razorLanguageServices;
private readonly Workspace _workspace;
public TestWorkspaceServices(
HostServices hostServices,
IEnumerable<IWorkspaceService> workspaceServices,
IEnumerable<ILanguageService> razorLanguageServices,
Workspace workspace)
{
if (hostServices == null)
{
throw new ArgumentNullException(nameof(hostServices));
}
if (workspaceServices == null)
{
throw new ArgumentNullException(nameof(workspaceServices));
}
if (razorLanguageServices == null)
{
throw new ArgumentNullException(nameof(razorLanguageServices));
}
if (workspace == null)
{
throw new ArgumentNullException(nameof(workspace));
}
_hostServices = hostServices;
_workspaceServices = workspaceServices;
_razorLanguageServices = new TestRazorLanguageServices(this, razorLanguageServices);
_workspace = workspace;
}
public override HostServices HostServices => _hostServices;
public override Workspace Workspace => _workspace;
public override TWorkspaceService GetService<TWorkspaceService>()
{
var service = _workspaceServices.OfType<TWorkspaceService>().FirstOrDefault();
if (service == null)
{
// Fallback to default host services to resolve roslyn specific features.
service = DefaultWorkspace.Services.GetService<TWorkspaceService>();
}
return service;
}
public override HostLanguageServices GetLanguageServices(string languageName)
{
if (languageName != RazorLanguage.Name)
{
throw new InvalidOperationException($"Test services do not support language service '{languageName}'. The only language services supported are '{RazorLanguage.Name}'.");
}
return _razorLanguageServices;
}
public override IEnumerable<string> SupportedLanguages => new[] { RazorLanguage.Name };
public override bool IsSupported(string languageName) => languageName == RazorLanguage.Name;
public override IEnumerable<TLanguageService> FindLanguageServices<TLanguageService>(MetadataFilter filter) => throw new NotImplementedException();
}
}

View File

@ -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 Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Utilities;
@ -47,7 +49,25 @@ namespace Microsoft.VisualStudio.Editor.Razor
}
[Fact]
public void EnsureTextBufferInitialized_StoresTracker()
public void TryInitializeTextBuffer_WorkspaceAccessorCanNotAccessWorkspace_ReturnsFalse()
{
// Arrange
Workspace workspace = null;
var workspaceAccessor = new Mock<VisualStudioWorkspaceAccessor>();
workspaceAccessor.Setup(provider => provider.TryGetWorkspace(It.IsAny<ITextBuffer>(), out workspace))
.Returns(false);
var factoryService = new DefaultRazorEditorFactoryService(workspaceAccessor.Object);
var textBuffer = Mock.Of<ITextBuffer>(b => b.ContentType == RazorCoreContentType && b.Properties == new PropertyCollection());
// Act
var result = factoryService.TryInitializeTextBuffer(textBuffer);
// Assert
Assert.False(result);
}
[Fact]
public void TryInitializeTextBuffer_StoresTracker_ReturnsTrue()
{
// Arrange
var expectedDocumentTracker = Mock.Of<VisualStudioDocumentTracker>();
@ -55,29 +75,31 @@ namespace Microsoft.VisualStudio.Editor.Razor
var textBuffer = Mock.Of<ITextBuffer>(b => b.ContentType == RazorCoreContentType && b.Properties == new PropertyCollection());
// Act
factoryService.EnsureTextBufferInitialized(textBuffer);
var result = factoryService.TryInitializeTextBuffer(textBuffer);
// Assert
Assert.True(result);
Assert.True(textBuffer.Properties.TryGetProperty(typeof(VisualStudioDocumentTracker), out VisualStudioDocumentTracker documentTracker));
Assert.Same(expectedDocumentTracker, documentTracker);
}
[Fact]
public void EnsureTextBufferInitialized_OnlyStoresTrackerOnTextBufferOnce()
public void TryInitializeTextBuffer_OnlyStoresTrackerOnTextBufferOnce_ReturnsTrue()
{
// Arrange
var factoryService = CreateFactoryService();
var textBuffer = Mock.Of<ITextBuffer>(b => b.ContentType == RazorCoreContentType && b.Properties == new PropertyCollection());
factoryService.EnsureTextBufferInitialized(textBuffer);
factoryService.TryInitializeTextBuffer(textBuffer);
var expectedDocumentTracker = textBuffer.Properties[typeof(VisualStudioDocumentTracker)];
// Create a second factory service so it generates a different tracker
factoryService = CreateFactoryService();
// Act
factoryService.EnsureTextBufferInitialized(textBuffer);
var result = factoryService.TryInitializeTextBuffer(textBuffer);
// Assert
Assert.True(result);
Assert.True(textBuffer.Properties.TryGetProperty(typeof(VisualStudioDocumentTracker), out VisualStudioDocumentTracker documentTracker));
Assert.Same(expectedDocumentTracker, documentTracker);
}
@ -114,7 +136,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
}
[Fact]
public void EnsureTextBufferInitialized_StoresParser()
public void TryInitializeTextBuffer_StoresParser_ReturnsTrue()
{
// Arrange
var expectedParser = Mock.Of<VisualStudioRazorParser>();
@ -122,29 +144,31 @@ namespace Microsoft.VisualStudio.Editor.Razor
var textBuffer = Mock.Of<ITextBuffer>(b => b.ContentType == RazorCoreContentType && b.Properties == new PropertyCollection());
// Act
factoryService.EnsureTextBufferInitialized(textBuffer);
var result = factoryService.TryInitializeTextBuffer(textBuffer);
// Assert
Assert.True(result);
Assert.True(textBuffer.Properties.TryGetProperty(typeof(VisualStudioRazorParser), out VisualStudioRazorParser parser));
Assert.Same(expectedParser, parser);
}
[Fact]
public void EnsureTextBufferInitialized_OnlyStoresParserOnTextBufferOnce()
public void TryInitializeTextBuffer_OnlyStoresParserOnTextBufferOnce_ReturnsTrue()
{
// Arrange
var factoryService = CreateFactoryService();
var textBuffer = Mock.Of<ITextBuffer>(b => b.ContentType == RazorCoreContentType && b.Properties == new PropertyCollection());
factoryService.EnsureTextBufferInitialized(textBuffer);
factoryService.TryInitializeTextBuffer(textBuffer);
var expectedParser = textBuffer.Properties[typeof(VisualStudioRazorParser)];
// Create a second factory service so it generates a different parser
factoryService = CreateFactoryService();
// Act
factoryService.EnsureTextBufferInitialized(textBuffer);
var result = factoryService.TryInitializeTextBuffer(textBuffer);
// Assert
Assert.True(result);
Assert.True(textBuffer.Properties.TryGetProperty(typeof(VisualStudioRazorParser), out VisualStudioRazorParser parser));
Assert.Same(expectedParser, parser);
}
@ -181,7 +205,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
}
[Fact]
public void EnsureTextBufferInitialized_StoresSmartIndenter()
public void TryInitializeTextBuffer_StoresSmartIndenter_ReturnsTrue()
{
// Arrange
var expectedSmartIndenter = Mock.Of<BraceSmartIndenter>();
@ -189,29 +213,31 @@ namespace Microsoft.VisualStudio.Editor.Razor
var textBuffer = Mock.Of<ITextBuffer>(b => b.ContentType == RazorCoreContentType && b.Properties == new PropertyCollection());
// Act
factoryService.EnsureTextBufferInitialized(textBuffer);
var result = factoryService.TryInitializeTextBuffer(textBuffer);
// Assert
Assert.True(result);
Assert.True(textBuffer.Properties.TryGetProperty(typeof(BraceSmartIndenter), out BraceSmartIndenter smartIndenter));
Assert.Same(expectedSmartIndenter, smartIndenter);
}
[Fact]
public void EnsureTextBufferInitialized_OnlyStoresSmartIndenterOnTextBufferOnce()
public void TryInitializeTextBuffer_OnlyStoresSmartIndenterOnTextBufferOnce_ReturnsTrue()
{
// Arrange
var factoryService = CreateFactoryService();
var textBuffer = Mock.Of<ITextBuffer>(b => b.ContentType == RazorCoreContentType && b.Properties == new PropertyCollection());
factoryService.EnsureTextBufferInitialized(textBuffer);
factoryService.TryInitializeTextBuffer(textBuffer);
var expectedSmartIndenter = textBuffer.Properties[typeof(BraceSmartIndenter)];
// Create a second factory service so it generates a different smart indenter
factoryService = CreateFactoryService();
// Act
factoryService.EnsureTextBufferInitialized(textBuffer);
var result = factoryService.TryInitializeTextBuffer(textBuffer);
// Assert
Assert.True(result);
Assert.True(textBuffer.Properties.TryGetProperty(typeof(BraceSmartIndenter), out BraceSmartIndenter smartIndenter));
Assert.Same(expectedSmartIndenter, smartIndenter);
}
@ -228,7 +254,20 @@ namespace Microsoft.VisualStudio.Editor.Razor
var documentTrackerFactory = Mock.Of<VisualStudioDocumentTrackerFactory>(f => f.Create(It.IsAny<ITextBuffer>()) == documentTracker);
var parserFactory = Mock.Of<VisualStudioRazorParserFactory>(f => f.Create(It.IsAny<VisualStudioDocumentTracker>()) == parser);
var smartIndenterFactory = Mock.Of<BraceSmartIndenterFactory>(f => f.Create(It.IsAny<VisualStudioDocumentTracker>()) == smartIndenter);
var factoryService = new DefaultRazorEditorFactoryService(documentTrackerFactory, parserFactory, smartIndenterFactory);
var services = TestServices.Create(new ILanguageService[]
{
documentTrackerFactory,
parserFactory,
smartIndenterFactory
});
var workspace = TestWorkspace.Create(services);
var workspaceAccessor = new Mock<VisualStudioWorkspaceAccessor>();
workspaceAccessor.Setup(p => p.TryGetWorkspace(It.IsAny<ITextBuffer>(), out workspace))
.Returns(true);
var factoryService = new DefaultRazorEditorFactoryService(workspaceAccessor.Object);
return factoryService;
}

View File

@ -0,0 +1,150 @@
// Copyright (c) .NET 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.ObjectModel;
using Microsoft.CodeAnalysis;
using Microsoft.VisualStudio.Editor.Razor;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Projection;
using Moq;
using Xunit;
namespace Microsoft.VisualStudio.LanguageServices.Razor
{
public class DefaultVisualStudioWorkspaceAccessorTest
{
[Fact]
public void TryGetWorkspace_CanGetWorkspaceFromProjectionBuffersOnly()
{
// Arrange
var textBuffer = Mock.Of<ITextBuffer>();
var workspaceAccessor = new TestWorkspaceAccessor(true, false);
// Act
var result = workspaceAccessor.TryGetWorkspace(textBuffer, out var workspace);
// Assert
Assert.True(result);
}
[Fact]
public void TryGetWorkspace_CanGetWorkspaceFromBuffersInHierarchyOnly()
{
// Arrange
var textBuffer = Mock.Of<ITextBuffer>();
var workspaceAccessor = new TestWorkspaceAccessor(false, true);
// Act
var result = workspaceAccessor.TryGetWorkspace(textBuffer, out var workspace);
// Assert
Assert.True(result);
}
[Fact]
public void TryGetWorkspace_CanGetWorkspaceFromBuffersInHierarchyOrProjectionBuffers()
{
// Arrange
var textBuffer = Mock.Of<ITextBuffer>();
var workspaceAccessor = new TestWorkspaceAccessor(true, true);
// Act
var result = workspaceAccessor.TryGetWorkspace(textBuffer, out var workspace);
// Assert
Assert.True(result);
}
[Fact]
public void TryGetWorkspaceFromProjectionBuffer_NoProjectionBuffer_ReturnsFalse()
{
// Arrange
var bufferGraph = new Mock<IBufferGraph>();
bufferGraph.Setup(graph => graph.GetTextBuffers(It.IsAny<Predicate<ITextBuffer>>()))
.Returns<Predicate<ITextBuffer>>(predicate => new Collection<ITextBuffer>());
var bufferGraphService = new Mock<IBufferGraphFactoryService>();
bufferGraphService.Setup(service => service.CreateBufferGraph(It.IsAny<ITextBuffer>()))
.Returns(bufferGraph.Object);
var workspaceAccessor = new DefaultVisualStudioWorkspaceAccessor(bufferGraphService.Object, Mock.Of<TextBufferProjectService>(), TestWorkspace.Create());
var textBuffer = Mock.Of<ITextBuffer>();
// Act
var result = workspaceAccessor.TryGetWorkspaceFromProjectionBuffer(textBuffer, out var workspace);
// Assert
Assert.False(result);
}
[Fact]
public void TryGetWorkspaceFromHostProject_NoHostProject_ReturnsFalse()
{
// Arrange
var workspaceAccessor = new DefaultVisualStudioWorkspaceAccessor(Mock.Of<IBufferGraphFactoryService>(), Mock.Of<TextBufferProjectService>(), TestWorkspace.Create());
var textBuffer = Mock.Of<ITextBuffer>();
// Act
var result = workspaceAccessor.TryGetWorkspaceFromHostProject(textBuffer, out var workspace);
// Assert
Assert.False(result);
}
[Fact]
public void TryGetWorkspaceFromHostProject_HasHostProject_ReturnsTrueWithDefaultWorkspace()
{
// Arrange
var textBuffer = Mock.Of<ITextBuffer>();
var projectService = Mock.Of<TextBufferProjectService>(service => service.GetHostProject(textBuffer) == new object());
var defaultWorkspace = TestWorkspace.Create();
var workspaceAccessor = new DefaultVisualStudioWorkspaceAccessor(Mock.Of<IBufferGraphFactoryService>(), projectService, defaultWorkspace);
// Act
var result = workspaceAccessor.TryGetWorkspaceFromHostProject(textBuffer, out var workspace);
// Assert
Assert.True(result);
Assert.Same(defaultWorkspace, workspace);
}
private class TestWorkspaceAccessor : DefaultVisualStudioWorkspaceAccessor
{
private readonly bool _canGetWorkspaceFromProjectionBuffer;
private readonly bool _canGetWorkspaceFromHostProject;
internal TestWorkspaceAccessor(bool canGetWorkspaceFromProjectionBuffer, bool canGetWorkspaceFromHostProject) :
base(
Mock.Of<IBufferGraphFactoryService>(),
Mock.Of<TextBufferProjectService>(),
TestWorkspace.Create())
{
_canGetWorkspaceFromProjectionBuffer = canGetWorkspaceFromProjectionBuffer;
_canGetWorkspaceFromHostProject = canGetWorkspaceFromHostProject;
}
internal override bool TryGetWorkspaceFromProjectionBuffer(ITextBuffer textBuffer, out Workspace workspace)
{
if (_canGetWorkspaceFromProjectionBuffer)
{
workspace = TestWorkspace.Create();
return true;
}
workspace = null;
return false;
}
internal override bool TryGetWorkspaceFromHostProject(ITextBuffer textBuffer, out Workspace workspace)
{
if (_canGetWorkspaceFromHostProject)
{
workspace = TestWorkspace.Create();
return true;
}
workspace = null;
return false;
}
}
}
}