Skip to content

Commit 26a4f43

Browse files
SLVS-1537 Add ISolutionRoslynAnalyzerManager integration to ActiveSolutionBoundTracker (#5768)
1 parent e7b9387 commit 26a4f43

File tree

2 files changed

+102
-8
lines changed

2 files changed

+102
-8
lines changed

src/Integration.UnitTests/MefServices/ActiveSolutionBoundTrackerTests.cs

Lines changed: 93 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
using Moq;
2626
using SonarLint.VisualStudio.Core;
2727
using SonarLint.VisualStudio.Core.Binding;
28+
using SonarLint.VisualStudio.Infrastructure.VS.Roslyn;
2829
using SonarLint.VisualStudio.TestInfrastructure;
2930
using SonarQube.Client;
3031
using SonarQube.Client.Models;
@@ -188,11 +189,73 @@ public void ActiveSolutionBoundTracker_UnBoundProject_NullPassedToConfigScopeUpd
188189
configScopeUpdaterMock.Verify(x => x.UpdateConfigScopeForCurrentSolution(null));
189190
configScopeUpdaterMock.VerifyNoOtherCalls();
190191
}
192+
193+
[TestMethod]
194+
public void Ctor_NoSolution_CallsAnalyzerManager()
195+
{
196+
var configScopeUpdaterMock = new Mock<IConfigScopeUpdater>();
197+
var analyzerManager = new Mock<ISolutionRoslynAnalyzerManager>();
198+
199+
activeSolutionTracker.CurrentSolutionName = null;
200+
ConfigureService(isConnected: false);
201+
202+
var testSubject = CreateTestSubject(
203+
activeSolutionTracker,
204+
configProvider,
205+
loggerMock.Object,
206+
configScopeUpdater: configScopeUpdaterMock.Object,
207+
sonarQubeService: sonarQubeServiceMock.Object,
208+
solutionRoslynAnalyzerManager: analyzerManager.Object);
209+
210+
analyzerManager.Verify(x => x.OnSolutionChanged(null, BindingConfiguration.Standalone));
211+
}
212+
213+
[TestMethod]
214+
public void Ctor_StandaloneSolution_CallsAnalyzerManager()
215+
{
216+
var configScopeUpdaterMock = new Mock<IConfigScopeUpdater>();
217+
var analyzerManager = new Mock<ISolutionRoslynAnalyzerManager>();
218+
219+
activeSolutionTracker.CurrentSolutionName = "solution123";
220+
ConfigureService(isConnected: false);
221+
222+
var testSubject = CreateTestSubject(
223+
activeSolutionTracker,
224+
configProvider,
225+
loggerMock.Object,
226+
configScopeUpdater: configScopeUpdaterMock.Object,
227+
sonarQubeService: sonarQubeServiceMock.Object,
228+
solutionRoslynAnalyzerManager: analyzerManager.Object);
229+
230+
analyzerManager.Verify(x => x.OnSolutionChanged("solution123", BindingConfiguration.Standalone));
231+
}
232+
233+
[TestMethod]
234+
public void Ctor_BoundSolution_CallsAnalyzerManager()
235+
{
236+
var configScopeUpdaterMock = new Mock<IConfigScopeUpdater>();
237+
var analyzerManager = new Mock<ISolutionRoslynAnalyzerManager>();
238+
239+
activeSolutionTracker.CurrentSolutionName = "solution123";
240+
ConfigureService(isConnected: false);
241+
ConfigureSolutionBinding(boundSonarQubeProject);
242+
243+
var testSubject = CreateTestSubject(
244+
activeSolutionTracker,
245+
configProvider,
246+
loggerMock.Object,
247+
configScopeUpdater: configScopeUpdaterMock.Object,
248+
sonarQubeService: sonarQubeServiceMock.Object,
249+
solutionRoslynAnalyzerManager: analyzerManager.Object);
250+
251+
analyzerManager.Verify(x => x.OnSolutionChanged("solution123", It.Is<BindingConfiguration>(y => y.Mode == SonarLintMode.Connected && y.Project == boundSonarQubeProject)));
252+
}
191253

192254
[TestMethod]
193255
public void ActiveSolutionBoundTracker_Changes()
194256
{
195257
var configScopeUpdaterMock = new Mock<IConfigScopeUpdater>();
258+
var analyzerManager = new Mock<ISolutionRoslynAnalyzerManager>();
196259

197260
ConfigureService(isConnected: false);
198261
ConfigureSolutionBinding(boundSonarQubeProject);
@@ -202,7 +265,8 @@ public void ActiveSolutionBoundTracker_Changes()
202265
configProvider,
203266
loggerMock.Object,
204267
configScopeUpdater: configScopeUpdaterMock.Object,
205-
sonarQubeService: sonarQubeServiceMock.Object);
268+
sonarQubeService: sonarQubeServiceMock.Object,
269+
solutionRoslynAnalyzerManager: analyzerManager.Object);
206270
var eventCounter = new EventCounter(testSubject);
207271

208272
// Sanity
@@ -215,6 +279,7 @@ public void ActiveSolutionBoundTracker_Changes()
215279

216280
// Case 1: Clear bound project
217281
ConfigureSolutionBinding(null);
282+
activeSolutionTracker.CurrentSolutionName = "solution1";
218283

219284
// Act
220285
testSubject.HandleBindingChange(true);
@@ -225,7 +290,8 @@ public void ActiveSolutionBoundTracker_Changes()
225290
eventCounter.SolutionBindingChangedCount.Should().Be(1, "Unbind should trigger reanalysis");
226291
eventCounter.PreSolutionBindingUpdatedCount.Should().Be(0, "Unbind should not trigger change");
227292
eventCounter.SolutionBindingUpdatedCount.Should().Be(0, "Unbind should not trigger change");
228-
configScopeUpdaterMock.Verify(x => x.UpdateConfigScopeForCurrentSolution(It.IsAny<BoundServerProject>()), Times.Exactly(1));
293+
configScopeUpdaterMock.Verify(x => x.UpdateConfigScopeForCurrentSolution(null), Times.Exactly(1));
294+
VerifyAnalyzerUpdatedAndClearMock(analyzerManager, "solution1", false);
229295
VerifyAndResetBoundSolutionUiContextMock(isActive: false);
230296

231297
VerifyServiceDisconnect(Times.Never());
@@ -243,6 +309,7 @@ public void ActiveSolutionBoundTracker_Changes()
243309
eventCounter.PreSolutionBindingUpdatedCount.Should().Be(0, "Bind should not trigger update event");
244310
eventCounter.SolutionBindingUpdatedCount.Should().Be(0, "Bind should not trigger update event");
245311
configScopeUpdaterMock.Verify(x => x.UpdateConfigScopeForCurrentSolution(It.IsAny<BoundServerProject>()), Times.Exactly(2));
312+
VerifyAnalyzerUpdatedAndClearMock(analyzerManager, "solution1", true);
246313
VerifyAndResetBoundSolutionUiContextMock(isActive: true);
247314

248315
// Notifications from the Team Explorer should not trigger connect/disconnect
@@ -261,6 +328,7 @@ public void ActiveSolutionBoundTracker_Changes()
261328
eventCounter.PreSolutionBindingUpdatedCount.Should().Be(0, "Solution change should not trigger update event");
262329
eventCounter.SolutionBindingUpdatedCount.Should().Be(0, "Solution change should not trigger update event");
263330
configScopeUpdaterMock.Verify(x => x.UpdateConfigScopeForCurrentSolution(It.IsAny<BoundServerProject>()), Times.Exactly(3));
331+
VerifyAnalyzerUpdatedAndClearMock(analyzerManager, null, false);
264332
VerifyAndResetBoundSolutionUiContextMock(isActive: false);
265333

266334
// Closing an unbound solution should not call disconnect/connect
@@ -270,7 +338,7 @@ public void ActiveSolutionBoundTracker_Changes()
270338
// Case 4: Load a bound solution
271339
ConfigureSolutionBinding(boundSonarQubeProject);
272340
// Act
273-
activeSolutionTracker.SimulateActiveSolutionChanged(isSolutionOpen: true, "solution");
341+
activeSolutionTracker.SimulateActiveSolutionChanged(isSolutionOpen: true, "solution2");
274342

275343
// Assert
276344
testSubject.CurrentConfiguration.Mode.Should().Be(SonarLintMode.Connected, "Bound respond to solution change event and report bound");
@@ -279,6 +347,7 @@ public void ActiveSolutionBoundTracker_Changes()
279347
eventCounter.PreSolutionBindingUpdatedCount.Should().Be(0, "Bind should not trigger update event");
280348
eventCounter.SolutionBindingUpdatedCount.Should().Be(0, "Bind should not trigger update event");
281349
configScopeUpdaterMock.Verify(x => x.UpdateConfigScopeForCurrentSolution(It.IsAny<BoundServerProject>()), Times.Exactly(4));
350+
VerifyAnalyzerUpdatedAndClearMock(analyzerManager, "solution2", true);
282351
VerifyAndResetBoundSolutionUiContextMock(isActive: true);
283352

