Add Mac project build change trigger.
- Added a Mac specific implementation of the project build change trigger. It applies to all types of project builds so we need to do a little extra filtering to ensure that we're not operating on a non-ASP.NET Core project. - Added tests to validate that the project build event fires correctly. #1851
This commit is contained in:
parent
06c2cf31d4
commit
4b93741610
|
|
@ -81,7 +81,7 @@ namespace Microsoft.VisualStudio.Mac.LanguageServices.Razor.Editor
|
|||
}
|
||||
|
||||
// VisualStudio for Mac only supports ASP.NET Core Razor.
|
||||
public override bool IsSupportedProject(object project) => true;
|
||||
public override bool IsSupportedProject(object project) => project is DotNetProject;
|
||||
|
||||
public override string GetProjectName(object project)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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.ComponentModel.Composition;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
using Microsoft.VisualStudio.Editor.Razor;
|
||||
using MonoDevelop.Ide;
|
||||
using MonoDevelop.Projects;
|
||||
|
||||
namespace Microsoft.VisualStudio.Mac.LanguageServices.Razor
|
||||
{
|
||||
[Export(typeof(ProjectSnapshotChangeTrigger))]
|
||||
internal class ProjectBuildChangeTrigger : ProjectSnapshotChangeTrigger
|
||||
{
|
||||
private readonly TextBufferProjectService _projectService;
|
||||
private readonly ForegroundDispatcher _foregroundDispatcher;
|
||||
private ProjectSnapshotManagerBase _projectManager;
|
||||
|
||||
[ImportingConstructor]
|
||||
public ProjectBuildChangeTrigger(VisualStudioWorkspaceAccessor workspaceAccessor)
|
||||
{
|
||||
if (workspaceAccessor == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(workspaceAccessor));
|
||||
}
|
||||
|
||||
_foregroundDispatcher = workspaceAccessor.Workspace.Services.GetRequiredService<ForegroundDispatcher>();
|
||||
|
||||
var languageServices = workspaceAccessor.Workspace.Services.GetLanguageServices(RazorLanguage.Name);
|
||||
_projectService = languageServices.GetRequiredService<TextBufferProjectService>();
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal ProjectBuildChangeTrigger(
|
||||
ForegroundDispatcher foregroundDispatcher,
|
||||
TextBufferProjectService projectService,
|
||||
ProjectSnapshotManagerBase projectManager)
|
||||
{
|
||||
if (foregroundDispatcher == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(foregroundDispatcher));
|
||||
}
|
||||
|
||||
if (projectService == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectService));
|
||||
}
|
||||
|
||||
if (projectManager == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectManager));
|
||||
}
|
||||
|
||||
_foregroundDispatcher = foregroundDispatcher;
|
||||
_projectService = projectService;
|
||||
_projectManager = projectManager;
|
||||
}
|
||||
|
||||
public override void Initialize(ProjectSnapshotManagerBase projectManager)
|
||||
{
|
||||
if (projectManager == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectManager));
|
||||
}
|
||||
|
||||
_projectManager = projectManager;
|
||||
|
||||
IdeApp.ProjectOperations.EndBuild += ProjectOperations_EndBuild;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal void ProjectOperations_EndBuild(object sender, BuildEventArgs args)
|
||||
{
|
||||
if (args == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(args));
|
||||
}
|
||||
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
if (!args.Success)
|
||||
{
|
||||
// Build failed
|
||||
return;
|
||||
}
|
||||
|
||||
var projectItem = args.SolutionItem;
|
||||
if (!_projectService.IsSupportedProject(projectItem))
|
||||
{
|
||||
// We're hooked into all build events, it's possible to get called with an unsupported project item type.
|
||||
return;
|
||||
}
|
||||
|
||||
var projectName = _projectService.GetProjectName(projectItem);
|
||||
var projectPath = _projectService.GetProjectPath(projectItem);
|
||||
|
||||
// Get the corresponding roslyn project by matching the project name and the project path.
|
||||
foreach (var project in _projectManager.Workspace.CurrentSolution.Projects)
|
||||
{
|
||||
if (string.Equals(projectName, project.Name, StringComparison.Ordinal) &&
|
||||
string.Equals(projectPath, project.FilePath, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
_projectManager.ProjectBuildComplete(project);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
// Copyright (c) .NET 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.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
using Microsoft.VisualStudio.Editor.Razor;
|
||||
using MonoDevelop.Projects;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
using Project = Microsoft.CodeAnalysis.Project;
|
||||
|
||||
namespace Microsoft.VisualStudio.Mac.LanguageServices.Razor
|
||||
{
|
||||
public class ProjectBuildChangeTriggerTest : ForegroundDispatcherTestBase
|
||||
{
|
||||
[ForegroundFact]
|
||||
public void ProjectOperations_EndBuild_Invokes_ProjectBuildComplete()
|
||||
{
|
||||
// Arrange
|
||||
var args = new BuildEventArgs(monitor: null, success: true);
|
||||
var expectedProjectName = "Test1";
|
||||
var expectedProjectPath = "Path/To/Project";
|
||||
var projectService = CreateProjectService(expectedProjectName, expectedProjectPath);
|
||||
var workspace = new AdhocWorkspace();
|
||||
CreateProjectInWorkspace(workspace, expectedProjectName, expectedProjectPath);
|
||||
CreateProjectInWorkspace(workspace, "Test2", "Path/To/AnotherProject");
|
||||
|
||||
var projectManager = new Mock<ProjectSnapshotManagerBase>(MockBehavior.Strict);
|
||||
projectManager.SetupGet(p => p.Workspace).Returns(workspace);
|
||||
projectManager
|
||||
.Setup(p => p.ProjectBuildComplete(It.IsAny<Project>()))
|
||||
.Callback<Project>(c => Assert.Equal(expectedProjectName, c.Name));
|
||||
var trigger = new ProjectBuildChangeTrigger(Dispatcher, projectService, projectManager.Object);
|
||||
|
||||
// Act
|
||||
trigger.ProjectOperations_EndBuild(null, args);
|
||||
|
||||
// Assert
|
||||
projectManager.VerifyAll();
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void ProjectOperations_EndBuild_UntrackedProject_Noops()
|
||||
{
|
||||
// Arrange
|
||||
var args = new BuildEventArgs(monitor: null, success: true);
|
||||
var projectService = CreateProjectService("Test1", "Path/To/Project");
|
||||
var workspace = new AdhocWorkspace();
|
||||
CreateProjectInWorkspace(workspace, "Test2", "Path/To/AnotherProject");
|
||||
var projectManager = new Mock<ProjectSnapshotManagerBase>();
|
||||
projectManager.SetupGet(p => p.Workspace).Returns(workspace);
|
||||
projectManager
|
||||
.Setup(p => p.ProjectBuildComplete(It.IsAny<Project>()))
|
||||
.Throws<InvalidOperationException>();
|
||||
var trigger = new ProjectBuildChangeTrigger(Dispatcher, projectService, projectManager.Object);
|
||||
|
||||
// Act & Assert
|
||||
trigger.ProjectOperations_EndBuild(null, args);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void ProjectOperations_EndBuild_BuildFailed_Noops()
|
||||
{
|
||||
// Arrange
|
||||
var args = new BuildEventArgs(monitor: null, success: false);
|
||||
var projectService = new Mock<TextBufferProjectService>();
|
||||
projectService.Setup(p => p.IsSupportedProject(null)).Throws<InvalidOperationException>();
|
||||
var projectManager = new Mock<ProjectSnapshotManagerBase>();
|
||||
projectManager.SetupGet(p => p.Workspace).Throws<InvalidOperationException>();
|
||||
var trigger = new ProjectBuildChangeTrigger(Dispatcher, projectService.Object, projectManager.Object);
|
||||
|
||||
// Act & Assert
|
||||
trigger.ProjectOperations_EndBuild(null, args);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void ProjectOperations_EndBuild_UnsupportedProject_Noops()
|
||||
{
|
||||
// Arrange
|
||||
var args = new BuildEventArgs(monitor: null, success: true);
|
||||
var projectService = new Mock<TextBufferProjectService>();
|
||||
projectService.Setup(p => p.IsSupportedProject(null)).Returns(false);
|
||||
var projectManager = new Mock<ProjectSnapshotManagerBase>();
|
||||
projectManager.SetupGet(p => p.Workspace).Throws<InvalidOperationException>();
|
||||
var trigger = new ProjectBuildChangeTrigger(Dispatcher, projectService.Object, projectManager.Object);
|
||||
|
||||
// Act & Assert
|
||||
trigger.ProjectOperations_EndBuild(null, args);
|
||||
}
|
||||
|
||||
private static TextBufferProjectService CreateProjectService(string projectName, string projectPath)
|
||||
{
|
||||
var projectService = new Mock<TextBufferProjectService>();
|
||||
projectService.Setup(p => p.GetProjectName(null)).Returns(projectName);
|
||||
projectService.Setup(p => p.GetProjectPath(null)).Returns(projectPath);
|
||||
projectService.Setup(p => p.IsSupportedProject(null)).Returns(true);
|
||||
return projectService.Object;
|
||||
}
|
||||
|
||||
private static AdhocWorkspace CreateProjectInWorkspace(AdhocWorkspace workspace, string name, string path)
|
||||
{
|
||||
workspace.AddProject(ProjectInfo.Create(ProjectId.CreateNewId(), new VersionStamp(), name, "TestAssembly", LanguageNames.CSharp, filePath: path));
|
||||
return workspace;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue