diff --git a/src/CFamily.UnitTests/packages.lock.json b/src/CFamily.UnitTests/packages.lock.json index 2b617620d5..040d7c822a 100644 --- a/src/CFamily.UnitTests/packages.lock.json +++ b/src/CFamily.UnitTests/packages.lock.json @@ -1606,7 +1606,8 @@ "DiffPlex": "[1.7.1, )", "Microsoft.VisualStudio.Sdk": "[17.0.31902.203, )", "SonarLint.VisualStudio.Core": "[1.0.0, )", - "SonarLint.VisualStudio.Infrastructure.VS": "[1.0.0, )" + "SonarLint.VisualStudio.Infrastructure.VS": "[1.0.0, )", + "SonarLint.VisualStudio.SLCore": "[1.0.0, )" } }, "SonarLint.VisualStudio.Infrastructure.VS": { diff --git a/src/Education.UnitTests/Layout/Logical/ContextualRuleDescriptionTabTests.cs b/src/Education.UnitTests/Layout/Logical/ContextualRuleDescriptionTabTests.cs index 8ed06a49c6..b0278da4cf 100644 --- a/src/Education.UnitTests/Layout/Logical/ContextualRuleDescriptionTabTests.cs +++ b/src/Education.UnitTests/Layout/Logical/ContextualRuleDescriptionTabTests.cs @@ -45,6 +45,14 @@ public class ContextualRuleDescriptionTabTests private const string ContextTabContent2 = "htmlcontent2"; private const string ContextTabXamlContent2 = "xamlcontent2"; + [TestMethod] + public void Ctor_ContextContentTab_EnsuresHtmlIsXml() + { + var testSubject = new ContextualRuleDescriptionTab.ContextContentTab("title", "context", ""); + + testSubject.HtmlContent.Should().BeEquivalentTo(""); + } + [TestMethod] public void Title_ReturnsCorrectTitle() { diff --git a/src/Education.UnitTests/Layout/Logical/NonContextualRuleDescriptionTabTests.cs b/src/Education.UnitTests/Layout/Logical/NonContextualRuleDescriptionTabTests.cs index 1615718942..c7f3b8de1c 100644 --- a/src/Education.UnitTests/Layout/Logical/NonContextualRuleDescriptionTabTests.cs +++ b/src/Education.UnitTests/Layout/Logical/NonContextualRuleDescriptionTabTests.cs @@ -30,6 +30,14 @@ namespace SonarLint.VisualStudio.Education.UnitTests.Layout.Logical; [TestClass] public class NonContextualRuleDescriptionTabTests { + [TestMethod] + public void Ctor_EnsuresHtmlIsXml() + { + var testSubject = new NonContextualRuleDescriptionTab("title", ""); + + testSubject.htmlContent.Should().BeEquivalentTo(""); + } + [TestMethod] public void ProduceVisualNode_ReturnsSingleContentSection() { diff --git a/src/Education.UnitTests/Layout/Logical/RichRuleDescriptionProviderTest.cs b/src/Education.UnitTests/Layout/Logical/RichRuleDescriptionProviderTest.cs new file mode 100644 index 0000000000..846a84eb6d --- /dev/null +++ b/src/Education.UnitTests/Layout/Logical/RichRuleDescriptionProviderTest.cs @@ -0,0 +1,86 @@ +/* + * 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.Collections.Generic; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using SonarLint.VisualStudio.Education.Layout.Logical; +using SonarLint.VisualStudio.Education.Rule; +using SonarLint.VisualStudio.SLCore.Protocol; +using SonarLint.VisualStudio.SLCore.Service.Rules.Models; +using SonarLint.VisualStudio.TestInfrastructure; + +namespace SonarLint.VisualStudio.Education.UnitTests.Layout.Logical; + +[TestClass] +public class RichRuleDescriptionProviderTest +{ + [TestMethod] + public void MefCtor_CheckIsExported() + { + MefTestHelpers.CheckTypeCanBeImported(); + } + + [TestMethod] + public void MefCtor_CheckIsSingleton() + { + MefTestHelpers.CheckIsSingletonMefComponent(); + } + + [TestMethod] + public void Get_ReturnsCorrectStructure() + { + var richRuleDescriptionProvider = new RichRuleDescriptionProvider(); + var ruleInfoMock = new Mock(); + ruleInfoMock.SetupGet(x => x.RichRuleDescriptionDto).Returns( + new RuleSplitDescriptionDto("intro", + new List + { + new("tab 1", + Either.CreateLeft( + new RuleNonContextualSectionDto("content"))), + new("tab 2", + Either.CreateRight( + new RuleContextualSectionWithDefaultContextKeyDto("context2", + new List + { + new("context1content", "context1", "Context 1"), + new("context2content", "context2", "Context 2") + }))), + new("tab 3", + Either.CreateLeft( + new RuleNonContextualSectionDto("content"))), + })); + + + richRuleDescriptionProvider.GetRichRuleDescriptionModel(ruleInfoMock.Object).Should().BeEquivalentTo( + new RichRuleDescription("intro", new List + { + new NonContextualRuleDescriptionTab("tab 1", "content"), + new ContextualRuleDescriptionTab("tab 2", "context2", new List + { + new("Context 1", "context1", "context1content"), + new("Context 2", "context2", "context2content") + }), + new NonContextualRuleDescriptionTab("tab 3", "content"), + })); + } +} diff --git a/src/Education.UnitTests/Layout/Logical/RichRuleDescriptionTests.cs b/src/Education.UnitTests/Layout/Logical/RichRuleDescriptionTests.cs index efb9f7905d..8bccd59e05 100644 --- a/src/Education.UnitTests/Layout/Logical/RichRuleDescriptionTests.cs +++ b/src/Education.UnitTests/Layout/Logical/RichRuleDescriptionTests.cs @@ -32,6 +32,14 @@ namespace SonarLint.VisualStudio.Education.UnitTests.Layout.Logical; [TestClass] public class RichRuleDescriptionTests { + [TestMethod] + public void Ctor_EnsuresHtmlIsXml() + { + var testSubject = new RichRuleDescription( "", new List()); + + testSubject.introductionHtml.Should().BeEquivalentTo(""); + } + [TestMethod] public void ProduceVisualNode_ProducesMultiBlockSectionWithIntroAndTabs() { diff --git a/src/Education.UnitTests/Rule/RuleHelpTests.cs b/src/Education.UnitTests/Rule/RuleHelpTests.cs deleted file mode 100644 index 5eb5e8f760..0000000000 --- a/src/Education.UnitTests/Rule/RuleHelpTests.cs +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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 Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace SonarLint.VisualStudio.Education.UnitTests.Rule -{ - [TestClass] - public class RuleHelpTests - { - - // [TestMethod] - // public void Ctor_SetsProperties() - // { - // var context = new Context("some context key", "some display name"); - // var descriptionSection1 = new DescriptionSection("some descriptionSection key 1", "some htmlcontent 1", context); - // var descriptionSection2 = new DescriptionSection("some descriptionSection key 2", "some htmlcontent 2"); - // var descriptionSections = new[] { descriptionSection1, descriptionSection2 }; - // - // var educationPrinciples = new[] { "defense_in_depth", "never_trust_user_input" }; - // var defaultImpacts = new Dictionary(); - // defaultImpacts.Add(SoftwareQuality.Maintainability, SoftwareQualitySeverity.Medium); - // defaultImpacts.Add(SoftwareQuality.Reliability, SoftwareQualitySeverity.Low); - // - // var tags = new string[] { "convention", "bad-practice" }; - // - // var testSubject = new RuleInfo( - // Language.CSharp.ServerLanguage.Key, - // "xxx:S123", - // "a description", - // "the rule name", - // RuleIssueSeverity.Blocker, - // RuleIssueType.Vulnerability, - // isActiveByDefault: true, - // tags, - // descriptionSections, - // educationPrinciples, - // "some user note", - // CleanCodeAttribute.Respectful, - // defaultImpacts); - // - // testSubject.LanguageKey.Should().Be(Language.CSharp.ServerLanguage.Key); - // testSubject.FullRuleKey.Should().Be("xxx:S123"); - // testSubject.Description.Should().Be("a description"); - // testSubject.Name.Should().Be("the rule name"); - // testSubject.Severity.Should().Be(RuleIssueSeverity.Blocker); - // testSubject.IssueType.Should().Be(RuleIssueType.Vulnerability); - // testSubject.IsActiveByDefault.Should().BeTrue(); - // testSubject.Tags.Should().BeEquivalentTo(tags); - // testSubject.DescriptionSections.Should().BeEquivalentTo(descriptionSections); - // testSubject.EducationPrinciples.Should().BeEquivalentTo(educationPrinciples); - // testSubject.HtmlNote.Should().Be("some user note"); - // testSubject.CleanCodeAttribute.Should().Be(CleanCodeAttribute.Respectful); - // testSubject.DefaultImpacts.Should().BeEquivalentTo(defaultImpacts); - // } - } -} diff --git a/src/Education.UnitTests/Rule/RuleInfoTests.cs b/src/Education.UnitTests/Rule/RuleInfoTests.cs index 254b2d491f..5dadf111c8 100644 --- a/src/Education.UnitTests/Rule/RuleInfoTests.cs +++ b/src/Education.UnitTests/Rule/RuleInfoTests.cs @@ -18,88 +18,138 @@ * 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 SonarLint.VisualStudio.Core; +using SonarLint.VisualStudio.Core.Analysis; +using SonarLint.VisualStudio.Education.Rule; +using SonarLint.VisualStudio.SLCore.Service.Rules.Models; namespace SonarLint.VisualStudio.Education.UnitTests.Rule { [TestClass] public class RuleInfoTests { - // [TestMethod] - // public void Ctor_NullCollectionsAreAllowed_SetToEmptyCollections() - // { - // var testSubject = new RuleInfo( - // languageKey: null, - // fullRuleKey: null, - // description: null, - // name: null, - // RuleIssueSeverity.Unknown, - // RuleIssueType.Unknown, - // isActiveByDefault: false, - // tags: null, - // descriptionSections: null, - // educationPrinciples: null, - // htmlNote: null, - // cleanCodeAttribute: null, - // defaultImpacts: null); - // - // testSubject.Tags.Should().NotBeNull(); - // testSubject.DescriptionSections.Should().NotBeNull(); - // testSubject.EducationPrinciples.Should().NotBeNull(); - // - // testSubject.Tags.Should().HaveCount(0); - // testSubject.DescriptionSections.Should().HaveCount(0); - // testSubject.EducationPrinciples.Should().HaveCount(0); - // } - // - // [TestMethod] - // public void WithCleanCodeTaxonomyDisabled_SetsCctPropertiesToNull() - // { - // var languageKey = "any"; - // var fullRuleKey = "any"; - // var description = "any"; - // var name = "any"; - // var ruleIssueSeverity = RuleIssueSeverity.Critical; - // var ruleIssueType = RuleIssueType.Bug; - // var isActiveByDefault = true; - // IReadOnlyList tags = new []{ "any"}; - // IReadOnlyList descriptionSections = Array.Empty(); - // IReadOnlyList educationPrinciples = Array.Empty(); - // var htmlNote = "any"; - // CleanCodeAttribute? cleanCodeAttribute = CleanCodeAttribute.Focused; - // Dictionary defaultImpacts = new Dictionary(); - // - // var testSubject = new RuleInfo( - // languageKey, - // fullRuleKey, - // description, - // name, - // ruleIssueSeverity, - // ruleIssueType, - // isActiveByDefault, - // tags, - // descriptionSections, - // educationPrinciples, - // htmlNote, - // cleanCodeAttribute, - // defaultImpacts); - // - // var expected = new RuleInfo( - // languageKey, - // fullRuleKey, - // description, - // name, - // ruleIssueSeverity, - // ruleIssueType, - // isActiveByDefault, - // tags, - // descriptionSections, - // educationPrinciples, - // htmlNote, - // null, - // null); - // - // testSubject.WithCleanCodeTaxonomyDisabled().Should().BeEquivalentTo(expected); - // } + [TestMethod] + public void Ctor_NullCollectionsAreAllowed_SetToEmptyCollections() + { + var testSubject = new RuleInfo( + languageKey: null, + fullRuleKey: null, + description: null, + name: null, + RuleIssueSeverity.Unknown, + RuleIssueType.Unknown, + isActiveByDefault: false, + tags: null, + educationPrinciples: null, + htmlNote: null, + richRuleDescriptionDto: null, + cleanCodeAttribute: null, + defaultImpacts: null); + + testSubject.Tags.Should().NotBeNull(); + testSubject.EducationPrinciples.Should().NotBeNull(); + + testSubject.Tags.Should().HaveCount(0); + testSubject.EducationPrinciples.Should().HaveCount(0); + } + + [TestMethod] + public void WithCleanCodeTaxonomyDisabled_SetsCctPropertiesToNull() + { + var languageKey = "any"; + var fullRuleKey = "any"; + var description = "any"; + var name = "any"; + var ruleIssueSeverity = RuleIssueSeverity.Critical; + var ruleIssueType = RuleIssueType.Bug; + var isActiveByDefault = true; + IReadOnlyList tags = new []{ "any"}; + IReadOnlyList educationPrinciples = Array.Empty(); + var htmlNote = "any"; + var richDescription = new RuleSplitDescriptionDto("any", new List()); + CleanCodeAttribute? cleanCodeAttribute = CleanCodeAttribute.Focused; + Dictionary defaultImpacts = new Dictionary(); + + var testSubject = new RuleInfo( + languageKey, + fullRuleKey, + description, + name, + ruleIssueSeverity, + ruleIssueType, + isActiveByDefault, + tags, + educationPrinciples, + htmlNote, + richDescription, + cleanCodeAttribute, + defaultImpacts); + + var expected = new RuleInfo( + languageKey, + fullRuleKey, + description, + name, + ruleIssueSeverity, + ruleIssueType, + isActiveByDefault, + tags, + educationPrinciples, + htmlNote, + richDescription, + null, + null); + + testSubject.WithCleanCodeTaxonomyDisabled().Should().BeEquivalentTo(expected); + } + + [TestMethod] + public void Ctor_SetsProperties() + { + var richDescription = new RuleSplitDescriptionDto("any", new List()); + + var educationPrinciples = new[] { "defense_in_depth", "never_trust_user_input" }; + var defaultImpacts = new Dictionary(); + defaultImpacts.Add(SoftwareQuality.Maintainability, SoftwareQualitySeverity.Medium); + defaultImpacts.Add(SoftwareQuality.Reliability, SoftwareQualitySeverity.Low); + + var tags = new string[] { "convention", "bad-practice" }; + + var testSubject = new RuleInfo( + Language.CSharp.ServerLanguage.Key, + "xxx:S123", + "a description", + "the rule name", + RuleIssueSeverity.Blocker, + RuleIssueType.Vulnerability, + isActiveByDefault: true, + tags, + educationPrinciples, + "some user note", + richDescription, + CleanCodeAttribute.Respectful, + defaultImpacts); + + testSubject.LanguageKey.Should().Be(Language.CSharp.ServerLanguage.Key); + testSubject.FullRuleKey.Should().Be("xxx:S123"); + testSubject.Description.Should().Be("a description"); + testSubject.Name.Should().Be("the rule name"); + testSubject.Severity.Should().Be(RuleIssueSeverity.Blocker); + testSubject.IssueType.Should().Be(RuleIssueType.Vulnerability); + testSubject.IsActiveByDefault.Should().BeTrue(); + testSubject.Tags.Should().BeEquivalentTo(tags); + testSubject.EducationPrinciples.Should().BeEquivalentTo(educationPrinciples); + testSubject.HtmlNote.Should().Be("some user note"); + testSubject.RichRuleDescriptionDto.Should().BeSameAs(richDescription); + testSubject.CleanCodeAttribute.Should().Be(CleanCodeAttribute.Respectful); + testSubject.DefaultImpacts.Should().BeEquivalentTo(defaultImpacts); + } + } + + } diff --git a/src/Education.UnitTests/XamlGenerator/RuleHelpXamlBuilderTests.cs b/src/Education.UnitTests/XamlGenerator/RuleHelpXamlBuilderTests.cs index f8a77ee8e1..a70d686ddc 100644 --- a/src/Education.UnitTests/XamlGenerator/RuleHelpXamlBuilderTests.cs +++ b/src/Education.UnitTests/XamlGenerator/RuleHelpXamlBuilderTests.cs @@ -1,60 +1,61 @@ -// /* -// * 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.Collections.Generic; -// using Microsoft.VisualStudio.TestTools.UnitTesting; -// using Moq; -// using SonarLint.VisualStudio.Education.XamlGenerator; -// using SonarLint.VisualStudio.Rules; -// using SonarLint.VisualStudio.TestInfrastructure; -// -// namespace SonarLint.VisualStudio.Education.UnitTests.XamlGenerator; -// -// [TestClass] -// public class RuleHelpXamlBuilderTests -// { -// [TestMethod] -// public void MefCtor_CheckExports() -// { -// MefTestHelpers.CheckTypeCanBeImported( -// MefTestHelpers.CreateExport(), -// MefTestHelpers.CreateExport()); -// } -// -// [DataTestMethod] -// [DataRow(true)] -// [DataRow(false)] -// public void Create_ChoosesCorrectLayoutBuilder(bool isExtendedRule) -// { -// var selectedIssueContext = "abrakadabra"; -// var ruleInfoMock = new Mock(); -// ruleInfoMock.SetupGet(x => x.DescriptionSections).Returns(isExtendedRule -// ? new List { new DescriptionSection(null, null), new DescriptionSection(null, null) } -// : null); -// var simpleRuleHelpXamlBuilderMock = new Mock(); -// var richRuleHelpXamlBuilderMock = new Mock(); -// var testSubject = new RuleHelpXamlBuilder(simpleRuleHelpXamlBuilderMock.Object, richRuleHelpXamlBuilderMock.Object); -// -// testSubject.Create(ruleInfoMock.Object, selectedIssueContext); -// -// simpleRuleHelpXamlBuilderMock.Verify(x => x.Create(ruleInfoMock.Object), isExtendedRule ? Times.Never : Times.Once); -// richRuleHelpXamlBuilderMock.Verify(x => x.Create(ruleInfoMock.Object, selectedIssueContext), isExtendedRule ? Times.Once : Times.Never); -// } -// } +/* + * 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.Collections.Generic; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using SonarLint.VisualStudio.Education.Rule; +using SonarLint.VisualStudio.Education.XamlGenerator; +using SonarLint.VisualStudio.SLCore.Service.Rules.Models; +using SonarLint.VisualStudio.TestInfrastructure; + +namespace SonarLint.VisualStudio.Education.UnitTests.XamlGenerator; + +[TestClass] +public class RuleHelpXamlBuilderTests +{ + [TestMethod] + public void MefCtor_CheckExports() + { + MefTestHelpers.CheckTypeCanBeImported( + MefTestHelpers.CreateExport(), + MefTestHelpers.CreateExport()); + } + + [DataTestMethod] + [DataRow(true)] + [DataRow(false)] + public void Create_ChoosesCorrectLayoutBuilder(bool isExtendedRule) + { + var selectedIssueContext = "abrakadabra"; + var ruleInfoMock = new Mock(); + ruleInfoMock.SetupGet(x => x.RichRuleDescriptionDto).Returns(isExtendedRule + ? new RuleSplitDescriptionDto("intro", new List()) + : null); + var simpleRuleHelpXamlBuilderMock = new Mock(); + var richRuleHelpXamlBuilderMock = new Mock(); + var testSubject = new RuleHelpXamlBuilder(simpleRuleHelpXamlBuilderMock.Object, richRuleHelpXamlBuilderMock.Object); + + testSubject.Create(ruleInfoMock.Object, selectedIssueContext); + + simpleRuleHelpXamlBuilderMock.Verify(x => x.Create(ruleInfoMock.Object), isExtendedRule ? Times.Never : Times.Once); + richRuleHelpXamlBuilderMock.Verify(x => x.Create(ruleInfoMock.Object, selectedIssueContext), isExtendedRule ? Times.Once : Times.Never); + } +} diff --git a/src/Education.UnitTests/XamlGenerator/XamlGeneratorHelperTests.cs b/src/Education.UnitTests/XamlGenerator/XamlGeneratorHelperTests.cs index 4ea64505a0..3c1ef1a093 100644 --- a/src/Education.UnitTests/XamlGenerator/XamlGeneratorHelperTests.cs +++ b/src/Education.UnitTests/XamlGenerator/XamlGeneratorHelperTests.cs @@ -52,7 +52,7 @@ public void WriteDocumentHeaderAndEndDocument_ExtendedDescription_ProduceCorrect var xmlWriter = new XamlWriterFactory().Create(sb); var ruleInfo = new RuleInfo("cs", "cs:123", "

Hi

", "Hi", RuleIssueSeverity.Critical, RuleIssueType.Vulnerability, true, new List(), - new List(), "

fix this pls

", null, null); + new List(), "

fix this pls

", null,null, null); var testSubject = CreateTestSubject(xmlWriter); @@ -84,7 +84,7 @@ public void WriteDocumentHeaderAndEndDocument_ProduceCorrectStructure() var xmlWriter = new XamlWriterFactory().Create(sb); var ruleInfo = new RuleInfo("cs", "cs:123", "

Hi

", "Hi", RuleIssueSeverity.Critical, RuleIssueType.Vulnerability, true, new List(), - new List(), null, null, null); + new List(), null, null, null, null); IXamlGeneratorHelper testSubject = CreateTestSubject(xmlWriter); testSubject.WriteDocumentHeader(ruleInfo); @@ -117,7 +117,7 @@ public void WriteDocumentHeaderAndEndDocument_ProduceCorrectStructure_NewCCT() var xmlWriter = new XamlWriterFactory().Create(sb); var ruleInfo = new RuleInfo("cs", "cs:123", "

Hi

", "Hi", RuleIssueSeverity.Critical, RuleIssueType.Vulnerability, true, new List(), - new List(), null, CleanCodeAttribute.Formatted, new Dictionary + new List(), null, null, CleanCodeAttribute.Formatted, new Dictionary { { SoftwareQuality.Maintainability, SoftwareQualitySeverity.High}, { SoftwareQuality.Security, SoftwareQualitySeverity.Low}, diff --git a/src/Education.UnitTests/packages.lock.json b/src/Education.UnitTests/packages.lock.json index a1351ac397..2af787fa59 100644 --- a/src/Education.UnitTests/packages.lock.json +++ b/src/Education.UnitTests/packages.lock.json @@ -1554,7 +1554,8 @@ "DiffPlex": "[1.7.1, )", "Microsoft.VisualStudio.Sdk": "[17.0.31902.203, )", "SonarLint.VisualStudio.Core": "[1.0.0, )", - "SonarLint.VisualStudio.Infrastructure.VS": "[1.0.0, )" + "SonarLint.VisualStudio.Infrastructure.VS": "[1.0.0, )", + "SonarLint.VisualStudio.SLCore": "[1.0.0, )" } }, "SonarLint.VisualStudio.Infrastructure.VS": { @@ -1623,6 +1624,13 @@ "SonarLint.VisualStudio.Progress": "[1.0.0, )" } }, + "SonarLint.VisualStudio.SLCore": { + "type": "Project", + "dependencies": { + "SonarLint.VisualStudio.Core": "[1.0.0, )", + "StreamJsonRpc": "[2.5.46, )" + } + }, "sonarqube.client": { "type": "Project", "dependencies": { diff --git a/src/Education/Education.csproj b/src/Education/Education.csproj index 8f9782d7bf..f75a692608 100644 --- a/src/Education/Education.csproj +++ b/src/Education/Education.csproj @@ -48,6 +48,7 @@ + Menus.ctmenu Designer diff --git a/src/Education/Layout/Logical/ContextualRuleDescriptionTab.cs b/src/Education/Layout/Logical/ContextualRuleDescriptionTab.cs index 795f16266f..3dd53bb834 100644 --- a/src/Education/Layout/Logical/ContextualRuleDescriptionTab.cs +++ b/src/Education/Layout/Logical/ContextualRuleDescriptionTab.cs @@ -23,6 +23,7 @@ using System.Linq; using SonarLint.VisualStudio.Education.Layout.Visual; using SonarLint.VisualStudio.Education.Layout.Visual.Tabs; +using SonarLint.VisualStudio.Education.Rule; namespace SonarLint.VisualStudio.Education.Layout.Logical { @@ -78,7 +79,7 @@ public ContextContentTab(string title, string contextKey, string htmlContent) { Title = title; ContextKey = contextKey; - HtmlContent = htmlContent; + HtmlContent = HtmlXmlCompatibilityHelper.EnsureHtmlIsXml(htmlContent); } public string Title { get; } diff --git a/src/Education/Layout/Logical/NonContextualRuleDescriptionTab.cs b/src/Education/Layout/Logical/NonContextualRuleDescriptionTab.cs index adb5cee61f..b9e55fb537 100644 --- a/src/Education/Layout/Logical/NonContextualRuleDescriptionTab.cs +++ b/src/Education/Layout/Logical/NonContextualRuleDescriptionTab.cs @@ -19,17 +19,18 @@ */ using SonarLint.VisualStudio.Education.Layout.Visual; +using SonarLint.VisualStudio.Education.Rule; namespace SonarLint.VisualStudio.Education.Layout.Logical { internal class NonContextualRuleDescriptionTab : IRuleDescriptionTab { - private readonly string htmlContent; + internal /* for testing */ readonly string htmlContent; public NonContextualRuleDescriptionTab(string title, string htmlContent) { Title = title; - this.htmlContent = htmlContent; + this.htmlContent = HtmlXmlCompatibilityHelper.EnsureHtmlIsXml(htmlContent); } public string Title { get; } diff --git a/src/Education/Layout/Logical/RichRuleDescription.cs b/src/Education/Layout/Logical/RichRuleDescription.cs index 68987792d3..79f3d76f2f 100644 --- a/src/Education/Layout/Logical/RichRuleDescription.cs +++ b/src/Education/Layout/Logical/RichRuleDescription.cs @@ -22,6 +22,7 @@ using System.Linq; using SonarLint.VisualStudio.Education.Layout.Visual; using SonarLint.VisualStudio.Education.Layout.Visual.Tabs; +using SonarLint.VisualStudio.Education.Rule; namespace SonarLint.VisualStudio.Education.Layout.Logical { @@ -31,12 +32,12 @@ internal interface IRichRuleDescription : IVisualNodeProducer internal class RichRuleDescription : IRichRuleDescription { - private readonly string introductionHtml; + internal /* for testing */ readonly string introductionHtml; private readonly List tabs; public RichRuleDescription(string introductionHtml, List tabs) { - this.introductionHtml = introductionHtml; + this.introductionHtml = HtmlXmlCompatibilityHelper.EnsureHtmlIsXml(introductionHtml); this.tabs = tabs; } diff --git a/src/Education/Layout/Logical/RichRuleDescriptionConverter.cs b/src/Education/Layout/Logical/RichRuleDescriptionConverter.cs deleted file mode 100644 index 2bca193820..0000000000 --- a/src/Education/Layout/Logical/RichRuleDescriptionConverter.cs +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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.Education.Rule; - -namespace SonarLint.VisualStudio.Education.Layout.Logical -{ - /// - /// Provides from - /// - internal interface IRichRuleDescriptionProvider - { - IRichRuleDescription GetRichRuleDescriptionModel(IRuleInfo ruleInfo); - } -} diff --git a/src/Education/Layout/Logical/RichRuleDescriptionProvider.cs b/src/Education/Layout/Logical/RichRuleDescriptionProvider.cs new file mode 100644 index 0000000000..42824dd03f --- /dev/null +++ b/src/Education/Layout/Logical/RichRuleDescriptionProvider.cs @@ -0,0 +1,62 @@ +/* + * 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.Collections.Generic; +using System.ComponentModel.Composition; +using System.Linq; +using SonarLint.VisualStudio.Education.Rule; +using SonarLint.VisualStudio.SLCore.Service.Rules.Models; + +namespace SonarLint.VisualStudio.Education.Layout.Logical +{ + /// + /// Provides from + /// + internal interface IRichRuleDescriptionProvider + { + IRichRuleDescription GetRichRuleDescriptionModel(IRuleInfo ruleInfo); + } + + [Export(typeof(IRichRuleDescriptionProvider))] + [PartCreationPolicy(CreationPolicy.Shared)] + internal class RichRuleDescriptionProvider : IRichRuleDescriptionProvider + { + public IRichRuleDescription GetRichRuleDescriptionModel(IRuleInfo ruleInfo) => + new RichRuleDescription(ruleInfo.RichRuleDescriptionDto.introductionHtmlContent, CreateMainTabs(ruleInfo)); + + private static List CreateMainTabs(IRuleInfo ruleInfo) => + ruleInfo.RichRuleDescriptionDto.tabs + .Select(tab => tab.content.Left is not null ? CreateNonContextualTab(tab) : CreateContextualTab(tab)) + .ToList(); + + private static IRuleDescriptionTab CreateNonContextualTab(RuleDescriptionTabDto tab) => + new NonContextualRuleDescriptionTab(tab.title, tab.content.Left.htmlContent); + + private static IRuleDescriptionTab CreateContextualTab(RuleDescriptionTabDto tab) => + new ContextualRuleDescriptionTab(tab.title, + tab.content.Right.defaultContextKey, + tab.content.Right.contextualSections + .Select(context => + new ContextualRuleDescriptionTab.ContextContentTab(context.displayName, + context.contextKey, + context.htmlContent)) + .ToList()); + } +} diff --git a/src/Education/Rule/IRuleInfo.cs b/src/Education/Rule/IRuleInfo.cs index 5651984fe9..b359e93710 100644 --- a/src/Education/Rule/IRuleInfo.cs +++ b/src/Education/Rule/IRuleInfo.cs @@ -26,6 +26,7 @@ using System.Collections.Generic; using Newtonsoft.Json; using SonarLint.VisualStudio.Core.Analysis; +using SonarLint.VisualStudio.SLCore.Service.Rules.Models; namespace SonarLint.VisualStudio.Education.Rule { @@ -85,6 +86,8 @@ public interface IRuleInfo IReadOnlyList EducationPrinciples { get; } string HtmlNote { get; } + + RuleSplitDescriptionDto RichRuleDescriptionDto { get; } CleanCodeAttribute? CleanCodeAttribute { get; } @@ -97,7 +100,7 @@ public class RuleInfo : IRuleInfo { public RuleInfo(string languageKey, string fullRuleKey, string description, string name, RuleIssueSeverity severity, RuleIssueType issueType, bool isActiveByDefault, - IReadOnlyList tags, IReadOnlyList educationPrinciples, string htmlNote, + IReadOnlyList tags, IReadOnlyList educationPrinciples, string htmlNote, RuleSplitDescriptionDto richRuleDescriptionDto, CleanCodeAttribute? cleanCodeAttribute, Dictionary defaultImpacts) { LanguageKey = languageKey; @@ -110,6 +113,7 @@ public RuleInfo(string languageKey, string fullRuleKey, string description, stri Tags = tags ?? Array.Empty(); EducationPrinciples = educationPrinciples ?? Array.Empty(); HtmlNote = htmlNote; + RichRuleDescriptionDto = richRuleDescriptionDto; CleanCodeAttribute = cleanCodeAttribute; DefaultImpacts = defaultImpacts ?? new Dictionary(); } @@ -133,6 +137,7 @@ public RuleInfo(string languageKey, string fullRuleKey, string description, stri public IReadOnlyList EducationPrinciples { get; } public string HtmlNote { get; } + public RuleSplitDescriptionDto RichRuleDescriptionDto { get; set; } public CleanCodeAttribute? CleanCodeAttribute { get; } @@ -149,6 +154,7 @@ public IRuleInfo WithCleanCodeTaxonomyDisabled() => Tags, EducationPrinciples, HtmlNote, + RichRuleDescriptionDto, null, null); } diff --git a/src/Education/Rule/RuleExtensions.cs b/src/Education/Rule/RuleExtensions.cs index 40697606df..b5c3a69996 100644 --- a/src/Education/Rule/RuleExtensions.cs +++ b/src/Education/Rule/RuleExtensions.cs @@ -24,10 +24,7 @@ namespace SonarLint.VisualStudio.Education.Rule { public static class RuleExtensions { - public static bool IsRichRuleDescription(this IRuleInfo ruleInfo) - { - throw new NotImplementedException(); // will be re-implemented later - // return ruleInfo.DescriptionSections != null && ruleInfo.DescriptionSections.Count > 1; - } + public static bool IsRichRuleDescription(this IRuleInfo ruleInfo) => + ruleInfo.RichRuleDescriptionDto != null; } } diff --git a/src/Education/packages.lock.json b/src/Education/packages.lock.json index f197f1d130..de20482796 100644 --- a/src/Education/packages.lock.json +++ b/src/Education/packages.lock.json @@ -1130,10 +1130,11 @@ "SonarLint.VisualStudio.Core": "[1.0.0, )" } }, - "SonarLint.VisualStudio.Rules": { + "SonarLint.VisualStudio.SLCore": { "type": "Project", "dependencies": { - "SonarLint.VisualStudio.Core": "[1.0.0, )" + "SonarLint.VisualStudio.Core": "[1.0.0, )", + "StreamJsonRpc": "[2.5.46, )" } }, "sonarqube.client": { diff --git a/src/Integration.Vsix.UnitTests/packages.lock.json b/src/Integration.Vsix.UnitTests/packages.lock.json index faae7d1c45..0150669fef 100644 --- a/src/Integration.Vsix.UnitTests/packages.lock.json +++ b/src/Integration.Vsix.UnitTests/packages.lock.json @@ -1617,7 +1617,8 @@ "DiffPlex": "[1.7.1, )", "Microsoft.VisualStudio.Sdk": "[17.0.31902.203, )", "SonarLint.VisualStudio.Core": "[1.0.0, )", - "SonarLint.VisualStudio.Infrastructure.VS": "[1.0.0, )" + "SonarLint.VisualStudio.Infrastructure.VS": "[1.0.0, )", + "SonarLint.VisualStudio.SLCore": "[1.0.0, )" } }, "SonarLint.VisualStudio.Infrastructure.VS": { diff --git a/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithStrongNames.txt b/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithStrongNames.txt index 2e16b9e54a..6338bdcbd8 100644 --- a/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithStrongNames.txt +++ b/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithStrongNames.txt @@ -1,7 +1,7 @@ --- ################################ # Assembly references report -# Report date/time: 2024-02-12T15:03:48.2745900Z +# Report date/time: 2024-02-14T10:22:34.7734356Z ################################ # # Generated by Devtility CheckAsmRefs v0.11.0.223 @@ -221,13 +221,14 @@ Referenced assemblies: - 'PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' - 'SonarLint.VisualStudio.Core, Version=7.7.0.0, Culture=neutral, PublicKeyToken=c5b62af9de6d7244' - 'SonarLint.VisualStudio.Infrastructure.VS, Version=7.7.0.0, Culture=neutral, PublicKeyToken=c5b62af9de6d7244' +- 'SonarLint.VisualStudio.SLCore, Version=7.7.0.0, Culture=neutral, PublicKeyToken=c5b62af9de6d7244' - 'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'System.ComponentModel.Composition, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' - 'System.Xaml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' -# Number of references: 19 +# Number of references: 20 --- Assembly: 'SonarLint.VisualStudio.Infrastructure.VS, Version=7.8.0.0, Culture=neutral, PublicKeyToken=c5b62af9de6d7244' diff --git a/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithoutStrongNames.txt b/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithoutStrongNames.txt index 9c516a378a..9c9d7d6e0e 100644 --- a/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithoutStrongNames.txt +++ b/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithoutStrongNames.txt @@ -1,7 +1,7 @@ --- ################################ # Assembly references report -# Report date/time: 2024-02-12T15:03:48.2745900Z +# Report date/time: 2024-02-14T10:22:34.7734356Z ################################ # # Generated by Devtility CheckAsmRefs v0.11.0.223 @@ -221,13 +221,14 @@ Referenced assemblies: - 'PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' - 'SonarLint.VisualStudio.Core, Version=7.7.0.0, Culture=neutral, PublicKeyToken=null' - 'SonarLint.VisualStudio.Infrastructure.VS, Version=7.7.0.0, Culture=neutral, PublicKeyToken=null' +- 'SonarLint.VisualStudio.SLCore, Version=7.7.0.0, Culture=neutral, PublicKeyToken=null' - 'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'System.ComponentModel.Composition, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' - 'System.Xaml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' -# Number of references: 19 +# Number of references: 20 --- Assembly: 'SonarLint.VisualStudio.Infrastructure.VS, Version=7.8.0.0, Culture=neutral, PublicKeyToken=null' diff --git a/src/Integration.Vsix/packages.lock.json b/src/Integration.Vsix/packages.lock.json index 494dd3d992..c5576d48fb 100644 --- a/src/Integration.Vsix/packages.lock.json +++ b/src/Integration.Vsix/packages.lock.json @@ -1578,7 +1578,8 @@ "DiffPlex": "[1.7.1, )", "Microsoft.VisualStudio.Sdk": "[17.0.31902.203, )", "SonarLint.VisualStudio.Core": "[1.0.0, )", - "SonarLint.VisualStudio.Infrastructure.VS": "[1.0.0, )" + "SonarLint.VisualStudio.Infrastructure.VS": "[1.0.0, )", + "SonarLint.VisualStudio.SLCore": "[1.0.0, )" } }, "SonarLint.VisualStudio.Infrastructure.VS": { diff --git a/src/SLCore/Protocol/Either.cs b/src/SLCore/Protocol/Either.cs index 1a52524ebc..d20d7e027d 100644 --- a/src/SLCore/Protocol/Either.cs +++ b/src/SLCore/Protocol/Either.cs @@ -18,6 +18,8 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +using System; + namespace SonarLint.VisualStudio.SLCore.Protocol { /// @@ -34,7 +36,7 @@ private Either() public TLeft Left { get; private set; } public TRight Right { get; private set; } - public static Either CreateLeft(TLeft left) => new Either { Left = left }; - public static Either CreateRight(TRight right) => new Either { Right = right }; + public static Either CreateLeft(TLeft left) => new() { Left = left ?? throw new ArgumentNullException(nameof(left))}; + public static Either CreateRight(TRight right) => new() { Right = right ?? throw new ArgumentNullException(nameof(right))}; } }