File: src\Analyzers\CSharp\Tests\DocumentationComments\AddDocCommentNodesCodeFixProviderTests.cs
Web Access
Project: src\src\CodeStyle\CSharp\Tests\Microsoft.CodeAnalysis.CSharp.CodeStyle.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.CodeStyle.UnitTests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.DocumentationComments;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
using Xunit.Abstractions;
 
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.DocumentationComments;
 
[Trait(Traits.Feature, Traits.Features.CodeActionsAddDocCommentNodes)]
public class AddDocCommentNodesCodesFixProviderTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest_NoEditor
{
    private static readonly CSharpParseOptions Regular = new(kind: SourceCodeKind.Regular);
 
    public AddDocCommentNodesCodesFixProviderTests(ITestOutputHelper logger)
       : base(logger)
    {
    }
 
    internal override (DiagnosticAnalyzer?, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace)
        => (null, new CSharpAddDocCommentNodesCodeFixProvider());
 
    private async Task TestAsync(string initial, string expected)
    {
        var parseOptions = Regular.WithDocumentationMode(DocumentationMode.Diagnose);
        await TestAsync(initial, expected, parseOptions: parseOptions);
    }
 
    [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddDocCommentNodes)]
    public async Task AddsParamTag_NoNodesBefore()
    {
        var initial =
            """
            class Program
            {
                /// <summary>
                /// 
                /// </summary>
                /// <param name="j"></param>
                public void Fizz(int [|i|], int j, int k) {}
            }
            """;
 
        var expected =
            """
            class Program
            {
                /// <summary>
                /// 
                /// </summary>
                /// <param name="i"></param>
                /// <param name="j"></param>
                /// <param name="k"></param>
                public void Fizz(int i, int j, int k) {}
            }
            """;
        await TestAsync(initial, expected);
    }
 
    [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddDocCommentNodes)]
    public async Task AddsParamTag_NoNodesAfter()
    {
        var initial =
            """
            class Program
            {
                /// <summary>
                /// 
                /// </summary>
                /// <param name="j"></param>
                public void Fizz(int i, int j, int [|k|]) {}
            }
            """;
 
        var expected =
            """
            class Program
            {
                /// <summary>
                /// 
                /// </summary>
                /// <param name="i"></param>
                /// <param name="j"></param>
                /// <param name="k"></param>
                public void Fizz(int i, int j, int k) {}
            }
            """;
        await TestAsync(initial, expected);
    }
 
    [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddDocCommentNodes)]
    public async Task AddsParamTag_NodesBeforeAndAfter()
    {
        var initial =
            """
            class Program
            {
                /// <summary>
                /// 
                /// </summary>
                /// <param name="i"></param>
                /// <param name="k"></param>
                public void Fizz(int i, int [|j|], int k) {}
            }
            """;
 
        var expected =
            """
            class Program
            {
                /// <summary>
                /// 
                /// </summary>
                /// <param name="i"></param>
                /// <param name="j"></param>
                /// <param name="k"></param>
                public void Fizz(int i, int j, int k) {}
            }
            """;
        await TestAsync(initial, expected);
    }
 
    [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddDocCommentNodes)]
    public async Task AddsParamTag_NodesBeforeAndAfter_RawTextInComment()
    {
        var initial =
            """
            class Program
            {
                /// <summary>
                /// 
                /// </summary>
                /// <param name="i"></param>
                /// text
                /// <param name="k"></param>
                public void Fizz(int i, int [|j|], int k) {}
            }
            """;
 
        var expected =
            """
            class Program
            {
                /// <summary>
                /// 
                /// </summary>
                /// <param name="i"></param>
                /// <param name="j"></param>
                /// text
                /// <param name="k"></param>
                public void Fizz(int i, int j, int k) {}
            }
            """;
        await TestAsync(initial, expected);
    }
 
    [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddDocCommentNodes)]
    public async Task AddsParamTag_NodesBeforeAndAfter_WithContent()
    {
        var initial =
            """
            class Program
            {
                /// <summary>
                /// 
                /// </summary>
                /// <param name="i">Parameter <paramref name="i"/> does something</param>
                /// <param name="k">Parameter <paramref name="k"/> does something else</param>
                public void Fizz(int i, int [|j|], int k) {}
            }
            """;
 
        var expected =
            """
            class Program
            {
                /// <summary>
                /// 
                /// </summary>
                /// <param name="i">Parameter <paramref name="i"/> does something</param>
                /// <param name="j"></param>
                /// <param name="k">Parameter <paramref name="k"/> does something else</param>
                public void Fizz(int i, int j, int k) {}
            }
            """;
        await TestAsync(initial, expected);
    }
 
    [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddDocCommentNodes)]
    public async Task AddsParamTag_NestedInSummaryTag()
    {
        var initial =
            """
            class Program
            {
                /// <summary>
                /// <param name="j"></param>
                /// </summary>
                public void Fizz(int i, int j, int [|k|]) {}
            }
            """;
 
        var expected =
            """
            class Program
            {
                /// <summary>
                /// <param name="i"></param>
                /// <param name="j"></param>
                /// <param name="k"></param>
                /// </summary>
                public void Fizz(int i, int j, int k) {}
            }
            """;
        await TestAsync(initial, expected);
    }
 
    [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddDocCommentNodes)]
    public async Task AddsParamTag_BeforeNode_EverythingOnOneLine()
    {
        var initial =
            """
            class Program
            {
                /// <summary></summary> <param name="j"></param>
                public void Fizz(int [|i|], int j, int k) {}
            }
            """;
 
        var expected =
            """
            class Program
            {
                /// <summary></summary>
                /// <param name="i"></param> <param name="j"></param>
                /// <param name="k"></param>
                public void Fizz(int i, int j, int k) {}
            }
            """;
        await TestAsync(initial, expected);
    }
 
    [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddDocCommentNodes)]
    public async Task AddsParamTag_AfterNode_EverythingOnOneLine()
    {
        var initial =
            """
            class Program
            {
                /// <summary></summary> <param name="j"></param>
                public void Fizz(int i, int j, int [|k|]) {}
            }
            """;
 
        var expected =
            """
            class Program
            {
                /// <summary></summary>
                /// <param name="i"></param> <param name="j"></param>
                /// <param name="k"></param>
                public void Fizz(int i, int j, int k) {}
            }
            """;
        await TestAsync(initial, expected);
    }
 
    [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddDocCommentNodes)]
    public async Task AddsParamTag_BeforeNode_JustParamNode()
    {
        var initial =
            """
            class Program
            {
                /// <param name="j"></param>
                public void Fizz(int [|i|], int j, int k) {}
            }
            """;
 
        var expected =
            """
            class Program
            {
                /// <param name="i"></param>
                /// <param name="j"></param>
                /// <param name="k"></param>
                public void Fizz(int i, int j, int k) {}
            }
            """;
        await TestAsync(initial, expected);
    }
 
    [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddDocCommentNodes)]
    public async Task AddsParamTag_AfterNode_JustParamNode()
    {
        var initial =
            """
            class Program
            {
                /// <param name="j"></param>
                public void Fizz(int i, int j, int [|k|]) {}
            }
            """;
 
        var expected =
            """
            class Program
            {
                /// <param name="i"></param>
                /// <param name="j"></param>
                /// <param name="k"></param>
                public void Fizz(int i, int j, int k) {}
            }
            """;
        await TestAsync(initial, expected);
    }
 
    [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddDocCommentNodes)]
    public async Task AddsParamTag_MultipleDocComments()
    {
        var initial =
            """
            class Program
            {
                /// <summary></summary>
                // ...
                /// <summary>
                /// 
                /// </summary>
                /// <param name="j"></param>
                public void Fizz(int [|i|], int j, int k) {}
            }
            """;
 
        var expected =
            """
            class Program
            {
                /// <summary></summary>
                // ...
                /// <summary>
                /// 
                /// </summary>
                /// <param name="i"></param>
                /// <param name="j"></param>
                /// <param name="k"></param>
                public void Fizz(int i, int j, int k) {}
            }
            """;
        await TestAsync(initial, expected);
    }
 
    [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddDocCommentNodes)]
    public async Task AddsParamTag_Ctor()
    {
        var initial =
            """
            class Program
            {
                /// <summary>
                /// 
                /// </summary>
                /// <param name="j"></param>
                public Program(int [|i|], int j, int k) {}
            }
            """;
 
        var expected =
            """
            class Program
            {
                /// <summary>
                /// 
                /// </summary>
                /// <param name="i"></param>
                /// <param name="j"></param>
                /// <param name="k"></param>
                public Program(int i, int j, int k) {}
            }
            """;
        await TestAsync(initial, expected);
    }
 
    [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddDocCommentNodes)]
    public async Task AddsParamTag_Delegate()
    {
        var initial =
            """
            class Program
            {
                /// <summary>
                /// 
                /// </summary>
                /// <param name="j"></param>
                public delegate int Goo(int [|i|], int j, int k);
            }
            """;
 
        var expected =
            """
            class Program
            {
                /// <summary>
                /// 
                /// </summary>
                /// <param name="i"></param>
                /// <param name="j"></param>
                /// <param name="k"></param>
                public delegate int Goo(int [|i|], int j, int k);
            }
            """;
        await TestAsync(initial, expected);
    }
 
    [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddDocCommentNodes)]
    public async Task AddsParamTag_Operator()
    {
        var initial =
            """
            public struct MyStruct
            {
                public int Val { get; }
 
                public MyStruct(int val)
                {
                    Val = val;
                }
 
                /// <summary>
                /// 
                /// </summary>
                /// <param name="s1"></param>
                /// <returns></returns>
                public static MyStruct operator +(MyStruct s1, MyStruct [|s2|])
                {
                    return new MyStruct(s1.Val + s2.Val);
                }
            }
            """;
 
        var expected =
            """
            public struct MyStruct
            {
                public int Val { get; }
 
                public MyStruct(int val)
                {
                    Val = val;
                }
 
                /// <summary>
                /// 
                /// </summary>
                /// <param name="s1"></param>
                /// <param name="s2"></param>
                /// <returns></returns>
                public static MyStruct operator +(MyStruct s1, MyStruct s2)
                {
                    return new MyStruct(s1.Val + s2.Val);
                }
            }
            """;
        await TestAsync(initial, expected);
    }
 
    [Fact]
    [Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)]
    public async Task TestFixAllInDocument_MultipleParamNodesInVariousPlaces()
    {
        var initial =
            """
            class Program
            {
                /// <summary>
                /// <param name="i"></param>
                /// </summary>
                /// <param name="k"></param>
                public void Fizz(int i, int {|FixAllInDocument:j|}, int k, int l) {}
            }
            """;
 
        var expected =
            """
            class Program
            {
                /// <summary>
                /// <param name="i"></param>
                /// </summary>
                /// <param name="i"></param>
                /// <param name="j"></param>
                /// <param name="k"></param>
                /// <param name="l"></param>
                public void Fizz(int i, int j, int k, int l) {}
            }
            """;
 
        await TestAsync(initial, expected);
    }
 
    [Fact]
    [Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)]
    public async Task TestFixAllInDocument()
    {
        var initial = """
            <Workspace>
                <Project Language="C#" AssemblyName="Assembly1" CommonReferences="true" DocumentationMode="Diagnose">
                    <Document>
            <![CDATA[
            class Program1
            {
                /// <summary>
                /// 
                /// </summary>
                /// <param name="i"></param>
                /// <param name="j"></param>
                public void Fizz(int i, int j, {|FixAllInDocument:int k|}) {}
 
                /// <summary>
                /// 
                /// </summary>
                /// <param name="j"></param>
                /// <param name="k"></param>
                /// <returns></returns>
                public int Buzz(int i, int j, int k) { returns 0; }
            }]]>
                    </Document>
                    <Document>
            <![CDATA[
            class Program2
            {
                /// <summary>
                /// 
                /// </summary>
                /// <param name="i"></param>
                /// <param name="j"></param>
                public void Fizz(int i, int j, int k) {}
            }]]>
                    </Document>
                </Project>
                <Project Language="C#" AssemblyName="Assembly2" CommonReferences="true" DocumentationMode="Diagnose">
                    <Document>
            <![CDATA[
            class Program3
            {
                /// <summary>
                /// 
                /// </summary>
                /// <param name="i"></param>
                /// <param name="j"></param>
                public void Fizz(int i, int j, int k) {}
            }]]>
                    </Document>
                </Project>
            </Workspace>
            """;
 
        var expected = """
            <Workspace>
                <Project Language="C#" AssemblyName="Assembly1" CommonReferences="true" DocumentationMode="Diagnose">
                    <Document>
            <![CDATA[
            class Program1
            {
                /// <summary>
                /// 
                /// </summary>
                /// <param name="i"></param>
                /// <param name="j"></param>
                /// <param name="k"></param>
                public void Fizz(int i, int j, int k) {}
 
                /// <summary>
                /// 
                /// </summary>
                /// <param name="i"></param>
                /// <param name="j"></param>
                /// <param name="k"></param>
                /// <returns></returns>
                public int Buzz(int i, int j, int k) { returns 0; }
            }]]>
                    </Document>
                    <Document>
            <![CDATA[
            class Program2
            {
                /// <summary>
                /// 
                /// </summary>
                /// <param name="i"></param>
                /// <param name="j"></param>
                public void Fizz(int i, int j, int k) {}
            }]]>
                    </Document>
                </Project>
                <Project Language="C#" AssemblyName="Assembly2" CommonReferences="true" DocumentationMode="Diagnose">
                    <Document>
            <![CDATA[
            class Program3
            {
                /// <summary>
                /// 
                /// </summary>
                /// <param name="i"></param>
                /// <param name="j"></param>
                public void Fizz(int i, int j, int k) {}
            }]]>
                    </Document>
                </Project>
            </Workspace>
            """;
 
        await TestAsync(initial, expected);
    }
 
    [Fact]
    [Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)]
    public async Task TestFixAllInProject()
    {
        var initial = """
            <Workspace>
                <Project Language="C#" AssemblyName="Assembly1" CommonReferences="true" DocumentationMode="Diagnose">
                    <Document>
            <![CDATA[
            class Program1
            {
                /// <summary>
                /// 
                /// </summary>
                /// <param name="i"></param>
                /// <param name="j"></param>
                public void Fizz(int i, int j, {|FixAllInProject:int k|}) {}
            }]]>
                    </Document>
                    <Document>
            <![CDATA[
            class Program2
            {
                /// <summary>
                /// 
                /// </summary>
                /// <param name="j"></param>
                /// <param name="k"></param>
                /// <returns></returns>
                public int Buzz(int i, int j, int k) { returns 0; }
            }]]>
                    </Document>
                </Project>
                <Project Language="C#" AssemblyName="Assembly2" CommonReferences="true" DocumentationMode="Diagnose">
                    <Document>
            <![CDATA[
            class Program3
            {
                /// <summary>
                /// 
                /// </summary>
                /// <param name="j"></param>
                /// <param name="k"></param>
                /// <returns></returns>
                public int Buzz(int i, int j, int k) { returns 0; }
            }]]>
                    </Document>
                </Project>
            </Workspace>
            """;
 
        var expected = """
            <Workspace>
                <Project Language="C#" AssemblyName="Assembly1" CommonReferences="true">
                    <Document>
            <![CDATA[
            class Program1
            {
                /// <summary>
                /// 
                /// </summary>
                /// <param name="i"></param>
                /// <param name="j"></param>
                /// <param name="k"></param>
                public void Fizz(int i, int j, int k) {}
            }]]>
                    </Document>
                    <Document>
            <![CDATA[
            class Program2
            {
                /// <summary>
                /// 
                /// </summary>
                /// <param name="i"></param>
                /// <param name="j"></param>
                /// <param name="k"></param>
                /// <returns></returns>
                public int Buzz(int i, int j, int k) { returns 0; }
            }]]>
                    </Document>
                </Project>
                <Project Language="C#" AssemblyName="Assembly2" CommonReferences="true">
                    <Document>
            <![CDATA[
            class Program3
            {
                /// <summary>
                /// 
                /// </summary>
                /// <param name="j"></param>
                /// <param name="k"></param>
                /// <returns></returns>
                public int Buzz(int i, int j, int k) { returns 0; }
            }]]>
                    </Document>
                </Project>
            </Workspace>
            """;
 
        await TestAsync(initial, expected);
    }
 
    [Fact]
    [Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)]
    public async Task TestFixAllInSolution()
    {
        var initial = """
            <Workspace>
                <Project Language="C#" AssemblyName="Assembly1" CommonReferences="true" DocumentationMode="Diagnose">
                    <Document>
            <![CDATA[
            class Program1
            {
                /// <summary>
                /// 
                /// </summary>
                /// <param name="i"></param>
                /// <param name="j"></param>
                public void Fizz(int i, int j, {|FixAllInSolution:int k|}) {}
            }]]>
                    </Document>
                    <Document>
            <![CDATA[
            class Program2
            {
                /// <summary>
                /// 
                /// </summary>
                /// <param name="j"></param>
                public void Fizz(int i, int j, int k) {}
            }]]>
                    </Document>
                </Project>
                <Project Language="C#" AssemblyName="Assembly2" CommonReferences="true" DocumentationMode="Diagnose">
                    <Document>
            <![CDATA[
            class Program3
            {
                /// <summary>
                /// 
                /// </summary>
                /// <param name="i"></param>
                /// <param name="j"></param>
                public void Fizz(int i, int j, int k) {}
            }]]>
                    </Document>
                </Project>
            </Workspace>
            """;
 
        var expected = """
            <Workspace>
                <Project Language="C#" AssemblyName="Assembly1" CommonReferences="true" DocumentationMode="Diagnose">
                    <Document>
            <![CDATA[
            class Program1
            {
                /// <summary>
                /// 
                /// </summary>
                /// <param name="i"></param>
                /// <param name="j"></param>
                /// <param name="k"></param>
                public void Fizz(int i, int j, int k) {}
            }]]>
                    </Document>
                    <Document>
            <![CDATA[
            class Program2
            {
                /// <summary>
                /// 
                /// </summary>
                /// <param name="i"></param>
                /// <param name="j"></param>
                /// <param name="k"></param>
                public void Fizz(int i, int j, int k) {}
            }]]>
                    </Document>
                </Project>
                <Project Language="C#" AssemblyName="Assembly2" CommonReferences="true" DocumentationMode="Diagnose">
                    <Document>
            <![CDATA[
            class Program3
            {
                /// <summary>
                /// 
                /// </summary>
                /// <param name="i"></param>
                /// <param name="j"></param>
                /// <param name="k"></param>
                public void Fizz(int i, int j, int k) {}
            }]]>
                    </Document>
                </Project>
            </Workspace>
            """;
 
        await TestAsync(initial, expected);
    }
 
    [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddDocCommentNodes)]
    [WorkItem("https://github.com/dotnet/roslyn/issues/52738")]
    public async Task AddsParamTag_Record()
    {
        var initial = """
            /// <summary>
            /// 
            /// </summary>
            /// <param name="Second"></param>
            record R(int [|First|], int Second, int Third);
            """;
 
        var expected = """
            /// <summary>
            /// 
            /// </summary>
            /// <param name="First"></param>
            /// <param name="Second"></param>
            /// <param name="Third"></param>
            record R(int First, int Second, int Third);
            """;
        await TestAsync(initial, expected);
    }
 
    [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddDocCommentNodes)]
    public async Task AddsParamTag_Class()
    {
        var initial = """
            /// <summary>
            /// 
            /// </summary>
            /// <param name="Second"></param>
            class R(int [|First|], int Second, int Third);
            """;
 
        var expected = """
            /// <summary>
            /// 
            /// </summary>
            /// <param name="First"></param>
            /// <param name="Second"></param>
            /// <param name="Third"></param>
            class R(int First, int Second, int Third);
            """;
        await TestAsync(initial, expected);
    }
 
    [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddDocCommentNodes)]
    public async Task AddsParamTag_Struct()
    {
        var initial = """
            /// <summary>
            /// 
            /// </summary>
            /// <param name="Second"></param>
            struct R(int [|First|], int Second, int Third);
            """;
 
        var expected = """
            /// <summary>
            /// 
            /// </summary>
            /// <param name="First"></param>
            /// <param name="Second"></param>
            /// <param name="Third"></param>
            struct R(int First, int Second, int Third);
            """;
        await TestAsync(initial, expected);
    }
}