File: src\Analyzers\CSharp\Tests\FileHeaders\FileHeaderTests.cs
Web Access
Project: src\src\Features\CSharpTest\Microsoft.CodeAnalysis.CSharp.Features.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.Features.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.Formatting;
using Microsoft.CodeAnalysis.Testing;
using Roslyn.Test.Utilities;
using Xunit;
using VerifyCS = Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions.CSharpCodeFixVerifier<
    Microsoft.CodeAnalysis.CSharp.FileHeaders.CSharpFileHeaderDiagnosticAnalyzer,
    Microsoft.CodeAnalysis.CSharp.FileHeaders.CSharpFileHeaderCodeFixProvider>;
 
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.FileHeaders;
 
public class FileHeaderTests
{
    private const string TestSettings = """
        [*.cs]
        file_header_template = Copyright (c) SomeCorp. All rights reserved.\nLicensed under the ??? license. See LICENSE file in the project root for full license information.
        """;
 
    private const string TestSettingsWithEmptyLines = """
        [*.cs]
        file_header_template = \nCopyright (c) SomeCorp. All rights reserved.\n\nLicensed under the ??? license. See LICENSE file in the project root for full license information.\n
        """;
 
    /// <summary>
    /// Verifies that the analyzer will not report a diagnostic when the file header is not configured.
    /// </summary>
    /// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
    [Theory]
    [InlineData("")]
    [InlineData("file_header_template =")]
    [InlineData("file_header_template = unset")]
    public async Task TestFileHeaderNotConfiguredAsync(string fileHeaderTemplate)
    {
        var testCode = """
            namespace N
            {
            }
            """;
 
        await new VerifyCS.Test
        {
            TestCode = testCode,
            EditorConfig = $@"
[*]
{fileHeaderTemplate}
",
        }.RunAsync();
    }
 
