Skip to content

Commit

Permalink
SLVS-1693 Fix: issues set to empty after hotspots event (#5886)
Browse files Browse the repository at this point in the history
  • Loading branch information
georgii-borovinskikh-sonarsource authored Dec 16, 2024
1 parent 3902570 commit 1d8a731
Show file tree
Hide file tree
Showing 23 changed files with 614 additions and 271 deletions.
2 changes: 1 addition & 1 deletion src/CFamily.UnitTests/Subprocess/MessageHandlerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ public void HandleMessage_IssuesForAnalyzedFileAreNotIgnored(string fileNameInMe
context.IssueConverter.Invocations.Count.Should().Be(1);
context.IssueConsumer.Invocations.Count.Should().Be(1);

context.IssueConsumer.Verify(x => x.Set(analyzedFile, It.IsAny<IEnumerable<IAnalysisIssue>>()));
context.IssueConsumer.Verify(x => x.SetIssues(analyzedFile, It.IsAny<IEnumerable<IAnalysisIssue>>()));
}

[TestMethod]
Expand Down
2 changes: 1 addition & 1 deletion src/CFamily/Subprocess/MessageHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ private void HandleAnalysisIssue(Message message)
// TextBufferIssueTracker when the file was closed, but the TextBufferIssueTracker will
// still exist and handle the call.
// todo https://sonarsource.atlassian.net/browse/SLVS-1661
issueConsumer.Set(request.Context.File, new[] { issue });
issueConsumer.SetIssues(request.Context.File, new[] { issue });
}

internal /* for testing */ static bool IsIssueForActiveRule(Message message, ICFamilyRulesConfig rulesConfiguration)
Expand Down
38 changes: 0 additions & 38 deletions src/Core.UnitTests/Analysis/AnalysisServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,44 +113,6 @@ public void ScheduleAnalysis_NoEnvironmentSettings_DefaultTimeout()
scheduler.Received().Schedule("file/path", Arg.Any<Action<CancellationToken>>(), AnalysisService.DefaultAnalysisTimeoutMs);
}

[TestMethod]
public void PublishIssues_NoConsumerInStorage_DoesNothing()
{
var issueConsumerStorage = CreateIssueConsumerStorageWithStoredItem(Guid.NewGuid(), null, false);
var testSubject = CreateTestSubject(issueConsumerStorage:issueConsumerStorage);

var act = () => testSubject.PublishIssues("file/path", Guid.NewGuid(), Substitute.For<IEnumerable<IAnalysisIssue>>());

act.Should().NotThrow();
}

[TestMethod]
public void PublishIssues_DifferentAnalysisId_DoesNothing()
{
var analysisId = Guid.NewGuid();
var issueConsumer = Substitute.For<IIssueConsumer>();
var issueConsumerStorage = CreateIssueConsumerStorageWithStoredItem(Guid.NewGuid(), issueConsumer, true);
var testSubject = CreateTestSubject(issueConsumerStorage:issueConsumerStorage);

testSubject.PublishIssues("file/path", analysisId, Substitute.For<IEnumerable<IAnalysisIssue>>());

issueConsumer.DidNotReceiveWithAnyArgs().Set(default, default);
}

[TestMethod]
public void PublishIssues_MatchingConsumer_PublishesIssues()
{
var analysisId = Guid.NewGuid();
var issueConsumer = Substitute.For<IIssueConsumer>();
var issueConsumerStorage = CreateIssueConsumerStorageWithStoredItem(analysisId, issueConsumer, true);
var testSubject = CreateTestSubject(issueConsumerStorage:issueConsumerStorage);
var analysisIssues = Substitute.For<IEnumerable<IAnalysisIssue>>();

testSubject.PublishIssues("file/path", analysisId, analysisIssues);

issueConsumer.Received().Set("file/path", analysisIssues);
}

[DataRow(true)]
[DataRow(false)]
[DataTestMethod]
Expand Down
96 changes: 96 additions & 0 deletions src/Core.UnitTests/Analysis/HotspotsPublisherTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* SonarLint for Visual Studio
* Copyright (C) 2016-2024 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

using SonarLint.VisualStudio.Core.Analysis;
using SonarLint.VisualStudio.TestInfrastructure;

namespace SonarLint.VisualStudio.Core.UnitTests.Analysis;

[TestClass]
public class HotspotPublisherTests
{
private IIssueConsumerStorage issueConsumerStorage;
private IIssueConsumer issueConsumer;
private HotspotPublisher testSubject;

[TestMethod]
public void MefCtor_CheckIsExported() =>
MefTestHelpers.CheckTypeCanBeImported<HotspotPublisher, IHotspotPublisher>(MefTestHelpers.CreateExport<IIssueConsumerStorage>());

[TestMethod]
public void MefCtor_CheckIsSingleton() =>
MefTestHelpers.CheckIsSingletonMefComponent<HotspotPublisher>();

[TestInitialize]
public void TestInitialize()
{
issueConsumerStorage = Substitute.For<IIssueConsumerStorage>();
issueConsumer = Substitute.For<IIssueConsumer>();
testSubject = new HotspotPublisher(issueConsumerStorage);
}

[TestMethod]
public void PublishHotspots_NoConsumerInStorage_DoesNothing()
{
issueConsumerStorage.TryGet(default, out _, out _).ReturnsForAnyArgs(false);

var act = () => testSubject.Publish("file/path", Guid.NewGuid(), Substitute.For<IEnumerable<IAnalysisIssue>>());

act.Should().NotThrow();
issueConsumer.DidNotReceiveWithAnyArgs().SetIssues(default, default);
issueConsumer.DidNotReceiveWithAnyArgs().SetHotspots(default, default);
}

[TestMethod]
public void PublishHotspots_DifferentAnalysisId_DoesNothing()
{
issueConsumerStorage.TryGet("file/path", out Arg.Any<Guid>(), out Arg.Any<IIssueConsumer>())
.Returns(info =>
{
info[1] = Guid.NewGuid();
info[2] = issueConsumer;
return true;
});

testSubject.Publish("file/path", Guid.NewGuid(), Substitute.For<IEnumerable<IAnalysisIssue>>());

issueConsumer.DidNotReceiveWithAnyArgs().SetIssues(default, default);
issueConsumer.DidNotReceiveWithAnyArgs().SetHotspots(default, default);
}

[TestMethod]
public void PublishHotspots_MatchingConsumer_PublishesHotspots()
{
var analysisId = Guid.NewGuid();
var analysisIssues = Substitute.For<IEnumerable<IAnalysisIssue>>();
issueConsumerStorage.TryGet("file/path", out Arg.Any<Guid>(), out Arg.Any<IIssueConsumer>())
.Returns(info =>
{
info[1] = analysisId;
info[2] = issueConsumer;
return true;
});

testSubject.Publish("file/path", analysisId, analysisIssues);

issueConsumer.Received().SetHotspots("file/path", analysisIssues);
issueConsumer.DidNotReceiveWithAnyArgs().SetIssues(default, default);
}
}
96 changes: 96 additions & 0 deletions src/Core.UnitTests/Analysis/IssuePublisherTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* SonarLint for Visual Studio
* Copyright (C) 2016-2024 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

using SonarLint.VisualStudio.Core.Analysis;
using SonarLint.VisualStudio.TestInfrastructure;

namespace SonarLint.VisualStudio.Core.UnitTests.Analysis;

[TestClass]
public class IssuePublisherTests
{
private IIssueConsumerStorage issueConsumerStorage;
private IIssueConsumer issueConsumer;
private IssuePublisher testSubject;

[TestMethod]
public void MefCtor_CheckIsExported() =>
MefTestHelpers.CheckTypeCanBeImported<IssuePublisher, IIssuePublisher>(MefTestHelpers.CreateExport<IIssueConsumerStorage>());

[TestMethod]
public void MefCtor_CheckIsSingleton() =>
MefTestHelpers.CheckIsSingletonMefComponent<IssuePublisher>();

[TestInitialize]
public void TestInitialize()
{
issueConsumerStorage = Substitute.For<IIssueConsumerStorage>();
issueConsumer = Substitute.For<IIssueConsumer>();
testSubject = new IssuePublisher(issueConsumerStorage);
}

[TestMethod]
public void PublishIssues_NoConsumerInStorage_DoesNothing()
{
issueConsumerStorage.TryGet(default, out _, out _).ReturnsForAnyArgs(false);

var act = () => testSubject.Publish("file/path", Guid.NewGuid(), Substitute.For<IEnumerable<IAnalysisIssue>>());

act.Should().NotThrow();
issueConsumer.DidNotReceiveWithAnyArgs().SetIssues(default, default);
issueConsumer.DidNotReceiveWithAnyArgs().SetHotspots(default, default);
}

[TestMethod]
public void PublishIssues_DifferentAnalysisId_DoesNothing()
{
issueConsumerStorage.TryGet("file/path", out Arg.Any<Guid>(), out Arg.Any<IIssueConsumer>())
.Returns(info =>
{
info[1] = Guid.NewGuid();
info[2] = issueConsumer;
return true;
});

testSubject.Publish("file/path", Guid.NewGuid(), Substitute.For<IEnumerable<IAnalysisIssue>>());

issueConsumer.DidNotReceiveWithAnyArgs().SetIssues(default, default);
issueConsumer.DidNotReceiveWithAnyArgs().SetHotspots(default, default);
}

[TestMethod]
public void PublishIssues_MatchingConsumer_PublishesIssues()
{
var analysisId = Guid.NewGuid();
var analysisIssues = Substitute.For<IEnumerable<IAnalysisIssue>>();
issueConsumerStorage.TryGet("file/path", out Arg.Any<Guid>(), out Arg.Any<IIssueConsumer>())
.Returns(info =>
{
info[1] = analysisId;
info[2] = issueConsumer;
return true;
});

testSubject.Publish("file/path", analysisId, analysisIssues);

issueConsumer.Received().SetIssues("file/path", analysisIssues);
issueConsumer.DidNotReceiveWithAnyArgs().SetHotspots(default, default);
}
}
9 changes: 0 additions & 9 deletions src/Core/Analysis/AnalysisService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,6 @@ public bool IsAnalysisSupported(IEnumerable<AnalysisLanguage> languages)
return analyzerController.IsAnalysisSupported(languages);
}

public void PublishIssues(string filePath, Guid analysisId, IEnumerable<IAnalysisIssue> issues)
{
if (issueConsumerStorage.TryGet(filePath, out var currentAnalysisId, out var issueConsumer)
&& analysisId == currentAnalysisId)
{
issueConsumer.Set(filePath, issues);
}
}

public void ScheduleAnalysis(string filePath,
Guid analysisId,
IEnumerable<AnalysisLanguage> detectedLanguages,
Expand Down
38 changes: 38 additions & 0 deletions src/Core/Analysis/HotspotPublisher.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* SonarLint for Visual Studio
* Copyright (C) 2016-2024 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

using System.ComponentModel.Composition;

namespace SonarLint.VisualStudio.Core.Analysis;

[Export(typeof(IHotspotPublisher))]
[PartCreationPolicy(CreationPolicy.Shared)]
[method:ImportingConstructor]
internal class HotspotPublisher(IIssueConsumerStorage issueConsumerStorage) : IHotspotPublisher
{
public void Publish(string filePath, Guid analysisId, IEnumerable<IAnalysisIssue> findings)
{
if (issueConsumerStorage.TryGet(filePath, out var currentAnalysisId, out var issueConsumer)
&& analysisId == currentAnalysisId)
{
issueConsumer.SetHotspots(filePath, findings);
}
}
}
5 changes: 0 additions & 5 deletions src/Core/Analysis/IAnalysisService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,6 @@ public interface IAnalysisService
/// </summary>
bool IsAnalysisSupported(IEnumerable<AnalysisLanguage> languages);

/// <summary>
/// Handles analysis results
/// </summary>
void PublishIssues(string filePath, Guid analysisId, IEnumerable<IAnalysisIssue> issues);

/// <summary>
/// Starts analysis for <paramref name="filePath"/>
/// </summary>
Expand Down
33 changes: 33 additions & 0 deletions src/Core/Analysis/IFindingsPublisher.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* SonarLint for Visual Studio
* Copyright (C) 2016-2024 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

namespace SonarLint.VisualStudio.Core.Analysis;

public interface IFindingsPublisher
{
/// <summary>
/// Handles analysis results
/// </summary>
void Publish(string filePath, Guid analysisId, IEnumerable<IAnalysisIssue> findings);
}

public interface IIssuePublisher : IFindingsPublisher;

public interface IHotspotPublisher : IFindingsPublisher;
3 changes: 2 additions & 1 deletion src/Core/Analysis/IIssueConsumer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@ namespace SonarLint.VisualStudio.Core.Analysis;
/// </summary>
public interface IIssueConsumer
{
void Set(string path, IEnumerable<IAnalysisIssue> issues);
void SetIssues(string path, IEnumerable<IAnalysisIssue> issues);
void SetHotspots(string path, IEnumerable<IAnalysisIssue> hotspots);
}
Loading

0 comments on commit 1d8a731

Please sign in to comment.