// 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.Collections.Generic; using System.Linq; using Moq; using Xunit; namespace Microsoft.CodeAnalysis.Razor.ProjectSystem { public class DefaultProjectSnapshotManagerTest { public DefaultProjectSnapshotManagerTest() { Workspace = new AdhocWorkspace(); ProjectManager = new TestProjectSnapshotManager(Enumerable.Empty(), Workspace); } private TestProjectSnapshotManager ProjectManager { get; } private Workspace Workspace { get; } [Fact] public void ProjectAdded_AddsProject_NotifiesListeners_AndStartsBackgroundWorker() { // Arrange var project = Workspace.CurrentSolution.AddProject("Test", "Test", LanguageNames.CSharp); // Act ProjectManager.ProjectAdded(project); // Assert var snapshot = ProjectManager.GetSnapshot(project.Id); Assert.True(snapshot.IsDirty); Assert.True(ProjectManager.ListenersNotified); Assert.True(ProjectManager.WorkerStarted); } [Fact] public void ProjectChanged_MadeDirty_RetainsComputedState_NotifiesListeners_AndStartsBackgroundWorker() { // Arrange var project = Workspace.CurrentSolution.AddProject("Test", "Test", LanguageNames.CSharp); ProjectManager.ProjectAdded(project); ProjectManager.Reset(); // Adding some computed state var configuration = Mock.Of(); ProjectManager.ProjectChanged(new ProjectSnapshotUpdateContext(project) { Configuration = configuration }); ProjectManager.Reset(); project = project.WithAssemblyName("Test1"); // Simulate a project change // Act ProjectManager.ProjectChanged(project); // Assert var snapshot = ProjectManager.GetSnapshot(project.Id); Assert.True(snapshot.IsDirty); Assert.Same(configuration, snapshot.Configuration); Assert.False(ProjectManager.ListenersNotified); Assert.True(ProjectManager.WorkerStarted); } [Fact] public void ProjectChanged_BackgroundUpdate_MadeClean_WithSignificantChanges_NotifiesListeners_AndDoesNotStartBackgroundWorker() { // Arrange var project = Workspace.CurrentSolution.AddProject("Test", "Test", LanguageNames.CSharp); ProjectManager.ProjectAdded(project); ProjectManager.Reset(); var configuration = Mock.Of(); // Act ProjectManager.ProjectChanged(new ProjectSnapshotUpdateContext(project) { Configuration = configuration }); // Assert var snapshot = ProjectManager.GetSnapshot(project.Id); Assert.False(snapshot.IsDirty); Assert.Same(configuration, snapshot.Configuration); Assert.True(ProjectManager.ListenersNotified); Assert.False(ProjectManager.WorkerStarted); } [Fact] public void ProjectChanged_BackgroundUpdate_MadeClean_WithoutSignificantChanges_NotifiesListeners_AndDoesNotStartBackgroundWorker() { // Arrange var project = Workspace.CurrentSolution.AddProject("Test", "Test", LanguageNames.CSharp); ProjectManager.ProjectAdded(project); ProjectManager.Reset(); var configuration = Mock.Of(); ProjectManager.ProjectChanged(new ProjectSnapshotUpdateContext(project) { Configuration = configuration }); ProjectManager.Reset(); project = project.WithAssemblyName("Test1"); // Simulate a project change ProjectManager.ProjectChanged(project); ProjectManager.Reset(); // Act ProjectManager.ProjectChanged(new ProjectSnapshotUpdateContext(project) { Configuration = configuration }); // Assert var snapshot = ProjectManager.GetSnapshot(project.Id); Assert.False(snapshot.IsDirty); Assert.Same(configuration, snapshot.Configuration); Assert.False(ProjectManager.ListenersNotified); Assert.False(ProjectManager.WorkerStarted); } [Fact] public void ProjectChanged_BackgroundUpdate_StillDirty_WithSignificantChanges_NotifiesListeners_AndStartsBackgroundWorker() { // Arrange var project = Workspace.CurrentSolution.AddProject("Test", "Test", LanguageNames.CSharp); ProjectManager.ProjectAdded(project); ProjectManager.Reset(); var configuration = Mock.Of(); // Compute an update for "Test" var update = new ProjectSnapshotUpdateContext(project) { Configuration = configuration }; project = project.WithAssemblyName("Test1"); // Simulate a project change ProjectManager.ProjectChanged(project); ProjectManager.Reset(); // Act ProjectManager.ProjectChanged(update); // Assert var snapshot = ProjectManager.GetSnapshot(project.Id); Assert.True(snapshot.IsDirty); Assert.Same(configuration, snapshot.Configuration); Assert.True(ProjectManager.ListenersNotified); Assert.True(ProjectManager.WorkerStarted); } [Fact] public void ProjectChanged_BackgroundUpdate_StillDirty_WithoutSignificantChanges_NotifiesListeners_AndStartsBackgroundWorker() { // Arrange var project = Workspace.CurrentSolution.AddProject("Test", "Test", LanguageNames.CSharp); ProjectManager.ProjectAdded(project); ProjectManager.Reset(); var configuration = Mock.Of(); ProjectManager.ProjectChanged(new ProjectSnapshotUpdateContext(project) { Configuration = configuration }); project = project.WithAssemblyName("Test1"); // Simulate a project change ProjectManager.ProjectChanged(project); ProjectManager.Reset(); // Compute an update for "Test1" var update = new ProjectSnapshotUpdateContext(project) { Configuration = configuration }; project = project.WithAssemblyName("Test2"); // Simulate a project change ProjectManager.ProjectChanged(project); ProjectManager.Reset(); // Act ProjectManager.ProjectChanged(update); // Still dirty because the project changed while computing the update // Assert var snapshot = ProjectManager.GetSnapshot(project.Id); Assert.True(snapshot.IsDirty); Assert.Same(configuration, snapshot.Configuration); Assert.False(ProjectManager.ListenersNotified); Assert.True(ProjectManager.WorkerStarted); } [Fact] public void ProjectChanged_IgnoresUnknownProject() { // Arrange var project = Workspace.CurrentSolution.AddProject("Test", "Test", LanguageNames.CSharp); // Act ProjectManager.ProjectChanged(project); // Assert Assert.Empty(ProjectManager.Projects); Assert.False(ProjectManager.ListenersNotified); Assert.False(ProjectManager.WorkerStarted); } [Fact] public void ProjectChanged_WithComputedState_IgnoresUnknownProject() { // Arrange var project = Workspace.CurrentSolution.AddProject("Test", "Test", LanguageNames.CSharp); // Act ProjectManager.ProjectChanged(new ProjectSnapshotUpdateContext(project)); // Assert Assert.Empty(ProjectManager.Projects); Assert.False(ProjectManager.ListenersNotified); Assert.False(ProjectManager.WorkerStarted); } [Fact] public void ProjectRemoved_RemovesProject_NotifiesListeners_DoesNotStartBackgroundWorker() { // Arrange var project = Workspace.CurrentSolution.AddProject("Test", "Test", LanguageNames.CSharp); ProjectManager.ProjectAdded(project); ProjectManager.Reset(); // Act ProjectManager.ProjectRemoved(project); // Assert Assert.Empty(ProjectManager.Projects); Assert.True(ProjectManager.ListenersNotified); Assert.False(ProjectManager.WorkerStarted); } [Fact] public void ProjectRemoved_IgnoresUnknownProject() { // Arrange var project = Workspace.CurrentSolution.AddProject("Test", "Test", LanguageNames.CSharp); // Act ProjectManager.ProjectRemoved(project); // Assert Assert.Empty(ProjectManager.Projects); Assert.False(ProjectManager.ListenersNotified); Assert.False(ProjectManager.WorkerStarted); } [Fact] public void ProjectsCleared_RemovesProject_NotifiesListeners_DoesNotStartBackgroundWorker() { // Arrange var project = Workspace.CurrentSolution.AddProject("Test", "Test", LanguageNames.CSharp); ProjectManager.ProjectAdded(project); ProjectManager.Reset(); // Act ProjectManager.ProjectsCleared(); // Assert Assert.Empty(ProjectManager.Projects); Assert.True(ProjectManager.ListenersNotified); Assert.False(ProjectManager.WorkerStarted); } private class TestProjectSnapshotManager : DefaultProjectSnapshotManager { public TestProjectSnapshotManager(IEnumerable triggers, Workspace workspace) : base(triggers, workspace) { } public bool ListenersNotified { get; private set; } public bool WorkerStarted { get; private set; } public DefaultProjectSnapshot GetSnapshot(ProjectId id) { return Projects.Cast().FirstOrDefault(s => s.UnderlyingProject.Id == id); } public void Reset() { ListenersNotified = false; WorkerStarted = false; } protected override void NotifyListeners(ProjectChangeEventArgs e) { ListenersNotified = true; } protected override void NotifyBackgroundWorker() { WorkerStarted = true; } } } }