    /// <summary>
    /// Verifies that the analyzer will report a diagnostic when the file is completely missing a header.
    /// </summary>
    /// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
    [Theory]
    [InlineData("\n")]
    [InlineData("\r\n")]
    [WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1414432")]
    public async Task TestNoFileHeaderAsync(string lineEnding)
    {
        var testCode = """
            [||]namespace N
            {
            }
            """;
        var fixedCode = """
            // Copyright (c) SomeCorp. All rights reserved.
            // Licensed under the ??? license. See LICENSE file in the project root for full license information.
 
            namespace N
            {
            }
            """;
 
        await new VerifyCS.Test
        {
            TestCode = testCode.ReplaceLineEndings(lineEnding),
            FixedCode = fixedCode.ReplaceLineEndings(lineEnding),
            EditorConfig = TestSettings,
            Options =
            {
                { FormattingOptions2.NewLine, lineEnding },
            },
        }.RunAsync();
    }
 
    /// <summary>
    /// Verifies that the analyzer will report a diagnostic when the file is completely missing a header.
    /// </summary>
    /// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
    [Fact]
    public async Task TestNoFileHeaderWithUsingDirectiveAsync()
    {
        var testCode = """
            [||]using System;
 
            namespace N
            {
            }
            """;
        var fixedCode = """
            // Copyright (c) SomeCorp. All rights reserved.
            // Licensed under the ??? license. See LICENSE file in the project root for full license information.
 
            using System;
 
            namespace N
            {
            }
            """;
 
        await new VerifyCS.Test
        {
            TestCode = testCode,
            FixedCode = fixedCode,
            EditorConfig = TestSettings,
        }.RunAsync();
    }
 
    /// <summary>
    /// Verifies that the analyzer will report a diagnostic when the file is completely missing a header.
    /// </summary>
    /// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
    [Fact]
    public async Task TestNoFileHeaderWithBlankLineAndUsingDirectiveAsync()
    {
        var testCode = """
            [||]
            using System;
 
            namespace N
            {
            }
            """;
        var fixedCode = """
            // Copyright (c) SomeCorp. All rights reserved.
            // Licensed under the ??? license. See LICENSE file in the project root for full license information.
 
            using System;
 
            namespace N
            {
            }
            """;
 
        await new VerifyCS.Test
        {
            TestCode = testCode,
            FixedCode = fixedCode,
            EditorConfig = TestSettings,
        }.RunAsync();
    }
 
    /// <summary>
    /// Verifies that the analyzer will report a diagnostic when the file is completely missing a header.
    /// </summary>
    /// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
    [Fact]
    public async Task TestNoFileHeaderWithWhitespaceLineAsync()
    {
        var testCode = "[||]    " + """
 
            using System;
 
            namespace N
            {
            }
            """;
        var fixedCode = """
            // Copyright (c) SomeCorp. All rights reserved.
            // Licensed under the ??? license. See LICENSE file in the project root for full license information.
 
            using System;
 
            namespace N
            {
            }
            """;
 
        await new VerifyCS.Test
        {
            TestCode = testCode,
            FixedCode = fixedCode,
            EditorConfig = TestSettings,
        }.RunAsync();
    }
 
    /// <summary>
    /// Verifies that the built-in variable <c>fileName</c> works as expected.
    /// </summary>
    /// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
    [Fact]
    public async Task TestFileNameBuiltInVariableAsync()
    {
        var editorConfig = """
            [*.cs]
            file_header_template = {fileName} Copyright (c) SomeCorp. All rights reserved.\nLicensed under the ??? license. See LICENSE file in the project root for full license information.
            """;
 
        var testCode = """
            [||]namespace N
            {
            }
            """;
        var fixedCode = """
            // Test0.cs Copyright (c) SomeCorp. All rights reserved.
            // Licensed under the ??? license. See LICENSE file in the project root for full license information.
 
            namespace N
            {
            }
            """;
 
        await new VerifyCS.Test
        {
            TestCode = testCode,
            FixedCode = fixedCode,
            EditorConfig = editorConfig,
        }.RunAsync();
    }
 
    /// <summary>
    /// Verifies that a valid file header built using single line comments will not produce a diagnostic message.
    /// </summary>
    /// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
    [Fact]
    public async Task TestValidFileHeaderWithSingleLineCommentsAsync()
    {
        var testCode = """
            // Copyright (c) SomeCorp. All rights reserved.
            // Licensed under the ??? license. See LICENSE file in the project root for full license information.
 
            namespace Bar
            {
            }
            """;
 
        await new VerifyCS.Test
        {
            TestCode = testCode,
            EditorConfig = TestSettings,
        }.RunAsync();
    }
 
    /// <summary>
    /// Verifies that a valid file header built using multi-line comments will not produce a diagnostic message.
    /// </summary>
    /// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
    [Fact]
    public async Task TestValidFileHeaderWithMultiLineComments1Async()
    {
        var testCode = """
            /* Copyright (c) SomeCorp. All rights reserved.
             * Licensed under the ??? license. See LICENSE file in the project root for full license information.
             */
 
            namespace Bar
            {
            }
            """;
 
        await new VerifyCS.Test
        {
            TestCode = testCode,
            EditorConfig = TestSettings,
        }.RunAsync();
    }
 
    /// <summary>
    /// Verifies that a valid file header built using multi-line comments will not produce a diagnostic message.
    /// </summary>
    /// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
    [Fact]
    public async Task TestValidFileHeaderWithMultiLineComments2Async()
    {
        var testCode = """
            /* Copyright (c) SomeCorp. All rights reserved.
               Licensed under the ??? license. See LICENSE file in the project root for full license information. */
 
            namespace Bar
            {
            }
            """;
 
        await new VerifyCS.Test
        {
            TestCode = testCode,
            EditorConfig = TestSettings,
        }.RunAsync();
    }
 
    /// <summary>
    /// Verifies that a valid file header built using unterminated multi-line comments will not produce a diagnostic
    /// message.
    /// </summary>
    /// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
    [Fact]
    public async Task TestValidFileHeaderWithMultiLineComments3Async()
    {
        var testCode = """
            /* Copyright (c) SomeCorp. All rights reserved.
               Licensed under the ??? license. See LICENSE file in the project root for full license information.
            """;
 
        await new VerifyCS.Test
        {
            TestCode = testCode,
            ExpectedDiagnostics =
            {
                // /0/Test0.cs(1,1): error CS1035: End-of-file found, '*/' expected
                DiagnosticResult.CompilerError("CS1035").WithSpan(1, 1, 1, 1),
            },
            FixedCode = testCode,
            EditorConfig = TestSettings,
        }.RunAsync();
    }
 
    /// <summary>
    /// Verifies that a file header without text / only whitespace will produce the expected diagnostic message.
    /// </summary>
    /// <param name="comment">The comment text.</param>
    /// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
    [Theory]
    [InlineData("[|//|]")]
    [InlineData("[|//|]    ")]
    public async Task TestInvalidFileHeaderWithoutTextAsync(string comment)
    {
        var testCode = $@"{comment}
 
namespace Bar
{{
}}";
        var fixedCode = """
            // Copyright (c) SomeCorp. All rights reserved.
            // Licensed under the ??? license. See LICENSE file in the project root for full license information.
 
            namespace Bar
            {
            }
            """;
 
        await new VerifyCS.Test
        {
            TestCode = testCode,
            FixedCode = fixedCode,
            EditorConfig = TestSettings,
        }.RunAsync();
    }
 
    /// <summary>
    /// Verifies that an invalid file header built using single line comments will produce the expected diagnostic message.
    /// </summary>
    /// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
    [Fact]
    public async Task TestInvalidFileHeaderWithWrongTextAsync()
    {
        var testCode = """
            [|//|] Copyright (c) OtherCorp. All rights reserved.
            // Licensed under the ??? license. See LICENSE file in the project root for full license information.
 
            namespace Bar
            {
            }
            """;
        var fixedCode = """
            // Copyright (c) SomeCorp. All rights reserved.
            // Licensed under the ??? license. See LICENSE file in the project root for full license information.
 
            namespace Bar
            {
            }
            """;
 
        await new VerifyCS.Test
        {
            TestCode = testCode,
            FixedCode = fixedCode,
            EditorConfig = TestSettings,
        }.RunAsync();
    }
 
    /// <summary>
    /// Verifies that an invalid file header built using single line comments will produce the expected diagnostic message.
    /// </summary>
    /// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
    [Fact]
    public async Task TestInvalidFileHeaderWithWrongText2Async()
    {
        var testCode = """
            [|/*|] Copyright (c) OtherCorp. All rights reserved.
             * Licensed under the ??? license. See LICENSE file in the project root for full license information.
             */
 
            namespace Bar
            {
            }
            """;
        var fixedCode = """
            // Copyright (c) SomeCorp. All rights reserved.
            // Licensed under the ??? license. See LICENSE file in the project root for full license information.
 
            /* Copyright (c) OtherCorp. All rights reserved.
             * Licensed under the ??? license. See LICENSE file in the project root for full license information.
             */
 
            namespace Bar
            {
            }
            """;
 
        await new VerifyCS.Test
        {
            TestCode = testCode,
            FixedCode = fixedCode,
            EditorConfig = TestSettings,
        }.RunAsync();
    }
 
    [Theory]
    [InlineData("", "")]
    [InlineData(" Header", "")]
    [InlineData(" Header", " Header")]
    public async Task TestValidFileHeaderInRegionAsync(string startLabel, string endLabel)
    {
        var testCode = $@"#region{startLabel}
// Copyright (c) SomeCorp. All rights reserved.
// Licensed under the ??? license. See LICENSE file in the project root for full license information.
#endregion{endLabel}
 
namespace Bar
{{
}}
";
 
        await new VerifyCS.Test
        {
            TestCode = testCode,
            EditorConfig = TestSettings,
        }.RunAsync();
    }
 
    [Theory]
    [InlineData("", "")]
    [InlineData(" Header", "")]
    [InlineData(" Header", " Header")]
    public async Task TestInvalidFileHeaderWithWrongTextInRegionAsync(string startLabel, string endLabel)
    {
        var testCode = $@"#region{startLabel}
[|//|] Copyright (c) OtherCorp. All rights reserved.
// Licensed under the ??? license. See LICENSE file in the project root for full license information.
#endregion{endLabel}
 
namespace Bar
{{
}}
";
        var fixedCode = $@"// Copyright (c) SomeCorp. All rights reserved.
// Licensed under the ??? license. See LICENSE file in the project root for full license information.
 
#region{startLabel}
// Copyright (c) OtherCorp. All rights reserved.
// Licensed under the ??? license. See LICENSE file in the project root for full license information.
#endregion{endLabel}
 
namespace Bar
{{
}}
";
 
        await new VerifyCS.Test
        {
            TestCode = testCode,
            FixedCode = fixedCode,
            EditorConfig = TestSettings,
        }.RunAsync();
    }
 
    /// <summary>
    /// Verifies that an invalid file header built using single line comments will produce the expected diagnostic message.
    /// </summary>
    /// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
    [Fact]
    public async Task TestInvalidFileHeaderWithWrongTextInUnterminatedMultiLineComment1Async()
    {
        var testCode = """
            {|CS1035:|}[|/*|] Copyright (c) OtherCorp. All rights reserved.
             * Licensed under the ??? license. See LICENSE file in the project root for full license information.
            """;
        var fixedCode = """
            // Copyright (c) SomeCorp. All rights reserved.
            // Licensed under the ??? license. See LICENSE file in the project root for full license information.
 
            {|CS1035:|}/* Copyright (c) OtherCorp. All rights reserved.
             * Licensed under the ??? license. See LICENSE file in the project root for full license information.
            """;
 
        await new VerifyCS.Test
        {
            TestCode = testCode,
            FixedCode = fixedCode,
            EditorConfig = TestSettings,
        }.RunAsync();
    }
 
    /// <summary>
    /// Verifies that an invalid file header built using single line comments will produce the expected diagnostic message.
    /// </summary>
    /// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
    [Fact]
    public async Task TestInvalidFileHeaderWithWrongTextInUnterminatedMultiLineComment2Async()
    {
        var testCode = """
            {|CS1035:|}[|/*|]/
            """;
        var fixedCode = """
            // Copyright (c) SomeCorp. All rights reserved.
            // Licensed under the ??? license. See LICENSE file in the project root for full license information.
 
            {|CS1035:|}/*/
            """;
 
        await new VerifyCS.Test
        {
            TestCode = testCode,
            FixedCode = fixedCode,
            EditorConfig = TestSettings,
        }.RunAsync();
    }
 
    /// <summary>
    /// Verifies that an invalid file header built using single line comments will produce the expected diagnostic message.
    /// </summary>
    /// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
    [Theory]
    [InlineData("")]
    [InlineData("    ")]
    public async Task TestInvalidFileHeaderWithWrongTextAfterBlankLineAsync(string firstLine)
    {
        var testCode = $@"{firstLine}
[|//|] Copyright (c) OtherCorp. All rights reserved.
// Licensed under the ??? license. See LICENSE file in the project root for full license information.
 
namespace Bar
{{
}}";
        var fixedCode = """
            // Copyright (c) SomeCorp. All rights reserved.
            // Licensed under the ??? license. See LICENSE file in the project root for full license information.
 
            namespace Bar
            {
            }
            """;
 
        await new VerifyCS.Test
        {
            TestCode = testCode,
            FixedCode = fixedCode,
            EditorConfig = TestSettings,
        }.RunAsync();
    }
 
    /// <summary>
    /// Verifies that an invalid file header built using single line comments will produce the expected diagnostic message.
    /// </summary>
    /// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
    [Fact]
    public async Task TestInvalidFileHeaderWithWrongTextFollowedByCommentAsync()
    {
        var testCode = """
            [|//|] Copyright (c) OtherCorp. All rights reserved.
            // Licensed under the ??? license. See LICENSE file in the project root for full license information.
 
            //using System;
 
            namespace Bar
            {
            }
            """;
        var fixedCode = """
            // Copyright (c) SomeCorp. All rights reserved.
            // Licensed under the ??? license. See LICENSE file in the project root for full license information.
 
            //using System;
 
            namespace Bar
            {
            }
            """;
 
        await new VerifyCS.Test
        {
            TestCode = testCode,
            FixedCode = fixedCode,
            EditorConfig = TestSettings,
        }.RunAsync();
    }
 
    [Fact]
    public async Task TestHeaderMissingRequiredNewLinesAsync()
    {
        var testCode = """
            [|//|] Copyright (c) SomeCorp. All rights reserved.
            // Licensed under the ??? license. See LICENSE file in the project root for full license information.
 
            namespace Bar
            {
            }
            """;
        var fixedCode = """
            //
            // Copyright (c) SomeCorp. All rights reserved.
            //
            // Licensed under the ??? license. See LICENSE file in the project root for full license information.
            //
 
            namespace Bar
            {
            }
            """;
 
        await new VerifyCS.Test
        {
            TestCode = testCode,
            FixedCode = fixedCode,
            EditorConfig = TestSettingsWithEmptyLines,
        }.RunAsync();
    }
}