284353
// Loading a bound solution should call connect
@@ -296,6 +365,7 @@ public void ActiveSolutionBoundTracker_Changes()
296365
eventCounter.PreSolutionBindingUpdatedCount.Should().Be(0, "Solution change should not trigger update event");
297366
eventCounter.SolutionBindingUpdatedCount.Should().Be(0, "Solution change should not trigger update event");
298367
configScopeUpdaterMock.Verify(x => x.UpdateConfigScopeForCurrentSolution(It.IsAny<BoundServerProject>()), Times.Exactly(5));
368+
VerifyAnalyzerUpdatedAndClearMock(analyzerManager, null, false);
299369
VerifyAndResetBoundSolutionUiContextMock(isActive: false);
300370

301371
// SonarQubeService.Disconnect should be called since the WPF DisconnectCommand is not available
@@ -312,11 +382,27 @@ public void ActiveSolutionBoundTracker_Changes()
312382
eventCounter.PreSolutionBindingChangedCount.Should().Be(5, "Once disposed should stop raising the event");
313383
eventCounter.SolutionBindingChangedCount.Should().Be(5, "Once disposed should stop raising the event");
314384
configScopeUpdaterMock.Verify(x => x.UpdateConfigScopeForCurrentSolution(It.IsAny<BoundServerProject>()), Times.Exactly(5));
385+
analyzerManager.Invocations.Should().BeEmpty();
315386
// SonarQubeService.Disconnect should be called since the WPF DisconnectCommand is not available
316387
VerifyServiceDisconnect(Times.Once());
317388
VerifyServiceConnect(Times.Once());
318389
}
319390

391+
private void VerifyAnalyzerUpdatedAndClearMock(Mock<ISolutionRoslynAnalyzerManager> analyzerManager, string solutionName, bool bound)
392+
{
393+
if (bound)
394+
{
395+
analyzerManager.Verify(x => x.OnSolutionChanged(solutionName,
396+
It.Is<BindingConfiguration>(y => y.Mode == SonarLintMode.Connected && y.Project == boundSonarQubeProject)),
397+
Times.Exactly(1));
398+
}
399+
else
400+
{
401+
analyzerManager.Verify(x => x.OnSolutionChanged(solutionName, BindingConfiguration.Standalone), Times.Exactly(1));
402+
}
403+
analyzerManager.Invocations.Clear();
404+
}
405+
320406
[TestMethod]
321407
public void UpdateConnection_WasDisconnected_NewSolutionIsUnbound_NoConnectOrDisconnectCalls()
322408
{
@@ -517,13 +603,15 @@ private ActiveSolutionBoundTracker CreateTestSubject(
517603
ILogger logger = null,
518604
IBoundSolutionGitMonitor gitEvents = null,
519605
IConfigScopeUpdater configScopeUpdater = null,
520-
ISonarQubeService sonarQubeService = null)
606+
ISonarQubeService sonarQubeService = null,
607+
ISolutionRoslynAnalyzerManager solutionRoslynAnalyzerManager = null)
521608
{
522609
configScopeUpdater ??= Mock.Of<IConfigScopeUpdater>();
523610
logger ??= new TestLogger(logToConsole: true);
524611
gitEvents ??= Mock.Of<IBoundSolutionGitMonitor>();
525612
sonarQubeService ??= Mock.Of<ISonarQubeService>();
526-
return new ActiveSolutionBoundTracker(serviceProvider, solutionTracker, configScopeUpdater, logger, gitEvents, configurationProvider, sonarQubeService);
613+
solutionRoslynAnalyzerManager ??= Mock.Of<ISolutionRoslynAnalyzerManager>();
614+
return new ActiveSolutionBoundTracker(serviceProvider, solutionTracker, configScopeUpdater, solutionRoslynAnalyzerManager, logger, gitEvents, configurationProvider, sonarQubeService);
527615
}
528616

529617
private void ConfigureService(bool isConnected)

