Change notifications for the project manager
There's still nothing processing the notifications in the background. This is all the plumbing for dirty checking and publishing updates.
This commit is contained in:
parent
82866d9442
commit
7cca8618ea
|
|
@ -5,6 +5,14 @@ using System;
|
|||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
// All of the public state of this is immutable - we create a new instance and notify subscribers
|
||||
// when it changes.
|
||||
//
|
||||
// However we use the private state to track things like dirty/clean.
|
||||
//
|
||||
// See the private constructors... When we update the snapshot we either are processing a Workspace
|
||||
// change (Project) or updating the computed state (ProjectSnapshotUpdateContext). We don't do both
|
||||
// at once.
|
||||
internal class DefaultProjectSnapshot : ProjectSnapshot
|
||||
{
|
||||
public DefaultProjectSnapshot(Project underlyingProject)
|
||||
|
|
@ -17,6 +25,81 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
UnderlyingProject = underlyingProject;
|
||||
}
|
||||
|
||||
private DefaultProjectSnapshot(Project underlyingProject, DefaultProjectSnapshot other)
|
||||
{
|
||||
if (underlyingProject == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(underlyingProject));
|
||||
}
|
||||
|
||||
if (other == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(other));
|
||||
}
|
||||
|
||||
UnderlyingProject = underlyingProject;
|
||||
|
||||
ComputedVersion = other.ComputedVersion;
|
||||
Configuration = other.Configuration;
|
||||
}
|
||||
|
||||
private DefaultProjectSnapshot(ProjectSnapshotUpdateContext update, DefaultProjectSnapshot other)
|
||||
{
|
||||
if (update == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(update));
|
||||
}
|
||||
|
||||
if (other == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(other));
|
||||
}
|
||||
|
||||
UnderlyingProject = other.UnderlyingProject;
|
||||
|
||||
ComputedVersion = update.UnderlyingProject.Version;
|
||||
Configuration = update.Configuration;
|
||||
}
|
||||
|
||||
public override ProjectExtensibilityConfiguration Configuration { get; }
|
||||
|
||||
public override Project UnderlyingProject { get; }
|
||||
|
||||
// This is the version that the computed state is based on.
|
||||
public VersionStamp? ComputedVersion { get; set; }
|
||||
|
||||
// We know the project is dirty if we don't have a computed result, or it was computed for a different version.
|
||||
// Since the PSM updates the snapshots synchronously, the snapshot can never be older than the computed state.
|
||||
public bool IsDirty => ComputedVersion == null || ComputedVersion.Value != UnderlyingProject.Version;
|
||||
|
||||
public DefaultProjectSnapshot WithProjectChange(Project project)
|
||||
{
|
||||
if (project == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(project));
|
||||
}
|
||||
|
||||
return new DefaultProjectSnapshot(project, this);
|
||||
}
|
||||
|
||||
public DefaultProjectSnapshot WithProjectChange(ProjectSnapshotUpdateContext update)
|
||||
{
|
||||
if (update == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(update));
|
||||
}
|
||||
|
||||
return new DefaultProjectSnapshot(update, this);
|
||||
}
|
||||
|
||||
public bool HasChangesComparedTo(ProjectSnapshot original)
|
||||
{
|
||||
if (original == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(original));
|
||||
}
|
||||
|
||||
return !object.Equals(Configuration, original.Configuration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
internal class DefaultProjectSnapshotManager : ProjectSnapshotManagerBase
|
||||
{
|
||||
private readonly ProjectSnapshotChangeTrigger[] _triggers;
|
||||
private readonly Dictionary<ProjectId, ProjectSnapshot> _projects;
|
||||
private readonly Dictionary<ProjectId, DefaultProjectSnapshot> _projects;
|
||||
private readonly List<WeakReference<DefaultProjectSnapshotListener>> _listeners;
|
||||
|
||||
public DefaultProjectSnapshotManager(IEnumerable<ProjectSnapshotChangeTrigger> triggers, Workspace workspace)
|
||||
|
|
@ -28,7 +28,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
_triggers = triggers.ToArray();
|
||||
Workspace = workspace;
|
||||
|
||||
_projects = new Dictionary<ProjectId, ProjectSnapshot>();
|
||||
_projects = new Dictionary<ProjectId, DefaultProjectSnapshot>();
|
||||
_listeners = new List<WeakReference<DefaultProjectSnapshotListener>>();
|
||||
|
||||
for (var i = 0; i < _triggers.Length; i++)
|
||||
|
|
@ -59,6 +59,9 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
var snapshot = new DefaultProjectSnapshot(underlyingProject);
|
||||
_projects[underlyingProject.Id] = snapshot;
|
||||
|
||||
// New projects always start dirty, need to compute state in the background.
|
||||
NotifyBackgroundWorker();
|
||||
|
||||
// We need to notify listeners about every project add.
|
||||
NotifyListeners(new ProjectChangeEventArgs(snapshot, ProjectChangeKind.Added));
|
||||
}
|
||||
|
|
@ -70,12 +73,49 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
throw new ArgumentNullException(nameof(underlyingProject));
|
||||
}
|
||||
|
||||
// For now we don't have any state associated with the project so we can just construct a new snapshot.
|
||||
var snapshot = new DefaultProjectSnapshot(underlyingProject);
|
||||
_projects[underlyingProject.Id] = snapshot;
|
||||
if (_projects.TryGetValue(underlyingProject.Id, out var original))
|
||||
{
|
||||
// Doing an update to the project should keep computed values, but mark the project as dirty if the
|
||||
// underlying project is newer.
|
||||
var snapshot = original.WithProjectChange(underlyingProject);
|
||||
_projects[underlyingProject.Id] = snapshot;
|
||||
|
||||
// There's no need to notify listeners about project changes because we don't have any state.
|
||||
// This will change when we implement extensibility support.
|
||||
if (snapshot.IsDirty)
|
||||
{
|
||||
// We don't need to notify listeners yet because we don't have any **new** computed state. However we do
|
||||
// need to trigger the background work to asynchronously compute the effect of the updates.
|
||||
NotifyBackgroundWorker();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void ProjectChanged(ProjectSnapshotUpdateContext update)
|
||||
{
|
||||
if (update == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(update));
|
||||
}
|
||||
|
||||
if (_projects.TryGetValue(update.UnderlyingProject.Id, out var original))
|
||||
{
|
||||
// This is an update to the project's computed values, so everything should be overwritten
|
||||
var snapshot = original.WithProjectChange(update);
|
||||
_projects[update.UnderlyingProject.Id] = snapshot;
|
||||
|
||||
if (snapshot.IsDirty)
|
||||
{
|
||||
// It's possible that the snapshot can still be dirty if we got a project update while computing state in
|
||||
// the background. We need to trigger the background work to asynchronously compute the effect of the updates.
|
||||
NotifyBackgroundWorker();
|
||||
}
|
||||
|
||||
// Now we need to know if the changes that we applied are significant. If that's the case then
|
||||
// we need to notify listeners.
|
||||
if (snapshot.HasChangesComparedTo(original))
|
||||
{
|
||||
NotifyListeners(new ProjectChangeEventArgs(snapshot, ProjectChangeKind.Changed));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void ProjectRemoved(Project underlyingProject)
|
||||
|
|
@ -105,7 +145,14 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
}
|
||||
}
|
||||
|
||||
private void NotifyListeners(ProjectChangeEventArgs e)
|
||||
// virtual so it can be overridden in tests
|
||||
protected virtual void NotifyBackgroundWorker()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// virtual so it can be overridden in tests
|
||||
protected virtual void NotifyListeners(ProjectChangeEventArgs e)
|
||||
{
|
||||
for (var i = 0; i < _listeners.Count; i++)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Internal;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
|
|
@ -37,5 +39,27 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
public override ProjectExtensibilityAssembly RazorAssembly { get; }
|
||||
|
||||
public ProjectExtensibilityAssembly MvcAssembly { get; }
|
||||
|
||||
public override bool Equals(ProjectExtensibilityConfiguration other)
|
||||
{
|
||||
if (other == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// We're intentionally ignoring the 'Kind' here. That's mostly for diagnostics and doesn't influence any behavior.
|
||||
return Enumerable.SequenceEqual(Assemblies.OrderBy(a => a.Identity.Name), other.Assemblies.OrderBy(a => a.Identity.Name));
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
var hash = new HashCodeCombiner();
|
||||
foreach (var assembly in Assemblies.OrderBy(a => a.Identity.Name))
|
||||
{
|
||||
hash.Add(assembly);
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ using System;
|
|||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
internal sealed class ProjectExtensibilityAssembly
|
||||
internal sealed class ProjectExtensibilityAssembly : IEquatable<ProjectExtensibilityAssembly>
|
||||
{
|
||||
public ProjectExtensibilityAssembly(AssemblyIdentity identity)
|
||||
{
|
||||
|
|
@ -18,5 +18,25 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
}
|
||||
|
||||
public AssemblyIdentity Identity { get; }
|
||||
|
||||
public bool Equals(ProjectExtensibilityAssembly other)
|
||||
{
|
||||
if (other == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Identity.Equals(other.Identity);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Identity.GetHashCode();
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return base.Equals(obj as ProjectExtensibilityAssembly);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,26 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
internal abstract class ProjectExtensibilityConfiguration
|
||||
internal abstract class ProjectExtensibilityConfiguration : IEquatable<ProjectExtensibilityConfiguration>
|
||||
{
|
||||
public abstract IReadOnlyList<ProjectExtensibilityAssembly> Assemblies { get; }
|
||||
|
||||
public abstract ProjectExtensibilityConfigurationKind Kind { get; }
|
||||
|
||||
public abstract ProjectExtensibilityAssembly RazorAssembly { get; }
|
||||
|
||||
public abstract bool Equals(ProjectExtensibilityConfiguration other);
|
||||
|
||||
public abstract override int GetHashCode();
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return base.Equals(obj as ProjectExtensibilityConfiguration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +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;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
internal abstract class ProjectSnapshot
|
||||
{
|
||||
public abstract ProjectExtensibilityConfiguration Configuration { get; }
|
||||
|
||||
public abstract Project UnderlyingProject { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
public abstract void ProjectChanged(Project underlyingProject);
|
||||
|
||||
public abstract void ProjectChanged(ProjectSnapshotUpdateContext update);
|
||||
|
||||
public abstract void ProjectRemoved(Project underlyingProject);
|
||||
|
||||
public abstract void ProjectsCleared();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
internal class ProjectSnapshotUpdateContext
|
||||
{
|
||||
public ProjectSnapshotUpdateContext(Project underlyingProject)
|
||||
{
|
||||
if (underlyingProject == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(underlyingProject));
|
||||
}
|
||||
|
||||
UnderlyingProject = underlyingProject;
|
||||
}
|
||||
|
||||
public Project UnderlyingProject { get; }
|
||||
|
||||
public ProjectExtensibilityConfiguration Configuration { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,299 @@
|
|||
// 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<ProjectSnapshotChangeTrigger>(), 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<ProjectExtensibilityConfiguration>();
|
||||
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<ProjectExtensibilityConfiguration>();
|
||||
|
||||
// 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<ProjectExtensibilityConfiguration>();
|
||||
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<ProjectExtensibilityConfiguration>();
|
||||
|
||||
// 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<ProjectExtensibilityConfiguration>();
|
||||
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<ProjectSnapshotChangeTrigger> 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<DefaultProjectSnapshot>().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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue