From eecbb2646481d051629bd2990fdf9d745266fc5a Mon Sep 17 00:00:00 2001 From: Pavel Mikula <57188685+pavel-mikula-sonarsource@users.noreply.github.com> Date: Wed, 5 Feb 2025 09:51:38 +0100 Subject: [PATCH] SCAN4NET-231 Cleanup RoslynAnalyzerProviderTest (#2313) --- .../RoslynAnalyzerProviderTests.cs | 376 ++++++++---------- .../Roslyn/RoslynAnalyzerProvider.cs | 2 +- .../Roslyn/RoslynSonarLint.cs | 6 +- 3 files changed, 161 insertions(+), 223 deletions(-) diff --git a/Tests/SonarScanner.MSBuild.PreProcessor.Test/RoslynAnalyzerProviderTests.cs b/Tests/SonarScanner.MSBuild.PreProcessor.Test/RoslynAnalyzerProviderTests.cs index 6def106b6..21a0e9363 100644 --- a/Tests/SonarScanner.MSBuild.PreProcessor.Test/RoslynAnalyzerProviderTests.cs +++ b/Tests/SonarScanner.MSBuild.PreProcessor.Test/RoslynAnalyzerProviderTests.cs @@ -73,7 +73,7 @@ public void RoslynConfig_NoAssemblies() context.AssertInfoMessages("No Roslyn analyzer plugins were specified so no Roslyn analyzers will be run for cs"); context.AssertCorrectRulesets(); context.AssertExpectedAssemblies([]); - context.AssertExpectedPluginsRequested([]); + context.AssertEmptyPluginsRequested(); } [DataTestMethod] @@ -112,89 +112,21 @@ public void RoslynConfig_ValidProfile_WithLegacy(string language) {"sonar.cs.foo", "bar"}, {"sonar.vbnet.foo", "bar"}, {"sonar.cs.analyzer.security.pluginKey", "securitycsharpfrontend" }, - {"sonar.cs.analyzer.security.pluginVersion", "1.42.0" }, + {"sonar.cs.analyzer.security.pluginVersion", "2.34.0" }, {"sonar.cs.analyzer.security.staticResourceName", "SecurityAnalyzer.zip" }, {"sonaranalyzer.security.cs.pluginKey", "OLDSecurityCSharpFrontend" }, {"sonaranalyzer.security.cs.pluginVersion", "OLDSecurityCSharpFrontend" }, {"sonaranalyzer.security.cs.staticResourceName", "OLDSecurityCSharpFrontend" }, }); var context = new Context(TestContext, sonarProperties, [[@"c:\assembly1.dll"], [@"d:\foo\assembly2.dll"]], null, language); - var securityProperties = language == RoslynAnalyzerProvider.CSharpLanguage ? - """ - - - sonar.cs.analyzer.security.pluginKey - securitycsharpfrontend - - - sonar.cs.analyzer.security.pluginVersion - 1.42.0 - - - sonar.cs.analyzer.security.staticResourceName - SecurityAnalyzer.zip - - """ : string.Empty; - var expectedSonarLintXml = $""" - - - - - sonar.{language}.analyzer.dotnet.pluginKey - {language} - - - sonar.{language}.analyzer.dotnet.pluginVersion - 1.42.0 - - - sonar.{language}.analyzer.dotnet.staticResourceName - SonarAnalyzer.zip - - - sonar.{language}.testPropertyPattern - foo - - - sonar.{language}.foo - bar - {securityProperties} - - - - {language}-S1116 - - - key - value - - - - - {language}-S1125 - - - - - - - """; + context.AssertCorrectAnalyzerSettings(); context.AssertNoWarningsOrErrors(); context.AssertInfoMessages($"Provisioning analyzer assemblies for {language}..."); context.AssertCorrectRulesets(); context.AssertExpectedAssemblies(@"c:\assembly1.dll", @"d:\foo\assembly2.dll"); - var expectedPlugins = new List() - { - new() { Key = "wintellect", Version = "1.13.0", StaticResourceName = "wintellect.zip" }, - new() { Key = language, Version = "1.42.0", StaticResourceName = "SonarAnalyzer.zip" }, - }; - if (language == RoslynAnalyzerProvider.CSharpLanguage) - { - expectedPlugins.Add(new() { Key = "securitycsharpfrontend", Version = "1.42.0", StaticResourceName = "SecurityAnalyzer.zip" }); - } - context.AssertExpectedPluginsRequested(expectedPlugins); - context.AssertExpectedAdditionalFileExists(expectedSonarLintXml); + context.AssertExpectedPluginsRequested(); + context.AssertExpectedAdditionalFileExists(ExpectedSonarLintXml(language)); } [DataTestMethod] @@ -227,49 +159,19 @@ public void RoslynConfig_ValidProfile_LegacyOnly(string language) {"sonar.cs.testPropertyPattern", "foo"}, {"sonar.sources", "**/*.*"}, {"sonar.cs.foo", "bar"}, - {"sonar.vbnet.foo", "bar"} + {"sonar.vbnet.foo", "bar"}, + {"sonaranalyzer.security.cs.pluginKey", "securitycsharpfrontend" }, + {"sonaranalyzer.security.cs.pluginVersion", "2.34.0" }, + {"sonaranalyzer.security.cs.staticResourceName", "SecurityAnalyzer.zip" }, }); var context = new Context(TestContext, sonarProperties, [[@"c:\assembly1.dll"], [@"d:\foo\assembly2.dll"]], null, language); - var expectedSonarLintXml = $""" - - - - - sonar.{language}.testPropertyPattern - foo - - - sonar.{language}.foo - bar - - - - - {language}-S1116 - - - key - value - - - - - {language}-S1125 - - - - - - - """; + context.AssertCorrectAnalyzerSettings(); context.AssertNoWarningsOrErrors(); context.AssertCorrectRulesets(); context.AssertExpectedAssemblies(@"c:\assembly1.dll", @"d:\foo\assembly2.dll"); - context.AssertExpectedPluginsRequested([ - new() { Key = "wintellect", Version = "1.13.0", StaticResourceName = "wintellect.zip" }, - new() { Key = language, Version = "1.42.0", StaticResourceName = "SonarAnalyzer.zip" }]); - context.AssertExpectedAdditionalFileExists(expectedSonarLintXml); + context.AssertExpectedPluginsRequested(); + context.AssertExpectedAdditionalFileExists(ExpectedSonarLintXml(language)); } [DataTestMethod] @@ -294,86 +196,18 @@ public void RoslynConfig_ValidProfile_WithoutLegacy(string language) {"sonar.cs.foo", "bar"}, {"sonar.vbnet.foo", "bar"}, {"sonar.cs.analyzer.security.pluginKey", "securitycsharpfrontend"}, - {"sonar.cs.analyzer.security.pluginVersion", "1.13.0"}, + {"sonar.cs.analyzer.security.pluginVersion", "2.34.0"}, {"sonar.cs.analyzer.security.staticResourceName", "SecurityAnalyzer.zip"}, }); var context = new Context(TestContext, sonarProperties, [[@"c:\assembly1.dll"], [@"d:\foo\assembly2.dll"]], null, language); - var securityProperties = language == RoslynAnalyzerProvider.CSharpLanguage ? - """ - - - sonar.cs.analyzer.security.pluginKey - securitycsharpfrontend - - - sonar.cs.analyzer.security.pluginVersion - 1.13.0 - - - sonar.cs.analyzer.security.staticResourceName - SecurityAnalyzer.zip - - """ : string.Empty; - var expectedSonarLintXml = $""" - - - - - sonar.{language}.analyzer.dotnet.pluginKey - {language} - - - sonar.{language}.analyzer.dotnet.pluginVersion - 1.42.0 - - - sonar.{language}.analyzer.dotnet.staticResourceName - SonarAnalyzer.zip - - - sonar.{language}.testPropertyPattern - foo - - - sonar.{language}.foo - bar - {securityProperties} - - - - {language}-S1116 - - - key - value - - - - - {language}-S1125 - - - - - - - """; + context.AssertCorrectAnalyzerSettings(); context.AssertNoWarningsOrErrors(); context.AssertInfoMessages($"Provisioning analyzer assemblies for {language}..."); context.AssertCorrectRulesets(); context.AssertExpectedAssemblies(@"c:\assembly1.dll", @"d:\foo\assembly2.dll"); - var expectedPlugins = new List() - { - new() { Key = "wintellect", Version = "1.13.0", StaticResourceName = "wintellect.zip" }, - new() { Key = language, Version = "1.42.0", StaticResourceName = "SonarAnalyzer.zip" }, - }; - if (language == RoslynAnalyzerProvider.CSharpLanguage) - { - expectedPlugins.Add(new() { Key = "securitycsharpfrontend", Version = "1.13.0", StaticResourceName = "SecurityAnalyzer.zip" }); - } - context.AssertExpectedPluginsRequested(expectedPlugins); - context.AssertExpectedAdditionalFileExists(expectedSonarLintXml); + context.AssertExpectedPluginsRequested(); + context.AssertExpectedAdditionalFileExists(ExpectedSonarLintXml(language)); } [DataTestMethod] @@ -394,13 +228,83 @@ public void RoslynConfig_IncompletePluginIgnored(string analyzerIdProperty, stri context.AssertInfoMessages("No Roslyn analyzer plugins were specified so no Roslyn analyzers will be run for cs"); context.AssertCorrectRulesets(); context.AssertExpectedAssemblies([]); - context.AssertExpectedPluginsRequested([]); + context.AssertEmptyPluginsRequested(); } private static BuildSettings CreateSettings(string rootDir) => BuildSettings.CreateNonTeamBuildSettingsForTesting(rootDir); - private class Context + private static string ExpectedSonarLintXml(string language) => + language switch + { + RoslynAnalyzerProvider.CSharpLanguage => $""" + + + + + sonar.cs.testPropertyPattern + foo + + + sonar.cs.foo + bar + + + + + cs-S1116 + + + key + value + + + + + cs-S1125 + + + + + + + """, + RoslynAnalyzerProvider.VBNetLanguage => """ + + + + + sonar.vbnet.testPropertyPattern + foo + + + sonar.vbnet.foo + bar + + + + + vbnet-S1116 + + + key + value + + + + + vbnet-S1125 + + + + + + + """, + _ => throw new NotSupportedException($"Unexpected language: {language}") + }; + + private sealed class Context { private readonly TestContext testContext; private readonly TestLogger logger = new(); @@ -420,10 +324,10 @@ public Context(TestContext testContext, ListPropertiesProvider properties, List< ActualSettings = sut.SetupAnalyzer(); } - public void AssertCorrectRulesets(string expectedRules = null) + public void AssertCorrectRulesets() { - CheckRuleset(ActualSettings.RulesetPath, false, expectedRules); - CheckRuleset(ActualSettings.DeactivatedRulesetPath, true, expectedRules); + CheckRuleset(ActualSettings.RulesetPath, false); + CheckRuleset(ActualSettings.DeactivatedRulesetPath, true); } public void AssertNoWarningsOrErrors() @@ -467,8 +371,22 @@ public void AssertExpectedAssemblies(params string[] expected) ActualSettings.AnalyzerPlugins.Should().HaveCount(expected.Length, "Too many assembly file paths returned"); } - public void AssertExpectedPluginsRequested(IEnumerable plugins) => - analyzerInstaller.AssertOnlyExpectedPluginsRequested(plugins); + public void AssertEmptyPluginsRequested() => + analyzerInstaller.AssertOnlyExpectedPluginsRequested([]); + + public void AssertExpectedPluginsRequested() + { + var expectedPlugins = new List() + { + new() { Key = "wintellect", Version = "1.13.0", StaticResourceName = "wintellect.zip" }, + new() { Key = language, Version = "1.42.0", StaticResourceName = "SonarAnalyzer.zip" }, + }; + if (language == RoslynAnalyzerProvider.CSharpLanguage) + { + expectedPlugins.Add(new() { Key = "securitycsharpfrontend", Version = "2.34.0", StaticResourceName = "SecurityAnalyzer.zip" }); + } + analyzerInstaller.AssertOnlyExpectedPluginsRequested(expectedPlugins); + } public void AssertExpectedAdditionalFileExists(string expectedContent, string expectedFileName = "SonarLint.xml") { @@ -490,37 +408,61 @@ public void AssertExpectedAdditionalFileExists(string expectedContent, string ex } } - private static List CreateRules() => - [ - new("csharpsquid", "cs-S1116", true) { Parameters = new() { { "key", "value" } } }, - new("csharpsquid", "cs-S1125", true), - new("roslyn.wintellect", "Wintellect003", true), - new("csharpsquid", "cs-S1000", false), - new("vbnet", "vbnet-S1116", true) { Parameters = new() { { "key", "value" } } }, - new("vbnet", "vbnet-S1125", true), - new("vbnet", "vbnet-S1000", false), - new("roslyn.sonaranalyzer.security.cs", "SECURITY2076", true) - ]; - - private void CheckRuleset(string ruleSetPath, bool isDeactivated, string expectedRules = null) + private List CreateRules() => + language switch + { + RoslynAnalyzerProvider.CSharpLanguage => + [ + new("csharpsquid", "cs-S1116", true) { Parameters = new() { { "key", "value" } } }, + new("csharpsquid", "cs-S1125", true), + new("roslyn.wintellect", "Wintellect003", true), + new("csharpsquid", "cs-S1000", false), + new("roslyn.sonaranalyzer.security.cs", "SECURITY2076", true) + ], + RoslynAnalyzerProvider.VBNetLanguage => // VB.NET language will never receive roslyn.sonaranalyzer.security.cs rules, because those are only in the C# QP + [ + new("roslyn.wintellect", "Wintellect003", true), + new("vbnet", "vbnet-S1116", true) { Parameters = new() { { "key", "value" } } }, + new("vbnet", "vbnet-S1125", true), + new("vbnet", "vbnet-S1000", false), + ], + _ => throw new NotSupportedException($"Unexpected language: {language}") + }; + + private string ExpectedRuleSetFormat() => + language switch + { + RoslynAnalyzerProvider.CSharpLanguage => """ + + + + + + + + + + + """, + RoslynAnalyzerProvider.VBNetLanguage => """ + + + + + + + + + + """, + _ => throw new NotSupportedException($"Unexpected language: {language}") + }; + + private void CheckRuleset(string ruleSetPath, bool isDeactivated) { var expectedFileName = isDeactivated ? $"Sonar-{language}-none.ruleset" : $"Sonar-{language}.ruleset"; var expectedRule = isDeactivated ? "None" : "Warning"; - var expectedContent = string.Format(expectedRules ?? """ - - - - - - - - - - - - - - """, expectedRule); + var expectedContent = string.Format(ExpectedRuleSetFormat(), expectedRule); ruleSetPath.Should().NotBeNullOrEmpty("Ruleset file path should be set"); Path.IsPathRooted(ruleSetPath).Should().BeTrue("Ruleset file path should be absolute"); Path.GetFileName(ruleSetPath).Should().Be(expectedFileName, "Ruleset file does not have the expected name"); diff --git a/src/SonarScanner.MSBuild.PreProcessor/Roslyn/RoslynAnalyzerProvider.cs b/src/SonarScanner.MSBuild.PreProcessor/Roslyn/RoslynAnalyzerProvider.cs index 44e3b294a..b8661ddc3 100644 --- a/src/SonarScanner.MSBuild.PreProcessor/Roslyn/RoslynAnalyzerProvider.cs +++ b/src/SonarScanner.MSBuild.PreProcessor/Roslyn/RoslynAnalyzerProvider.cs @@ -123,7 +123,7 @@ private Plugin[] CreatePlugins() { candidates.Remove(LegacyServerPropertyPrefix + language); } - if (candidates.ContainsKey(string.Format(ServerPropertyFormat, language) + ".security") || language == VBNetLanguage) + if (candidates.ContainsKey(string.Format(ServerPropertyFormat, language) + ".security")) { candidates.Remove("sonaranalyzer.security.cs"); } diff --git a/src/SonarScanner.MSBuild.PreProcessor/Roslyn/RoslynSonarLint.cs b/src/SonarScanner.MSBuild.PreProcessor/Roslyn/RoslynSonarLint.cs index d2b6e382f..cc5717bd7 100644 --- a/src/SonarScanner.MSBuild.PreProcessor/Roslyn/RoslynSonarLint.cs +++ b/src/SonarScanner.MSBuild.PreProcessor/Roslyn/RoslynSonarLint.cs @@ -18,10 +18,6 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -using System.Collections.Generic; -using System.Linq; -using System.Text; -using SonarScanner.MSBuild.Common; using SonarScanner.MSBuild.PreProcessor.Roslyn.Model; namespace SonarScanner.MSBuild.PreProcessor.Roslyn; @@ -43,7 +39,7 @@ public static string GenerateXml(IEnumerable activeRules, IAnalysisPr builder.AppendLine(" "); foreach (var pair in settings) { - if (!Utilities.IsSecuredServerProperty(pair.Id)) + if (!Utilities.IsSecuredServerProperty(pair.Id) && !pair.Id.StartsWith($"sonar.{language}.analyzer.")) // sonar.cs.analyzer are used to talk only to this scanner, not analyzers { WriteSetting(builder, pair.Id, pair.Value); }