Add tests for DefaultProjectResolver.

- Added Razor common test projects to facilitate testing.
- Updated some bits on DocumentResolverTest to reflect a consistent naming convention.
- Worked around strong name shenanigans by getting properties via reflection in tests.

#7
This commit is contained in:
N. Taylor Mullen 2018-08-08 15:18:05 -07:00
Родитель c3051825ef
Коммит 4f2d7f1522
7 изменённых файлов: 235 добавлений и 28 удалений

Просмотреть файл

@ -42,6 +42,16 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{75400311-9
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor.LanguageServer.Test", "test\Microsoft.AspNetCore.Razor.LanguageServer.Test\Microsoft.AspNetCore.Razor.LanguageServer.Test.csproj", "{367BD0CB-B226-4105-B741-77A225722078}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{9BDFB459-02CA-45BB-AA57-FF4B6C07CC53}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{B0F1BB2B-99A0-47AD-8D14-CEFBFF58F797}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor.Test.Common", "modules\Razor\test\Microsoft.AspNetCore.Razor.Test.Common\Microsoft.AspNetCore.Razor.Test.Common.csproj", "{5CE61EF0-9B19-4FEF-BC48-5E09B774CBCE}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.Razor.Workspaces.Test.Common", "modules\Razor\test\Microsoft.CodeAnalysis.Razor.Workspaces.Test.Common\Microsoft.CodeAnalysis.Razor.Workspaces.Test.Common.csproj", "{AF97C5AD-8576-406D-B114-8584D5ACD410}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.Editor.Razor.Test.Common", "modules\Razor\test\Microsoft.VisualStudio.Editor.Razor.Test.Common\Microsoft.VisualStudio.Editor.Razor.Test.Common.csproj", "{92A80062-7895-403B-ABAB-5EADCC5440AA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -96,6 +106,18 @@ Global
{367BD0CB-B226-4105-B741-77A225722078}.Debug|Any CPU.Build.0 = Debug|Any CPU
{367BD0CB-B226-4105-B741-77A225722078}.Release|Any CPU.ActiveCfg = Release|Any CPU
{367BD0CB-B226-4105-B741-77A225722078}.Release|Any CPU.Build.0 = Release|Any CPU
{5CE61EF0-9B19-4FEF-BC48-5E09B774CBCE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5CE61EF0-9B19-4FEF-BC48-5E09B774CBCE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5CE61EF0-9B19-4FEF-BC48-5E09B774CBCE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5CE61EF0-9B19-4FEF-BC48-5E09B774CBCE}.Release|Any CPU.Build.0 = Release|Any CPU
{AF97C5AD-8576-406D-B114-8584D5ACD410}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AF97C5AD-8576-406D-B114-8584D5ACD410}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AF97C5AD-8576-406D-B114-8584D5ACD410}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AF97C5AD-8576-406D-B114-8584D5ACD410}.Release|Any CPU.Build.0 = Release|Any CPU
{92A80062-7895-403B-ABAB-5EADCC5440AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{92A80062-7895-403B-ABAB-5EADCC5440AA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{92A80062-7895-403B-ABAB-5EADCC5440AA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{92A80062-7895-403B-ABAB-5EADCC5440AA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -108,13 +130,18 @@ Global
{27AB8254-F634-4D21-AFF1-E078CF3A2009} = {BEAA2EDB-B257-4B34-9FA3-2AD973914528}
{427A5FBB-09EF-49BA-9B00-F3A09892811F} = {2BBB2AB1-2AE4-4306-AA88-FD66A90AA101}
{3C0D9AB8-562E-4736-A31D-839AF8C9D9FD} = {BEAA2EDB-B257-4B34-9FA3-2AD973914528}
{A3E388BC-F625-4A6A-AD31-04E8B1DD0D91} = {3C0D9AB8-562E-4736-A31D-839AF8C9D9FD}
{8CFC36F5-61B4-4F56-8311-750C2969B61D} = {3C0D9AB8-562E-4736-A31D-839AF8C9D9FD}
{487B924C-B52C-44B0-A10C-9CCE95138F14} = {3C0D9AB8-562E-4736-A31D-839AF8C9D9FD}
{F8FDEFA7-9574-4FF3-B854-44BD3F8F37C6} = {3C0D9AB8-562E-4736-A31D-839AF8C9D9FD}
{73F9F775-30E0-43DA-AA35-1F0D70130276} = {3C0D9AB8-562E-4736-A31D-839AF8C9D9FD}
{917E8383-55BB-44EB-BECF-4DDFD19174B4} = {3C0D9AB8-562E-4736-A31D-839AF8C9D9FD}
{A3E388BC-F625-4A6A-AD31-04E8B1DD0D91} = {9BDFB459-02CA-45BB-AA57-FF4B6C07CC53}
{8CFC36F5-61B4-4F56-8311-750C2969B61D} = {9BDFB459-02CA-45BB-AA57-FF4B6C07CC53}
{487B924C-B52C-44B0-A10C-9CCE95138F14} = {9BDFB459-02CA-45BB-AA57-FF4B6C07CC53}
{F8FDEFA7-9574-4FF3-B854-44BD3F8F37C6} = {9BDFB459-02CA-45BB-AA57-FF4B6C07CC53}
{73F9F775-30E0-43DA-AA35-1F0D70130276} = {9BDFB459-02CA-45BB-AA57-FF4B6C07CC53}
{917E8383-55BB-44EB-BECF-4DDFD19174B4} = {9BDFB459-02CA-45BB-AA57-FF4B6C07CC53}
{367BD0CB-B226-4105-B741-77A225722078} = {75400311-9729-4D9A-80D2-ADD5286DE405}
{9BDFB459-02CA-45BB-AA57-FF4B6C07CC53} = {3C0D9AB8-562E-4736-A31D-839AF8C9D9FD}
{B0F1BB2B-99A0-47AD-8D14-CEFBFF58F797} = {3C0D9AB8-562E-4736-A31D-839AF8C9D9FD}
{5CE61EF0-9B19-4FEF-BC48-5E09B774CBCE} = {B0F1BB2B-99A0-47AD-8D14-CEFBFF58F797}
{AF97C5AD-8576-406D-B114-8584D5ACD410} = {B0F1BB2B-99A0-47AD-8D14-CEFBFF58F797}
{92A80062-7895-403B-ABAB-5EADCC5440AA} = {B0F1BB2B-99A0-47AD-8D14-CEFBFF58F797}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {ED1782EB-ABE7-4615-80E2-442006DF2826}

Просмотреть файл

@ -3,6 +3,7 @@
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Razor;
namespace Microsoft.AspNetCore.Razor.LanguageServer.StrongNamed
{
@ -17,5 +18,12 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer.StrongNamed
public abstract void AssertForegroundThread([CallerMemberName] string caller = null);
public abstract void AssertBackgroundThread([CallerMemberName] string caller = null);
public static ForegroundDispatcherShim AsDispatcher(object dispatcher)
{
var foregroundDispatcher = (ForegroundDispatcher)dispatcher;
return new DefaultForegroundDispatcherShim(foregroundDispatcher);
}
}
}

Просмотреть файл

@ -3,14 +3,15 @@
using System;
using System.IO;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.LanguageServer.StrongNamed;
namespace Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem
{
internal class DefaultProjectResolver : ProjectResolver
{
private readonly HostProjectShim _miscellaneousHostProject;
// Internal for testing
protected internal readonly HostProjectShim _miscellaneousHostProject;
private readonly ForegroundDispatcherShim _foregroundDispatcher;
private readonly FilePathNormalizer _filePathNormalizer;
private readonly ProjectSnapshotManagerShimAccessor _projectSnapshotManagerAccessor;
@ -80,9 +81,14 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem
public override ProjectSnapshotShim GetMiscellaneousProject()
{
// If it's already added it'll be ignored.
_projectSnapshotManagerAccessor.Instance.HostProjectAdded(_miscellaneousHostProject);
_foregroundDispatcher.AssertForegroundThread();
var miscellaneousProject = _projectSnapshotManagerAccessor.Instance.GetLoadedProject(_miscellaneousHostProject.FilePath);
if (miscellaneousProject == null)
{
_projectSnapshotManagerAccessor.Instance.HostProjectAdded(_miscellaneousHostProject);
miscellaneousProject = _projectSnapshotManagerAccessor.Instance.GetLoadedProject(_miscellaneousHostProject.FilePath);
}
return miscellaneousProject;
}

Просмотреть файл

@ -3,28 +3,28 @@
using Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem;
using Microsoft.AspNetCore.Razor.LanguageServer.StrongNamed;
using Microsoft.AspNetCore.Razor.LanguageServer.Test;
using Moq;
using Xunit;
namespace Microsoft.AspNetCore.Razor.LanguageServer
{
public class DocumentDocumentResolverTest
public class DocumentDocumentResolverTest : TestBase
{
[Fact]
public void TryResolveDocument_AsksResolvedParentProjectForDocument_ReturnsTrue()
{
// Arrange
var documentPath = "C:\\path\\to\\document.cshtml";
var normalizedPath = "C:/path/to/document.cshtml";
var foregroundDispatcher = Mock.Of<ForegroundDispatcherShim>();
var documentFilePath = "C:\\path\\to\\document.cshtml";
var normalizedFilePath = "C:/path/to/document.cshtml";
var filePathNormalizer = new FilePathNormalizer();
var expectedDocument = Mock.Of<DocumentSnapshotShim>();
var project = Mock.Of<ProjectSnapshotShim>(shim => shim.GetDocument(normalizedPath) == expectedDocument);
var projectResolver = Mock.Of<ProjectResolver>(resolver => resolver.TryResolveProject(normalizedPath, out project) == true);
var documentResolver = new DefaultDocumentResolver(foregroundDispatcher, projectResolver, filePathNormalizer);
var project = Mock.Of<ProjectSnapshotShim>(shim => shim.GetDocument(normalizedFilePath) == expectedDocument);
var projectResolver = Mock.Of<ProjectResolver>(resolver => resolver.TryResolveProject(normalizedFilePath, out project) == true);
var documentResolver = new DefaultDocumentResolver(Dispatcher, projectResolver, filePathNormalizer);
// Act
var result = documentResolver.TryResolveDocument(documentPath, out var document);
var result = documentResolver.TryResolveDocument(documentFilePath, out var document);
// Assert
Assert.True(result);
@ -35,17 +35,16 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer
public void TryResolveDocument_AsksMiscellaneousProjectForDocumentItIsTracking_ReturnsTrue()
{
// Arrange
var documentPath = "C:\\path\\to\\document.cshtml";
var normalizedPath = "C:/path/to/document.cshtml";
var foregroundDispatcher = Mock.Of<ForegroundDispatcherShim>();
var documentFilePath = "C:\\path\\to\\document.cshtml";
var normalizedFilePath = "C:/path/to/document.cshtml";
var filePathNormalizer = new FilePathNormalizer();
var expectedDocument = Mock.Of<DocumentSnapshotShim>();
var project = Mock.Of<ProjectSnapshotShim>(shim => shim.GetDocument(normalizedPath) == expectedDocument && shim.DocumentFilePaths == new[] { normalizedPath });
var project = Mock.Of<ProjectSnapshotShim>(shim => shim.GetDocument(normalizedFilePath) == expectedDocument && shim.DocumentFilePaths == new[] { normalizedFilePath });
var projectResolver = Mock.Of<ProjectResolver>(resolver => resolver.GetMiscellaneousProject() == project);
var documentResolver = new DefaultDocumentResolver(foregroundDispatcher, projectResolver, filePathNormalizer);
var documentResolver = new DefaultDocumentResolver(Dispatcher, projectResolver, filePathNormalizer);
// Act
var result = documentResolver.TryResolveDocument(documentPath, out var document);
var result = documentResolver.TryResolveDocument(documentFilePath, out var document);
// Assert
Assert.True(result);
@ -56,15 +55,14 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer
public void TryResolveDocument_AsksMiscellaneousProjectForDocumentItIsNotTracking_ReturnsFalse()
{
// Arrange
var documentPath = "C:\\path\\to\\document.cshtml";
var foregroundDispatcher = Mock.Of<ForegroundDispatcherShim>();
var documentFilePath = "C:\\path\\to\\document.cshtml";
var filePathNormalizer = new FilePathNormalizer();
var project = Mock.Of<ProjectSnapshotShim>(shim => shim.DocumentFilePaths == new string[0]);
var projectResolver = Mock.Of<ProjectResolver>(resolver => resolver.GetMiscellaneousProject() == project);
var documentResolver = new DefaultDocumentResolver(foregroundDispatcher, projectResolver, filePathNormalizer);
var documentResolver = new DefaultDocumentResolver(Dispatcher, projectResolver, filePathNormalizer);
// Act
var result = documentResolver.TryResolveDocument(documentPath, out var document);
var result = documentResolver.TryResolveDocument(documentFilePath, out var document);
// Assert
Assert.False(result);

Просмотреть файл

@ -0,0 +1,144 @@
// Copyright (c) .NET 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.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem;
using Microsoft.AspNetCore.Razor.LanguageServer.StrongNamed;
using Microsoft.AspNetCore.Razor.LanguageServer.Test;
using Moq;
using Xunit;
namespace Microsoft.AspNetCore.Razor.LanguageServer
{
public class DocumentProjectResolverTest : TestBase
{
[Fact]
public void TryResolveProject_NoProjects_ReturnsFalse()
{
// Arrange
var documentFilePath = "C:/path/to/document.cshtml";
var projectResolver = CreateProjectResolver(() => new ProjectSnapshotShim[0]);
// Act
var result = projectResolver.TryResolveProject(documentFilePath, out var project);
// Assert
Assert.False(result);
Assert.Null(project);
}
[Fact]
public void TryResolveProject_OnlyMiscellaneousProject_ReturnsFalse()
{
// Arrange
var documentFilePath = "C:/path/to/document.cshtml";
DefaultProjectResolver projectResolver = null;
var miscProject = new Mock<ProjectSnapshotShim>();
miscProject.Setup(p => p.FilePath)
.Returns(() => projectResolver._miscellaneousHostProject.FilePath);
projectResolver = CreateProjectResolver(() => new[] { miscProject.Object });
// Act
var result = projectResolver.TryResolveProject(documentFilePath, out var project);
// Assert
Assert.False(result);
Assert.Null(project);
}
[Fact]
public void TryResolveProject_UnrelatedProject_ReturnsFalse()
{
// Arrange
var documentFilePath = "C:/path/to/document.cshtml";
var unrelatedProject = Mock.Of<ProjectSnapshotShim>(p => p.FilePath == "C:/other/path/to/project.csproj");
var projectResolver = CreateProjectResolver(() => new[] { unrelatedProject });
// Act
var result = projectResolver.TryResolveProject(documentFilePath, out var project);
// Assert
Assert.False(result);
Assert.Null(project);
}
[Fact]
public void TryResolveProject_OwnerProjectWithOthers_ReturnsTrue()
{
// Arrange
var documentFilePath = "C:/path/to/document.cshtml";
var unrelatedProject = Mock.Of<ProjectSnapshotShim>(p => p.FilePath == "C:/other/path/to/project.csproj");
var ownerProject = Mock.Of<ProjectSnapshotShim>(p => p.FilePath == "C:/path/to/project.csproj");
var projectResolver = CreateProjectResolver(() => new[] { unrelatedProject, ownerProject });
// Act
var result = projectResolver.TryResolveProject(documentFilePath, out var project);
// Assert
Assert.True(result);
Assert.Same(ownerProject, project);
}
[Fact]
public void GetMiscellaneousProject_ProjectLoaded_ReturnsExistingProject()
{
// Arrange
DefaultProjectResolver projectResolver = null;
var miscProject = new Mock<ProjectSnapshotShim>();
miscProject.Setup(p => p.FilePath)
.Returns(() => projectResolver._miscellaneousHostProject.FilePath);
var expectedProject = miscProject.Object;
projectResolver = CreateProjectResolver(() => new[] { expectedProject });
// Act
var project = projectResolver.GetMiscellaneousProject();
// Assert
Assert.Same(expectedProject, project);
}
[Fact]
public void GetMiscellaneousProject_ProjectNotLoaded_CreatesProjectAndReturnsCreatedProject()
{
// Arrange
DefaultProjectResolver projectResolver = null;
var projects = new List<ProjectSnapshotShim>();
var filePathNormalizer = new FilePathNormalizer();
var configurationResolver = Mock.Of<RazorConfigurationResolver>(resolver => resolver.Default == RazorConfiguration.Default);
var snapshotManager = new Mock<ProjectSnapshotManagerShim>();
snapshotManager.Setup(manager => manager.Projects)
.Returns(() => projects);
snapshotManager.Setup(manager => manager.GetLoadedProject(It.IsAny<string>()))
.Returns<string>(filePath => projects.FirstOrDefault(p => p.FilePath == filePath));
snapshotManager.Setup(manager => manager.HostProjectAdded(It.IsAny<HostProjectShim>()))
.Callback<HostProjectShim>(hostProject => projects.Add(Mock.Of<ProjectSnapshotShim>(p => p.FilePath == hostProject.FilePath)));
var snapshotManagerAccessor = Mock.Of<ProjectSnapshotManagerShimAccessor>(accessor => accessor.Instance == snapshotManager.Object);
projectResolver = new DefaultProjectResolver(Dispatcher, filePathNormalizer, configurationResolver, snapshotManagerAccessor);
// Act
var project = projectResolver.GetMiscellaneousProject();
// Assert
Assert.Single(projects);
Assert.Equal(projectResolver._miscellaneousHostProject.FilePath, project.FilePath);
}
private DefaultProjectResolver CreateProjectResolver(Func<ProjectSnapshotShim[]> projectFactory)
{
var filePathNormalizer = new FilePathNormalizer();
var configurationResolver = Mock.Of<RazorConfigurationResolver>(resolver => resolver.Default == RazorConfiguration.Default);
var snapshotManager = new Mock<ProjectSnapshotManagerShim>();
snapshotManager.Setup(manager => manager.Projects)
.Returns(projectFactory);
snapshotManager.Setup(manager => manager.GetLoadedProject(It.IsAny<string>()))
.Returns<string>(filePath => projectFactory().FirstOrDefault(project => project.FilePath == filePath));
var snapshotManagerAccessor = Mock.Of<ProjectSnapshotManagerShimAccessor>(accessor => accessor.Instance == snapshotManager.Object);
var projectResolver = new DefaultProjectResolver(Dispatcher, filePathNormalizer, configurationResolver, snapshotManagerAccessor);
return projectResolver;
}
}
}

Просмотреть файл

@ -10,6 +10,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\modules\Razor\test\Microsoft.VisualStudio.Editor.Razor.Test.Common\Microsoft.VisualStudio.Editor.Razor.Test.Common.csproj" />
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Razor.LanguageServer\Microsoft.AspNetCore.Razor.LanguageServer.csproj" />
</ItemGroup>

Просмотреть файл

@ -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.Reflection;
using Microsoft.AspNetCore.Razor.LanguageServer.StrongNamed;
using Xunit;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Test
{
public abstract class TestBase : ForegroundDispatcherTestBase
{
public TestBase()
{
var dispatcherObject = typeof(ForegroundDispatcherTestBase)
.GetProperty("Dispatcher", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)
.GetValue(this);
Dispatcher = ForegroundDispatcherShim.AsDispatcher(dispatcherObject);
}
public ForegroundDispatcherShim Dispatcher { get; }
}
}