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.
|
// 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)
|
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