From 044fcb8ac433c6bf7259c2752a63f650323068f6 Mon Sep 17 00:00:00 2001 From: Georgii Borovinskikh <117642191+georgii-borovinskikh-sonarsource@users.noreply.github.com> Date: Tue, 17 Dec 2024 14:54:20 +0100 Subject: [PATCH] SLVS-1705 Adapt AnalysisStatusNotifier to issue streaming (#5899) --- .../Analysis/HotspotsPublisherTests.cs | 4 ++ .../Analysis/IssuePublisherTests.cs | 4 ++ src/Core/Analysis/HotspotPublisher.cs | 2 + src/Core/Analysis/IAnalysisStatusNotifier.cs | 3 +- src/Core/Analysis/IFindingsPublisher.cs | 1 + src/Core/Analysis/IssuePublisher.cs | 2 + src/Core/CoreStrings.Designer.cs | 19 ++++++- src/Core/CoreStrings.resx | 6 ++ .../Analysis/AnalysisStatusNotifierTests.cs | 45 +++++++++------ .../Analysis/AnalysisStatusNotifier.cs | 13 +++-- .../Analysis/AnalysisStrings.Designer.cs | 3 +- .../Analysis/AnalysisStrings.resx | 2 +- .../Analysis/RaisedFindingProcessorTests.cs | 57 ++++++++++++++----- .../Analysis/RaisedFindingProcessor.cs | 2 +- .../Analysis/SLCoreAnalyzerTests.cs | 4 +- src/SLCore/Analysis/SLCoreAnalyzer.cs | 6 ++ 16 files changed, 128 insertions(+), 45 deletions(-) diff --git a/src/Core.UnitTests/Analysis/HotspotsPublisherTests.cs b/src/Core.UnitTests/Analysis/HotspotsPublisherTests.cs index 346672f84f..bcbe605bb4 100644 --- a/src/Core.UnitTests/Analysis/HotspotsPublisherTests.cs +++ b/src/Core.UnitTests/Analysis/HotspotsPublisherTests.cs @@ -46,6 +46,10 @@ public void TestInitialize() testSubject = new HotspotPublisher(issueConsumerStorage); } + [TestMethod] + public void FindingsType_ReturnsCorrectValue() => + testSubject.FindingsType.Should().Be(CoreStrings.FindingType_Hotspot); + [TestMethod] public void PublishHotspots_NoConsumerInStorage_DoesNothing() { diff --git a/src/Core.UnitTests/Analysis/IssuePublisherTests.cs b/src/Core.UnitTests/Analysis/IssuePublisherTests.cs index 237d187890..953b1999fa 100644 --- a/src/Core.UnitTests/Analysis/IssuePublisherTests.cs +++ b/src/Core.UnitTests/Analysis/IssuePublisherTests.cs @@ -46,6 +46,10 @@ public void TestInitialize() testSubject = new IssuePublisher(issueConsumerStorage); } + [TestMethod] + public void FindingsType_ReturnsCorrectValue() => + testSubject.FindingsType.Should().Be(CoreStrings.FindingType_Issue); + [TestMethod] public void PublishIssues_NoConsumerInStorage_DoesNothing() { diff --git a/src/Core/Analysis/HotspotPublisher.cs b/src/Core/Analysis/HotspotPublisher.cs index 9cbe88f6af..0e061770ae 100644 --- a/src/Core/Analysis/HotspotPublisher.cs +++ b/src/Core/Analysis/HotspotPublisher.cs @@ -27,6 +27,8 @@ namespace SonarLint.VisualStudio.Core.Analysis; [method:ImportingConstructor] internal class HotspotPublisher(IIssueConsumerStorage issueConsumerStorage) : IHotspotPublisher { + public string FindingsType => CoreStrings.FindingType_Hotspot; + public void Publish(string filePath, Guid analysisId, IEnumerable findings) { if (issueConsumerStorage.TryGet(filePath, out var currentAnalysisId, out var issueConsumer) diff --git a/src/Core/Analysis/IAnalysisStatusNotifier.cs b/src/Core/Analysis/IAnalysisStatusNotifier.cs index 8c79f68af7..d33314f819 100644 --- a/src/Core/Analysis/IAnalysisStatusNotifier.cs +++ b/src/Core/Analysis/IAnalysisStatusNotifier.cs @@ -25,7 +25,8 @@ namespace SonarLint.VisualStudio.Core.Analysis public interface IAnalysisStatusNotifier { void AnalysisStarted(); - void AnalysisFinished(int issueCount, TimeSpan analysisTime); + void AnalysisProgressed(int issueCount, string findingType, bool isIntermediate); + void AnalysisFinished(TimeSpan analysisTime); void AnalysisCancelled(); void AnalysisFailed(Exception ex); void AnalysisFailed(string failureMessage); diff --git a/src/Core/Analysis/IFindingsPublisher.cs b/src/Core/Analysis/IFindingsPublisher.cs index 89bb3e6203..dd38205afc 100644 --- a/src/Core/Analysis/IFindingsPublisher.cs +++ b/src/Core/Analysis/IFindingsPublisher.cs @@ -22,6 +22,7 @@ namespace SonarLint.VisualStudio.Core.Analysis; public interface IFindingsPublisher { + string FindingsType { get; } /// /// Handles analysis results /// diff --git a/src/Core/Analysis/IssuePublisher.cs b/src/Core/Analysis/IssuePublisher.cs index df09fbe228..cf985584be 100644 --- a/src/Core/Analysis/IssuePublisher.cs +++ b/src/Core/Analysis/IssuePublisher.cs @@ -27,6 +27,8 @@ namespace SonarLint.VisualStudio.Core.Analysis; [method:ImportingConstructor] internal class IssuePublisher(IIssueConsumerStorage issueConsumerStorage) : IIssuePublisher { + public string FindingsType => CoreStrings.FindingType_Issue; + public void Publish(string filePath, Guid analysisId, IEnumerable findings) { if (issueConsumerStorage.TryGet(filePath, out var currentAnalysisId, out var issueConsumer) diff --git a/src/Core/CoreStrings.Designer.cs b/src/Core/CoreStrings.Designer.cs index e6267e555f..f22c464441 100644 --- a/src/Core/CoreStrings.Designer.cs +++ b/src/Core/CoreStrings.Designer.cs @@ -1,7 +1,6 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -87,6 +86,24 @@ public static string CSharpLanguageName { } } + /// + /// Looks up a localized string similar to hotspot. + /// + public static string FindingType_Hotspot { + get { + return ResourceManager.GetString("FindingType_Hotspot", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to issue. + /// + public static string FindingType_Issue { + get { + return ResourceManager.GetString("FindingType_Issue", resourceCulture); + } + } + /// /// Looks up a localized string similar to Solution/folder is not in a git repository. /// diff --git a/src/Core/CoreStrings.resx b/src/Core/CoreStrings.resx index 359e51278f..49f18b8669 100644 --- a/src/Core/CoreStrings.resx +++ b/src/Core/CoreStrings.resx @@ -193,4 +193,10 @@ https://sonarcloud.io + + issue + + + hotspot + \ No newline at end of file diff --git a/src/Integration.Vsix.UnitTests/Analysis/AnalysisStatusNotifierTests.cs b/src/Integration.Vsix.UnitTests/Analysis/AnalysisStatusNotifierTests.cs index 75ef113b6c..4f43cd601f 100644 --- a/src/Integration.Vsix.UnitTests/Analysis/AnalysisStatusNotifierTests.cs +++ b/src/Integration.Vsix.UnitTests/Analysis/AnalysisStatusNotifierTests.cs @@ -18,15 +18,9 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -using System; -using System.Collections.Generic; -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; using SonarLint.VisualStudio.Core; using SonarLint.VisualStudio.Integration.Vsix.Analysis; using SonarLint.VisualStudio.Integration.Vsix.Helpers; -using SonarLint.VisualStudio.TestInfrastructure; namespace SonarLint.VisualStudio.Integration.UnitTests.Analysis { @@ -65,6 +59,24 @@ public void AnalysisStarted_LogToOutputWindow() logger.OutputStrings.Count.Should().Be(1); } + [DataRow(true)] + [DataRow(false)] + [DataTestMethod] + public void AnalysisProgressed_LogToOutputWindow(bool isIntermediate) + { + const string analyzerName = "some analyzer"; + const string filePath = "c:\\test\\foo-started.cpp"; + var logger = new TestLogger(); + var analysisId = Guid.NewGuid(); + + var testSubject = CreateTestSubject(filePath, analysisId, analyzerName, logger: logger); + testSubject.AnalysisProgressed(123, "finding", isIntermediate); + + var expectedMessage = string.Format(AnalysisStrings.MSG_FoundIssues, 123, "finding", filePath, analysisId, !isIntermediate); + logger.AssertPartialOutputStringExists(expectedMessage); + logger.AssertPartialOutputStringExists(analyzerName); + } + [TestMethod] [DataRow("foo-finished.cpp", "foo-finished.cpp")] [DataRow("c:\\test\\foo-finished.cpp", "foo-finished.cpp")] @@ -72,9 +84,9 @@ public void AnalysisStarted_LogToOutputWindow() public void AnalysisFinished_DisplayMessageAndStopSpinner(string filePath, string expectedNotifiedFileName) { var statusBarMock = new Mock(); - var testSubject = CreateTestSubject(filePath, Guid.NewGuid(), statusBarNotifier: statusBarMock.Object); - testSubject.AnalysisFinished(1, TimeSpan.Zero); + + testSubject.AnalysisFinished(TimeSpan.FromSeconds(3)); var expectedMessage = string.Format(AnalysisStrings.Notifier_AnalysisFinished, expectedNotifiedFileName); @@ -91,17 +103,14 @@ public void AnalysisFinished_LogToOutputWindow() var testSubject = CreateTestSubject(filePath, analysisId, analyzerName, logger: logger); - testSubject.AnalysisFinished(123, TimeSpan.FromSeconds(6.54321)); + testSubject.AnalysisFinished(TimeSpan.FromSeconds(6.54321)); var expectedMessage = string.Format(AnalysisStrings.MSG_AnalysisComplete, filePath, analysisId, 6.543); logger.AssertPartialOutputStringExists(expectedMessage); - expectedMessage = string.Format($"Found {123} issue(s) for {filePath}"); - logger.AssertPartialOutputStringExists(expectedMessage); - logger.AssertPartialOutputStringExists(analyzerName); - logger.OutputStrings.Count.Should().Be(2); + logger.OutputStrings.Count.Should().Be(1); } [TestMethod] @@ -136,7 +145,7 @@ public void AnalysisCancelled_LogToOutputWindow() logger.AssertPartialOutputStringExists(expectedMessage); logger.OutputStrings.Count.Should().Be(1); } - + [TestMethod] [DataRow("foo-timedout.cpp")] [DataRow("c:\\test\\foo-timedout.cpp")] @@ -151,7 +160,7 @@ public void AnalysisNotReady_RemoveMessageAndStopSpinner(string filePath) VerifyStatusBarMessageAndIcon(statusBarMock, "", false); } - + [TestMethod] public void AnalysisNotReady_LogToOutputWindow() { @@ -170,7 +179,7 @@ public void AnalysisNotReady_LogToOutputWindow() logger.AssertPartialOutputStringExists(expectedMessage); logger.OutputStrings.Count.Should().Be(1); } - + [TestMethod] [DataRow("foo-failed.cpp", "foo-failed.cpp")] @@ -188,8 +197,8 @@ public void AnalysisFailed_DisplayMessageAndStopSpinner(string filePath, string VerifyStatusBarMessageAndIcon(statusBarMock, expectedMessage, false); } - - + + [TestMethod] [DataRow("foo-failed.cpp", "foo-failed.cpp")] diff --git a/src/Integration.Vsix/Analysis/AnalysisStatusNotifier.cs b/src/Integration.Vsix/Analysis/AnalysisStatusNotifier.cs index 0b0cdd3c0b..685bcda64f 100644 --- a/src/Integration.Vsix/Analysis/AnalysisStatusNotifier.cs +++ b/src/Integration.Vsix/Analysis/AnalysisStatusNotifier.cs @@ -50,10 +50,15 @@ public void AnalysisStarted() Notify(AnalysisStrings.Notifier_AnalysisStarted, true); } - public void AnalysisFinished(int issueCount, TimeSpan analysisTime) + public void AnalysisProgressed( + int issueCount, + string findingType, + bool isIntermediate) => + Log(AnalysisStrings.MSG_FoundIssues, issueCount, findingType, filePath, analysisId, !isIntermediate); + + public void AnalysisFinished(TimeSpan analysisTime) { Log(AnalysisStrings.MSG_AnalysisComplete, filePath, analysisId, Math.Round(analysisTime.TotalSeconds, 3)); - Log(AnalysisStrings.MSG_FoundIssues, issueCount, filePath); Notify(AnalysisStrings.Notifier_AnalysisFinished, false); } @@ -61,7 +66,7 @@ public void AnalysisFinished(int issueCount, TimeSpan analysisTime) public void AnalysisCancelled() { Log(AnalysisStrings.MSG_AnalysisAborted, filePath, analysisId); - + Notify("", false); } @@ -80,7 +85,7 @@ public void AnalysisFailed(string failureMessage) public void AnalysisNotReady(string reason) { Log(AnalysisStrings.MSG_AnalysisNotReady, filePath, analysisId, reason); - + Notify("", false); } diff --git a/src/Integration.Vsix/Analysis/AnalysisStrings.Designer.cs b/src/Integration.Vsix/Analysis/AnalysisStrings.Designer.cs index 0b70f38c19..69cbe983ab 100644 --- a/src/Integration.Vsix/Analysis/AnalysisStrings.Designer.cs +++ b/src/Integration.Vsix/Analysis/AnalysisStrings.Designer.cs @@ -1,7 +1,6 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -180,7 +179,7 @@ internal static string MSG_AnalysisStarted { } /// - /// Looks up a localized string similar to Found {0} issue(s) for {1}. + /// Looks up a localized string similar to Found {0} {1}(s) in {2} [id: {3}, final: {4}]. /// internal static string MSG_FoundIssues { get { diff --git a/src/Integration.Vsix/Analysis/AnalysisStrings.resx b/src/Integration.Vsix/Analysis/AnalysisStrings.resx index db4a804e97..58ad21366a 100644 --- a/src/Integration.Vsix/Analysis/AnalysisStrings.resx +++ b/src/Integration.Vsix/Analysis/AnalysisStrings.resx @@ -170,7 +170,7 @@ Analyzing {0} with id {1} - Found {0} issue(s) for {1} + Found {0} {1}(s) in {2} [id: {3}, final: {4}] Binding has changed. Open documents will be re-analysed. diff --git a/src/SLCore.Listeners.UnitTests/Implementation/Analysis/RaisedFindingProcessorTests.cs b/src/SLCore.Listeners.UnitTests/Implementation/Analysis/RaisedFindingProcessorTests.cs index 8c3f6ebb3f..35f55590ee 100644 --- a/src/SLCore.Listeners.UnitTests/Implementation/Analysis/RaisedFindingProcessorTests.cs +++ b/src/SLCore.Listeners.UnitTests/Implementation/Analysis/RaisedFindingProcessorTests.cs @@ -28,8 +28,6 @@ using SonarLint.VisualStudio.SLCore.Listeners.Implementation.Analysis; using SonarLint.VisualStudio.SLCore.Protocol; using SonarLint.VisualStudio.SLCore.Service.Rules.Models; -using CleanCodeAttribute = SonarLint.VisualStudio.SLCore.Common.Models.CleanCodeAttribute; -using IssueSeverity = SonarLint.VisualStudio.SLCore.Common.Models.IssueSeverity; using SloopLanguage = SonarLint.VisualStudio.SLCore.Common.Models.Language; namespace SonarLint.VisualStudio.SLCore.Listeners.UnitTests.Implementation.Analysis; @@ -37,6 +35,8 @@ namespace SonarLint.VisualStudio.SLCore.Listeners.UnitTests.Implementation.Analy [TestClass] public class RaisedFindingProcessorTests { + private const string FindingsType = "FINDING"; + [TestMethod] public void MefCtor_CheckIsExported() => MefTestHelpers.CheckTypeCanBeImported( @@ -91,8 +91,9 @@ public void RaiseFindings_NoSupportedLanguages_PublishesEmpty() var findingsByFileUri = new Dictionary> { { fileUri, [CreateTestFinding("csharpsquid:S100"), CreateTestFinding("csharpsquid:S101")] } }; - var raiseFindingParams = new RaiseFindingParams("CONFIGURATION_ID", findingsByFileUri, false, analysisId); - var publisher = Substitute.For(); + var isIntermediatePublication = false; + var raiseFindingParams = new RaiseFindingParams("CONFIGURATION_ID", findingsByFileUri, isIntermediatePublication, analysisId); + var publisher = CreatePublisher(); IRaiseFindingToAnalysisIssueConverter raiseFindingToAnalysisIssueConverter = CreateConverter(findingsByFileUri.Single().Key, [], []); var analysisStatusNotifierFactory = CreateAnalysisStatusNotifierFactory(out var analysisStatusNotifier, fileUri.LocalPath, analysisId); @@ -105,7 +106,8 @@ public void RaiseFindings_NoSupportedLanguages_PublishesEmpty() raiseFindingToAnalysisIssueConverter.Received().GetAnalysisIssues(fileUri, Arg.Is>(x => !x.Any())); publisher.Received().Publish(fileUri.LocalPath, analysisId, Arg.Is>(x => !x.Any())); - analysisStatusNotifier.AnalysisFinished(0, TimeSpan.Zero); + analysisStatusNotifier.DidNotReceiveWithAnyArgs().AnalysisFinished(default); + analysisStatusNotifier.Received().AnalysisProgressed(0, FindingsType, isIntermediatePublication); } [TestMethod] @@ -116,8 +118,9 @@ public void RaiseFindings_NoKnownLanguages_PublishesEmpty() var findingsByFileUri = new Dictionary> { { fileUri, [CreateTestFinding("csharpsquid:S100"), CreateTestFinding("csharpsquid:S101")] } }; - var raiseFindingParams = new RaiseFindingParams("CONFIGURATION_ID", findingsByFileUri, false, analysisId); - var publisher = Substitute.For(); + var isIntermediatePublication = false; + var raiseFindingParams = new RaiseFindingParams("CONFIGURATION_ID", findingsByFileUri, isIntermediatePublication, analysisId); + var publisher = CreatePublisher(); IRaiseFindingToAnalysisIssueConverter raiseFindingToAnalysisIssueConverter = CreateConverter(findingsByFileUri.Single().Key, [], []); var analysisStatusNotifierFactory = CreateAnalysisStatusNotifierFactory(out var analysisStatusNotifier, fileUri.LocalPath, analysisId); @@ -131,7 +134,8 @@ public void RaiseFindings_NoKnownLanguages_PublishesEmpty() raiseFindingToAnalysisIssueConverter.Received().GetAnalysisIssues(fileUri, Arg.Is>(x => !x.Any())); publisher.Received().Publish(fileUri.LocalPath, analysisId, Arg.Is>(x => !x.Any())); - analysisStatusNotifier.AnalysisFinished(0, TimeSpan.Zero); + analysisStatusNotifier.DidNotReceiveWithAnyArgs().AnalysisFinished(default); + analysisStatusNotifier.Received().AnalysisProgressed(0, FindingsType, isIntermediatePublication); } [TestMethod] @@ -139,7 +143,7 @@ public void RaiseFindings_HasNoFileUri_FinishesAnalysis() { var analysisId = Guid.NewGuid(); var analysisStatusNotifierFactory = CreateAnalysisStatusNotifierFactory(out var analysisStatusNotifier, null, analysisId); - var publisher = Substitute.For(); + var publisher = CreatePublisher(); var testSubject = CreateTestSubject(analysisStatusNotifierFactory: analysisStatusNotifierFactory); var act = () => @@ -168,8 +172,9 @@ public void RaiseFindings_HasIssuesNotIntermediate_PublishFindings() var findingsByFileUri = new Dictionary> { { fileUri, raisedFindings } }; - var raiseFindingParams = new RaiseFindingParams("CONFIGURATION_ID", findingsByFileUri, false, analysisId); - var publisher = Substitute.For(); + var isIntermediatePublication = false; + var raiseFindingParams = new RaiseFindingParams("CONFIGURATION_ID", findingsByFileUri, isIntermediatePublication, analysisId); + var publisher = CreatePublisher(); IRaiseFindingToAnalysisIssueConverter raiseFindingToAnalysisIssueConverter = CreateConverter(findingsByFileUri.Single().Key, filteredRaisedFindings, filteredIssues); @@ -187,7 +192,8 @@ public void RaiseFindings_HasIssuesNotIntermediate_PublishFindings() x => x.SequenceEqual(filteredRaisedFindings))); analysisStatusNotifierFactory.Received(1).Create("SLCoreAnalyzer", fileUri.LocalPath, analysisId); - analysisStatusNotifier.Received(1).AnalysisFinished(2, TimeSpan.Zero); + analysisStatusNotifier.DidNotReceiveWithAnyArgs().AnalysisFinished(default); + analysisStatusNotifier.Received().AnalysisProgressed(2, FindingsType, isIntermediatePublication); } [DataRow(true)] @@ -208,12 +214,13 @@ public void RaiseFindings_MultipleFiles_PublishFindingsForEachFile(bool isInterm var raiseFindingParams = new RaiseFindingParams("CONFIGURATION_ID", findingsByFileUri, isIntermediate, analysisId); - var publisher = Substitute.For(); + var publisher = CreatePublisher(); var raiseFindingParamsToAnalysisIssueConverter = Substitute.For(); raiseFindingParamsToAnalysisIssueConverter.GetAnalysisIssues(fileUri1, Arg.Any>()).Returns([analysisIssue1]); raiseFindingParamsToAnalysisIssueConverter.GetAnalysisIssues(fileUri2, Arg.Any>()).Returns([analysisIssue2]); - var analysisStatusNotifierFactory = Substitute.For(); + var analysisStatusNotifierFactory = CreateAnalysisStatusNotifierFactory(out var notifier1, fileUri1.LocalPath, analysisId); + SetUpNotifierForFile(out var notifier2, fileUri2.LocalPath, analysisId, analysisStatusNotifierFactory); var testSubject = CreateTestSubject( raiseFindingToAnalysisIssueConverter: raiseFindingParamsToAnalysisIssueConverter, @@ -228,6 +235,10 @@ public void RaiseFindings_MultipleFiles_PublishFindingsForEachFile(bool isInterm analysisStatusNotifierFactory.Received(1).Create("SLCoreAnalyzer", fileUri1.LocalPath, analysisId); analysisStatusNotifierFactory.Received(1).Create("SLCoreAnalyzer", fileUri2.LocalPath, analysisId); + notifier1.DidNotReceiveWithAnyArgs().AnalysisFinished(default); + notifier1.Received().AnalysisProgressed(1, FindingsType, isIntermediate); + notifier2.DidNotReceiveWithAnyArgs().AnalysisFinished(default); + notifier2.Received().AnalysisProgressed(1, FindingsType, isIntermediate); } private RaisedFindingProcessor CreateTestSubject( @@ -261,9 +272,18 @@ private IAnalysisStatusNotifierFactory CreateAnalysisStatusNotifierFactory(out I Guid? analysisId) { var analysisStatusNotifierFactory = Substitute.For(); + SetUpNotifierForFile(out analysisStatusNotifier, filePath, analysisId, analysisStatusNotifierFactory); + return analysisStatusNotifierFactory; + } + + private static void SetUpNotifierForFile( + out IAnalysisStatusNotifier analysisStatusNotifier, + string filePath, + Guid? analysisId, + IAnalysisStatusNotifierFactory analysisStatusNotifierFactory) + { analysisStatusNotifier = Substitute.For(); analysisStatusNotifierFactory.Create(nameof(SLCoreAnalyzer), filePath, analysisId).Returns(analysisStatusNotifier); - return analysisStatusNotifierFactory; } private TestFinding CreateTestFinding(string ruleKey) @@ -271,6 +291,13 @@ private TestFinding CreateTestFinding(string ruleKey) return new TestFinding(default, default, ruleKey, default, default, default, default, default, default, default, default, default); } + private static IFindingsPublisher CreatePublisher() + { + var publisher = Substitute.For(); + publisher.FindingsType.Returns(FindingsType); + return publisher; + } + private static IAnalysisIssue CreateAnalysisIssue(string ruleKey) { var analysisIssue1 = Substitute.For(); diff --git a/src/SLCore.Listeners/Implementation/Analysis/RaisedFindingProcessor.cs b/src/SLCore.Listeners/Implementation/Analysis/RaisedFindingProcessor.cs index 53e93b6c2e..c0430d285c 100644 --- a/src/SLCore.Listeners/Implementation/Analysis/RaisedFindingProcessor.cs +++ b/src/SLCore.Listeners/Implementation/Analysis/RaisedFindingProcessor.cs @@ -85,7 +85,7 @@ private void PublishFindings(RaiseFindingParams parameters, IFindingsPubli findingsPublisher.Publish(localPath, parameters.analysisId!.Value, raiseFindingToAnalysisIssueConverter.GetAnalysisIssues(fileUri, supportedRaisedIssues)); - analysisStatusNotifier.AnalysisFinished(supportedRaisedIssues.Length, TimeSpan.Zero); + analysisStatusNotifier.AnalysisProgressed(supportedRaisedIssues.Length, findingsPublisher.FindingsType, parameters.isIntermediatePublication); } } diff --git a/src/SLCore.UnitTests/Analysis/SLCoreAnalyzerTests.cs b/src/SLCore.UnitTests/Analysis/SLCoreAnalyzerTests.cs index b49d05faf7..a5c2631859 100644 --- a/src/SLCore.UnitTests/Analysis/SLCoreAnalyzerTests.cs +++ b/src/SLCore.UnitTests/Analysis/SLCoreAnalyzerTests.cs @@ -226,7 +226,7 @@ public void ExecuteAnalysis_PassesCorrectCancellationTokenToAnalysisService() } [TestMethod] - public void ExecuteAnalysis_AnalysisServiceSucceeds_ExitsWithoutFinishingAnalysis() + public void ExecuteAnalysis_AnalysisServiceSucceeds_ExitsByFinishingAnalysis() { SetUpInitializedConfigScope(); analysisService.AnalyzeFilesAndTrackAsync(default, default).ReturnsForAnyArgs(new AnalyzeFilesResponse(new HashSet(), [])); @@ -236,7 +236,7 @@ public void ExecuteAnalysis_AnalysisServiceSucceeds_ExitsWithoutFinishingAnalysi notifier.DidNotReceiveWithAnyArgs().AnalysisNotReady(default); notifier.DidNotReceiveWithAnyArgs().AnalysisFailed(default(Exception)); notifier.DidNotReceiveWithAnyArgs().AnalysisFailed(default(string)); - notifier.DidNotReceiveWithAnyArgs().AnalysisFinished(default, default); + notifier.Received().AnalysisFinished(Arg.Is(x => x > TimeSpan.Zero)); } [TestMethod] diff --git a/src/SLCore/Analysis/SLCoreAnalyzer.cs b/src/SLCore/Analysis/SLCoreAnalyzer.cs index d3598dba12..7077ae6013 100644 --- a/src/SLCore/Analysis/SLCoreAnalyzer.cs +++ b/src/SLCore/Analysis/SLCoreAnalyzer.cs @@ -102,6 +102,8 @@ private async Task ExecuteAnalysisInternalAsync( Dictionary properties = []; using var temporaryResourcesHandle = EnrichPropertiesForCFamily(properties, path, detectedLanguages); + Stopwatch stopwatch = Stopwatch.StartNew(); + var (failedAnalysisFiles, _) = await analysisService.AnalyzeFilesAndTrackAsync( new AnalyzeFilesAndTrackParams( configScopeId, @@ -116,6 +118,10 @@ [new FileUri(path)], { analysisStatusNotifier.AnalysisFailed(SLCoreStrings.AnalysisFailedReason); } + else + { + analysisStatusNotifier.AnalysisFinished(stopwatch.Elapsed); + } } catch (OperationCanceledException) {