diff --git a/src/Core/Binding/IActiveSolutionBoundTracker.cs b/src/Core/Binding/IActiveSolutionBoundTracker.cs
index 650c3a2e7d..77821efe2b 100644
--- a/src/Core/Binding/IActiveSolutionBoundTracker.cs
+++ b/src/Core/Binding/IActiveSolutionBoundTracker.cs
@@ -22,6 +22,11 @@
namespace SonarLint.VisualStudio.Core.Binding
{
+ public interface IActiveSolutionChangedHandler
+ {
+ void HandleBindingChange(bool isBindingCleared);
+ }
+
///
/// Allows checking if the current Visual Studio solution is bound to a SonarQube project or not
///
diff --git a/src/Integration.UnitTests/MefServices/ActiveSolutionBoundTrackerTests.cs b/src/Integration.UnitTests/MefServices/ActiveSolutionBoundTrackerTests.cs
index bc22442154..72f88d38f8 100644
--- a/src/Integration.UnitTests/MefServices/ActiveSolutionBoundTrackerTests.cs
+++ b/src/Integration.UnitTests/MefServices/ActiveSolutionBoundTrackerTests.cs
@@ -218,7 +218,7 @@ public void ActiveSolutionBoundTracker_Changes()
ConfigureSolutionBinding(null);
// Act
- host.VisualStateManager.ClearBoundProject();
+ testSubject.HandleBindingChange(true);
// Assert
testSubject.CurrentConfiguration.Mode.Should().Be(SonarLintMode.Standalone, "Unbound solution should report false activation");
@@ -235,7 +235,7 @@ public void ActiveSolutionBoundTracker_Changes()
// Case 2: Set bound project
ConfigureSolutionBinding(boundProject);
// Act
- host.VisualStateManager.SetBoundProject(new Uri("http://localhost"), null, "project123");
+ testSubject.HandleBindingChange(false);
// Assert
testSubject.CurrentConfiguration.Mode.Should().Be(SonarLintMode.Connected, "Bound solution should report true activation");
@@ -307,7 +307,7 @@ public void ActiveSolutionBoundTracker_Changes()
// Act
testSubject.Dispose();
ConfigureSolutionBinding(boundProject);
- host.VisualStateManager.ClearBoundProject();
+ testSubject.HandleBindingChange(true);
// Assert
eventCounter.PreSolutionBindingChangedCount.Should().Be(5, "Once disposed should stop raising the event");
@@ -349,13 +349,53 @@ public void OnBindingStateChanged_NewConfiguration_EventsRaisedInCorrectOrder()
// Assert
// Different config so event should be raised
eventCounter.PreSolutionBindingChangedCount.Should().Be(1);
+ eventCounter.PreSolutionBindingUpdatedCount.Should().Be(0);
+ eventCounter.SolutionBindingChangedCount.Should().Be(1);
eventCounter.SolutionBindingUpdatedCount.Should().Be(0);
+
+ eventCounter.RaisedEventNames.Should().HaveCount(2);
+ eventCounter.RaisedEventNames[0].Should().Be(nameof(testSubject.PreSolutionBindingChanged));
+ eventCounter.RaisedEventNames[1].Should().Be(nameof(testSubject.SolutionBindingChanged));
+ }
+ }
+
+ [TestMethod]
+ public void HandleBindingChange_NewConfiguration_EventsRaisedInCorrectOrder()
+ {
+ // Arrange
+ var initialProject = new BoundServerProject(
+ "solution",
+ "projectKey",
+ new ServerConnection.SonarCloud("myOrgKey"));
+
+ // Set the current configuration used by the tracker
+ ConfigureSolutionBinding(initialProject);
+ using (var testSubject = CreateTestSubject(this.host, this.activeSolutionTracker, this.configProvider, loggerMock.Object))
+ {
+ var eventCounter = new EventCounter(testSubject);
+
+ // Now configure the provider to return a different configuration
+ var newProject = new BoundServerProject(
+ "solution",
+ "projectKey",
+ new ServerConnection.SonarCloud("myOrgKey_DIFFERENT"));
+ ConfigureSolutionBinding(newProject);
+
+ // Act - simulate the binding state changing in the Team explorer section.
+ // The project configuration hasn't changed (it doesn't matter what properties
+ // we pass here; they aren't used when raising the event.)
+ testSubject.HandleBindingChange(false);
+
+ // Assert
+ // Different config so event should be raised
eventCounter.PreSolutionBindingChangedCount.Should().Be(1);
+ eventCounter.PreSolutionBindingUpdatedCount.Should().Be(0);
+ eventCounter.SolutionBindingChangedCount.Should().Be(1);
eventCounter.SolutionBindingUpdatedCount.Should().Be(0);
eventCounter.RaisedEventNames.Should().HaveCount(2);
- eventCounter.RaisedEventNames[0].Should().Be("PreSolutionBindingChanged");
- eventCounter.RaisedEventNames[1].Should().Be("SolutionBindingChanged");
+ eventCounter.RaisedEventNames[0].Should().Be(nameof(testSubject.PreSolutionBindingChanged));
+ eventCounter.RaisedEventNames[1].Should().Be(nameof(testSubject.SolutionBindingChanged));
}
}
@@ -521,6 +561,25 @@ public void SolutionBindingUpdated_WhenClearBoundProject_NotRaised()
eventCounter.SolutionBindingChangedCount.Should().Be(0);
}
}
+
+ [TestMethod]
+ public void HandleBindingChange_WhenClearBoundProject_NotRaised()
+ {
+ // Arrange
+ using (var testSubject = CreateTestSubject(this.host, this.activeSolutionTracker, this.configProvider, loggerMock.Object))
+ {
+ var eventCounter = new EventCounter(testSubject);
+
+ // Act
+ testSubject.HandleBindingChange(true);
+
+ // Assert
+ eventCounter.PreSolutionBindingUpdatedCount.Should().Be(0);
+ eventCounter.SolutionBindingUpdatedCount.Should().Be(0);
+ eventCounter.PreSolutionBindingChangedCount.Should().Be(0);
+ eventCounter.SolutionBindingChangedCount.Should().Be(0);
+ }
+ }
[TestMethod]
public void SolutionBindingUpdated_WhenSetBoundProject_EventsRaisedInExpectedOrder()
@@ -540,8 +599,31 @@ public void SolutionBindingUpdated_WhenSetBoundProject_EventsRaisedInExpectedOrd
eventCounter.SolutionBindingChangedCount.Should().Be(0);
eventCounter.RaisedEventNames.Should().HaveCount(2);
- eventCounter.RaisedEventNames[0].Should().Be("PreSolutionBindingUpdated");
- eventCounter.RaisedEventNames[1].Should().Be("SolutionBindingUpdated");
+ eventCounter.RaisedEventNames[0].Should().Be(nameof(testSubject.PreSolutionBindingUpdated));
+ eventCounter.RaisedEventNames[1].Should().Be(nameof(testSubject.SolutionBindingUpdated));
+ }
+ }
+
+ [TestMethod]
+ public void HandleBindingChange_WhenSetBoundProject_EventsRaisedInExpectedOrder()
+ {
+ // Arrange
+ using (var testSubject = CreateTestSubject(this.host, this.activeSolutionTracker, this.configProvider, loggerMock.Object))
+ {
+ var eventCounter = new EventCounter(testSubject);
+
+ // Act
+ testSubject.HandleBindingChange(false);
+
+ // Assert
+ eventCounter.PreSolutionBindingUpdatedCount.Should().Be(1);
+ eventCounter.SolutionBindingUpdatedCount.Should().Be(1);
+ eventCounter.PreSolutionBindingChangedCount.Should().Be(0);
+ eventCounter.SolutionBindingChangedCount.Should().Be(0);
+
+ eventCounter.RaisedEventNames.Should().HaveCount(2);
+ eventCounter.RaisedEventNames[0].Should().Be(nameof(testSubject.PreSolutionBindingUpdated));
+ eventCounter.RaisedEventNames[1].Should().Be(nameof(testSubject.SolutionBindingUpdated));
}
}
@@ -566,8 +648,8 @@ public void GitRepoUpdated_SolutionBindingUpdatedEventsRaised()
eventCounter.SolutionBindingChangedCount.Should().Be(0);
eventCounter.RaisedEventNames.Should().HaveCount(2);
- eventCounter.RaisedEventNames[0].Should().Be("PreSolutionBindingUpdated");
- eventCounter.RaisedEventNames[1].Should().Be("SolutionBindingUpdated");
+ eventCounter.RaisedEventNames[0].Should().Be(nameof(testSubject.PreSolutionBindingUpdated));
+ eventCounter.RaisedEventNames[1].Should().Be(nameof(testSubject.SolutionBindingUpdated));
}
}
diff --git a/src/Integration/MefServices/ActiveSolutionBoundTracker.cs b/src/Integration/MefServices/ActiveSolutionBoundTracker.cs
index a644023f0b..4c0f4fa5f1 100644
--- a/src/Integration/MefServices/ActiveSolutionBoundTracker.cs
+++ b/src/Integration/MefServices/ActiveSolutionBoundTracker.cs
@@ -41,8 +41,9 @@ namespace SonarLint.VisualStudio.Integration
/// UIContext.
///
[Export(typeof(IActiveSolutionBoundTracker))]
+ [Export(typeof(IActiveSolutionChangedHandler))]
[PartCreationPolicy(CreationPolicy.Shared)]
- internal sealed class ActiveSolutionBoundTracker : IActiveSolutionBoundTracker, IDisposable, IPartImportsSatisfiedNotification
+ internal sealed class ActiveSolutionBoundTracker : IActiveSolutionBoundTracker, IActiveSolutionChangedHandler, IDisposable, IPartImportsSatisfiedNotification
{
private readonly IHost extensionHost;
private readonly IActiveSolutionTracker solutionTracker;
@@ -52,6 +53,7 @@ internal sealed class ActiveSolutionBoundTracker : IActiveSolutionBoundTracker,
private readonly IConfigScopeUpdater configScopeUpdater;
private readonly ILogger logger;
private readonly uint boundSolutionContextCookie;
+ private bool disposed;
public event EventHandler PreSolutionBindingChanged;
public event EventHandler SolutionBindingChanged;
@@ -95,6 +97,21 @@ public ActiveSolutionBoundTracker([Import(typeof(SVsServiceProvider))] IServiceP
this.gitEventsMonitor.HeadChanged += GitEventsMonitor_HeadChanged;
}
+ public void HandleBindingChange(bool isBindingCleared)
+ {
+ if (disposed)
+ {
+ return;
+ }
+
+ this.RaiseAnalyzersChangedIfBindingChanged(GetBindingConfiguration(), isBindingCleared);
+ }
+
+ private void OnBindingStateChanged(object sender, BindingStateEventArgs e)
+ {
+ HandleBindingChange(e.IsBindingCleared);
+ }
+
private BindingConfiguration GetBindingConfiguration()
{
return configurationProvider.GetConfiguration();
@@ -159,11 +176,6 @@ await Core.WebServiceHelper.SafeServiceCallAsync(() => sonarQubeService.ConnectA
}
}
- private void OnBindingStateChanged(object sender, BindingStateEventArgs e)
- {
- this.RaiseAnalyzersChangedIfBindingChanged(GetBindingConfiguration(), e.IsBindingCleared);
- }
-
private void RaiseAnalyzersChangedIfBindingChanged(BindingConfiguration newBindingConfiguration, bool? isBindingCleared = null)
{
configScopeUpdater.UpdateConfigScopeForCurrentSolution(newBindingConfiguration.Project);
@@ -206,6 +218,7 @@ private void Dispose(bool disposing)
{
if (disposing)
{
+ this.disposed = true;
this.solutionTracker.ActiveSolutionChanged -= this.OnActiveSolutionChanged;
this.extensionHost.VisualStateManager.BindingStateChanged -= this.OnBindingStateChanged;
this.gitEventsMonitor.HeadChanged -= GitEventsMonitor_HeadChanged;