From f789bfa77ecc15b54a04938cbf135792ea846798 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Fri, 14 Jun 2024 14:21:14 +0200 Subject: [PATCH] Add tests to CodeFixProviderSnippetTest --- .../CodeFixProviderSnippetTest.cs | 103 +++++++++++++++--- 1 file changed, 85 insertions(+), 18 deletions(-) diff --git a/analyzers/tests/SonarAnalyzer.TestFramework.Test/Verification/CodeFixProviderSnippetTest.cs b/analyzers/tests/SonarAnalyzer.TestFramework.Test/Verification/CodeFixProviderSnippetTest.cs index 6ce7db10e18..4530b82f7dc 100644 --- a/analyzers/tests/SonarAnalyzer.TestFramework.Test/Verification/CodeFixProviderSnippetTest.cs +++ b/analyzers/tests/SonarAnalyzer.TestFramework.Test/Verification/CodeFixProviderSnippetTest.cs @@ -36,23 +36,88 @@ public void VerifyCodeFix_Snippet() var verifier = new VerifierBuilder { Analyzers = [() => new TestRule([SyntaxKind.ClassDeclaration])], - CodeFix = () => new TestRuleCodeFix(async (document, node) => - document.WithSyntaxRoot( - (await node.SyntaxTree.GetRootAsync()).ReplaceNode(node, node is ClassDeclarationSyntax classDeclaration - ? classDeclaration.WithIdentifier(SyntaxFactory.Identifier("Changed").WithTriviaFrom(classDeclaration.Identifier)) - : node))), + CodeFix = () => new TestRuleCodeFix((root, node) => + root.ReplaceNode(node, node.WithIdentifier(SyntaxFactory.Identifier("Changed").WithTriviaFrom(node.Identifier)))), } .AddSnippet(""" class C { } """) - .WithCodeFixedSnippet( - """ + .WithCodeFixedSnippet(""" class Changed { } """); Action a = () => verifier.VerifyCodeFix(); a.Should().NotThrow(); } + [TestMethod] + public void VerifyCodeFix_Snippet_Fail() + { + var verifier = new VerifierBuilder + { + Analyzers = [() => new TestRule([SyntaxKind.ClassDeclaration])], + CodeFix = () => new TestRuleCodeFix((root, node) => + root.ReplaceNode(node, node.WithIdentifier(SyntaxFactory.Identifier("ActualChange").WithTriviaFrom(node.Identifier)))), + } + .AddSnippet(""" + class C { } + """) + .WithCodeFixedSnippet(""" + class ExpectedChange { } + """); + Action a = () => verifier.VerifyCodeFix(); + a.Should().Throw().WithMessage( + """Expected ActualCodeWithReplacedComments().ToUnixLineEndings() to be "class ExpectedChange { }" with a length of 24 """ + + """because VerifyWhileDocumentChanges updates the document until all issues are fixed, even if the fix itself creates a new issue again. Language: CSharp7, """ + + """but "class ActualChange { }" has a length of 22, differs near "Act" (index 6)."""); + } + + [TestMethod] + public void VerifyCodeFix_Snippet_Fail_WithNoncompliantComment() + { + var verifier = new VerifierBuilder + { + Analyzers = [() => new TestRule([SyntaxKind.ClassDeclaration])], + CodeFix = () => new TestRuleCodeFix((root, node) => + root.ReplaceNode(node, node.WithIdentifier(SyntaxFactory.Identifier("ActualChange").WithTriviaFrom(node.Identifier)))), + } + .AddSnippet(""" + class C { } // Noncompliant + """) + .WithCodeFixedSnippet(""" + class ExpectedChange { } // Fixed + """); + Action a = () => verifier.VerifyCodeFix(); + a.Should().Throw().WithMessage( + """Expected ActualCodeWithReplacedComments().ToUnixLineEndings() to be "class ExpectedChange { } // Fixed" with a length of 33 """ + + """because VerifyWhileDocumentChanges updates the document until all issues are fixed, even if the fix itself creates a new issue again. Language: CSharp7, """ + + """but "class ActualChange { } // Fixed" has a length of 31, differs near "Act" (index 6)."""); + } + + [DataTestMethod] + [DataRow(true)] + [DataRow(false)] + public void VerifyCodeFix_Snippet_MultipleIssues(bool withFixAllProvider) + { + var verifier = new VerifierBuilder + { + Analyzers = [() => new TestRule([SyntaxKind.ClassDeclaration], x => x is ClassDeclarationSyntax c && c.Identifier.ValueText.Length == 1)], + CodeFix = () => new TestRuleCodeFix(withFixAllProvider, (root, node) => + root.ReplaceNode(node, node.WithIdentifier(SyntaxFactory.Identifier($"{node.Identifier.ValueText}Changed").WithTriviaFrom(node.Identifier)))), + } + .AddSnippet(""" + class A { } // Noncompliant + class B { } // Noncompliant + class C { } // Noncompliant + """) + .WithCodeFixedSnippet(""" + class AChanged { } // Fixed + class BChanged { } // Fixed + class CChanged { } // Fixed + """); + Action a = () => verifier.VerifyCodeFix(); + a.Should().NotThrow(); + } + [DiagnosticAnalyzer(LanguageNames.CSharp)] public class TestRule : SonarDiagnosticAnalyzer { @@ -78,27 +143,29 @@ protected override void Initialize(SonarAnalysisContext context) => { c.ReportIssue(rule, c.Context.Node); } - }, [..RegisterSyntaxKinds]); + }, RegisterSyntaxKinds); } [ExportCodeFixProvider(LanguageNames.CSharp)] - public class TestRuleCodeFix : SonarCodeFix + public class TestRuleCodeFix(bool withFixAllProvider, Func createChangedRoot) : SonarCodeFix where TSyntax : SyntaxNode { public override ImmutableArray FixableDiagnosticIds => [TestRule.DiagnosticId]; - public Func> CreateChangedDocument { get; } + public TestRuleCodeFix(Func createChangedRoot) : this(true, createChangedRoot) { } - public TestRuleCodeFix(Func> createChangedDocument) - { - CreateChangedDocument = createChangedDocument; - } + public override FixAllProvider GetFixAllProvider() => withFixAllProvider + ? base.GetFixAllProvider() + : null; protected override Task RegisterCodeFixesAsync(SyntaxNode root, SonarCodeFixContext context) { - context.RegisterCodeFix("TestTitle", async x => await CreateChangedDocument( - context.Document, - (await context.Document.GetSyntaxRootAsync(x)).FindNode(context.Span)), - context.Diagnostics); + context.RegisterCodeFix("TestTitle", async x => + { + var root = await context.Document.GetSyntaxRootAsync(x); + var node = (TSyntax)root.FindNode(context.Span); + var newRoot = createChangedRoot(root, node); + return context.Document.WithSyntaxRoot(newRoot); + }, context.Diagnostics); return Task.CompletedTask; } }