src/Integration/MefServices/ActiveSolutionBoundTracker.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
using Microsoft.VisualStudio.Shell.Interop;
2424
using SonarLint.VisualStudio.Core;
2525
using SonarLint.VisualStudio.Core.Binding;
26+
using SonarLint.VisualStudio.Infrastructure.VS.Roslyn;
2627
using SonarQube.Client;
2728
using ErrorHandler = Microsoft.VisualStudio.ErrorHandler;
2829

@@ -47,6 +48,7 @@ internal sealed class ActiveSolutionBoundTracker : IActiveSolutionBoundTracker,
4748
private readonly IVsMonitorSelection vsMonitorSelection;
4849
private readonly IBoundSolutionGitMonitor gitEventsMonitor;
4950
private readonly IConfigScopeUpdater configScopeUpdater;
51+
private readonly ISolutionRoslynAnalyzerManager solutionRoslynAnalyzerManager;
5052
private readonly ILogger logger;
5153
private readonly uint boundSolutionContextCookie;
5254
private bool disposed;
@@ -61,6 +63,7 @@ internal sealed class ActiveSolutionBoundTracker : IActiveSolutionBoundTracker,
6163
public ActiveSolutionBoundTracker([Import(typeof(SVsServiceProvider))] IServiceProvider serviceProvider,
6264
IActiveSolutionTracker activeSolutionTracker,
6365
IConfigScopeUpdater configScopeUpdater,
66+
ISolutionRoslynAnalyzerManager solutionRoslynAnalyzerManager,
6467
ILogger logger,
6568
IBoundSolutionGitMonitor gitEventsMonitor,
6669
IConfigurationProvider configurationProvider,
@@ -78,11 +81,13 @@ public ActiveSolutionBoundTracker([Import(typeof(SVsServiceProvider))] IServiceP
7881
this.configurationProvider = configurationProvider;
7982
this.sonarQubeService = sonarQubeService;
8083
this.configScopeUpdater = configScopeUpdater;
84+
this.solutionRoslynAnalyzerManager = solutionRoslynAnalyzerManager;
8185

8286
// The solution changed inside the IDE
8387
solutionTracker.ActiveSolutionChanged += OnActiveSolutionChanged;
8488

8589
CurrentConfiguration = GetBindingConfiguration();
90+
solutionRoslynAnalyzerManager.OnSolutionChanged(activeSolutionTracker.CurrentSolutionName, CurrentConfiguration);
8691

8792
SetBoundSolutionUIContext();
8893

@@ -96,7 +101,7 @@ public void HandleBindingChange(bool isBindingCleared)
96101
return;
97102
}
98103

99-
this.RaiseAnalyzersChangedIfBindingChanged(GetBindingConfiguration(), isBindingCleared);
104+
this.RaiseAnalyzersChangedIfBindingChanged(GetBindingConfiguration(), solutionTracker.CurrentSolutionName, isBindingCleared);
100105
}
101106

102107
private BindingConfiguration GetBindingConfiguration()
@@ -126,7 +131,7 @@ private async void OnActiveSolutionChanged(object sender, ActiveSolutionChangedE
126131

127132
gitEventsMonitor.Refresh();
128133

129-
RaiseAnalyzersChangedIfBindingChanged(connectionUpdatedSuccessfully ? newBindingConfiguration : BindingConfiguration.Standalone);
134+
RaiseAnalyzersChangedIfBindingChanged(connectionUpdatedSuccessfully ? newBindingConfiguration : BindingConfiguration.Standalone, args.SolutionName);
130135
}
131136
catch (Exception ex) when (!ErrorHandler.IsCriticalException(ex))
132137
{
@@ -161,9 +166,10 @@ private async Task<bool> UpdateConnectionAsync(BindingConfiguration bindingConfi
161166
return isConnected;
162167
}
163168

164-
private void RaiseAnalyzersChangedIfBindingChanged(BindingConfiguration newBindingConfiguration, bool? isBindingCleared = null)
169+
private void RaiseAnalyzersChangedIfBindingChanged(BindingConfiguration newBindingConfiguration, string solutionName, bool? isBindingCleared = null)
165170
{
166171
configScopeUpdater.UpdateConfigScopeForCurrentSolution(newBindingConfiguration.Project);
172+
solutionRoslynAnalyzerManager.OnSolutionChanged(solutionName, newBindingConfiguration);
167173

168174
if (!CurrentConfiguration.Equals(newBindingConfiguration))
169175
{

0 commit comments

Comments
 (0)