File: Formatting\CSharpNewDocumentFormattingServiceTests.cs
Web Access
Project: src\src\EditorFeatures\CSharpTest\Microsoft.CodeAnalysis.CSharp.EditorFeatures.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.EditorFeatures.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.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.AddImport;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.CodeStyle;
using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities.Formatting;
using Roslyn.Test.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Formatting;
 
public class CSharpNewDocumentFormattingServiceTests : AbstractNewDocumentFormattingServiceTests
{
    public static IEnumerable<object[]> EndOfDocumentSequences
    {
        get
        {
            yield return new object[] { "" };
            yield return new object[] { """
 
                """ };
        }
    }
 
    protected override string Language => LanguageNames.CSharp;
    protected override EditorTestWorkspace CreateTestWorkspace(string testCode, ParseOptions? parseOptions)
        => EditorTestWorkspace.CreateCSharp(testCode, parseOptions);
 
    [Fact]
    public async Task TestFileScopedNamespaces()
    {
        await TestAsync(testCode: """
            namespace Goo
            {
                internal class C
                {
                }
            }
            """,
        expected: """
        namespace Goo;
 
        internal class C
        {
        }
        """,
        options: new OptionsCollection(LanguageNames.CSharp)
        {
            { CSharpCodeStyleOptions.NamespaceDeclarations, new CodeStyleOption2<NamespaceDeclarationPreference>(NamespaceDeclarationPreference.FileScoped, NotificationOption2.Error) }
        },
        parseOptions: new CSharpParseOptions(LanguageVersion.CSharp10));
    }
 
    [Fact]
    public async Task TestFileScopedNamespaces_Invalid_MultipleNamespaces()
    {
        var testCode = """
            namespace Goo
            {
            }
 
            namespace Bar
            {
            }
            """;
 
        await TestAsync(
            testCode: testCode,
            expected: testCode,
            options: new OptionsCollection(LanguageNames.CSharp)
            {
                { CSharpCodeStyleOptions.NamespaceDeclarations, new CodeStyleOption2<NamespaceDeclarationPreference>(NamespaceDeclarationPreference.FileScoped, NotificationOption2.Error) }
            },
            parseOptions: new CSharpParseOptions(LanguageVersion.CSharp10));
    }
 
    [Fact]
    public async Task TestFileScopedNamespaces_Invalid_WrongLanguageVersion()
    {
        var testCode = """
            namespace Goo
            {
                internal class C
                {
                }
            }
            """;
 
        await TestAsync(
            testCode: testCode,
            expected: testCode,
            options: new OptionsCollection(LanguageNames.CSharp)
            {
                { CSharpCodeStyleOptions.NamespaceDeclarations, new CodeStyleOption2<NamespaceDeclarationPreference>(NamespaceDeclarationPreference.FileScoped, NotificationOption2.Error) }
            },
            parseOptions: new CSharpParseOptions(LanguageVersion.CSharp9));
    }
 
    [Theory]
    [MemberData(nameof(EndOfDocumentSequences))]
    public async Task TestBlockScopedNamespaces(string endOfDocumentSequence)
    {
        await TestAsync(testCode: $@"
namespace Goo;
 
internal class C
{{
}}{endOfDocumentSequence}",
        expected: $@"
namespace Goo
{{
    internal class C
    {{
    }}
}}{endOfDocumentSequence}",
        options: new OptionsCollection(LanguageNames.CSharp)
        {
            { CSharpCodeStyleOptions.NamespaceDeclarations, new CodeStyleOption2<NamespaceDeclarationPreference>(NamespaceDeclarationPreference.BlockScoped, NotificationOption2.Error) }
        });
    }
 
    [Fact]
    public async Task TestOrganizeUsingsWithNoUsings()
    {
        var testCode = """
            namespace Goo
            {
            }
            """;
        await TestAsync(
            testCode: testCode,
            expected: testCode,
            options: new OptionsCollection(LanguageNames.CSharp)
            {
                { CSharpCodeStyleOptions.PreferredUsingDirectivePlacement, new CodeStyleOption2<AddImportPlacement>(AddImportPlacement.OutsideNamespace, NotificationOption2.Error) }
            });
    }
 
    [Fact]
    public async Task TestFileBanners()
    {
        await TestAsync(testCode: """
            using System;
 
            namespace Goo
            {
            }
            """,
        expected: """
        // This is a banner.
 
        using System;
 
        namespace Goo
        {
        }
        """,
        options: new OptionsCollection(LanguageNames.CSharp)
        {
            { CodeStyleOptions2.FileHeaderTemplate, "This is a banner." }
        });
    }
 
    [Fact]
    public async Task TestAccessibilityModifiers()
    {
        await TestAsync(testCode: """
            using System;
 
            namespace Goo
            {
                class C
                {
                }
            }
            """,
        expected: """
        using System;
 
        namespace Goo
        {
            internal class C
            {
            }
        }
        """,
        options: new OptionsCollection(LanguageNames.CSharp)
        {
            { CodeStyleOptions2.AccessibilityModifiersRequired, new CodeStyleOption2<AccessibilityModifiersRequired>(AccessibilityModifiersRequired.Always, NotificationOption2.Error) }
        });
    }
 
    [Fact]
    public async Task TestAccessibilityModifiers_FileScopedNamespace()
    {
        await TestAsync(testCode: """
            using System;
 
            namespace Goo
            {
                class C
                {
                }
            }
            """,
        expected: """
        using System;
 
        namespace Goo;
        internal class C
        {
        }
        """,
        options: new OptionsCollection(LanguageNames.CSharp)
        {
            { CSharpCodeStyleOptions.NamespaceDeclarations, new CodeStyleOption2<NamespaceDeclarationPreference>(NamespaceDeclarationPreference.FileScoped, NotificationOption2.Error) },
            { CodeStyleOptions2.AccessibilityModifiersRequired, new CodeStyleOption2<AccessibilityModifiersRequired>(AccessibilityModifiersRequired.Always, NotificationOption2.Error) }
        });
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/55703")]
    public async Task TestAccessibilityModifiers_IgnoresPartial()
    {
        await TestAsync(
            testCode: """
            using System;
 
            namespace Goo
            {
                class E
                {
                }
 
                partial class C
                {
                }
 
                class D
                {
                }
            }
            """,
            expected: """
            using System;
 
            namespace Goo
            {
                internal class E
                {
                }
 
                partial class C
                {
                }
 
                internal class D
                {
                }
            }
            """,
            options: new OptionsCollection(LanguageNames.CSharp)
            {
                { CodeStyleOptions2.AccessibilityModifiersRequired, new CodeStyleOption2<AccessibilityModifiersRequired>(AccessibilityModifiersRequired.Always, NotificationOption2.Error) }
            });
    }
 
    [Fact]
    public async Task TestUsingDirectivePlacement()
    {
        await TestAsync(testCode: """
            using System;
 
            namespace Goo
            {
            }
            """,
        expected: """
        namespace Goo
        {
            using System;
        }
        """,
        options: new OptionsCollection(LanguageNames.CSharp)
        {
            { CSharpCodeStyleOptions.PreferredUsingDirectivePlacement, new CodeStyleOption2<AddImportPlacement>(AddImportPlacement.InsideNamespace, NotificationOption2.Error) }
        });
    }
 
    [Fact]
    public async Task TestPreferTopLevelStatements()
    {
        await TestAsync(testCode: """
            using System;
 
            // See https://aka.ms/new-console-template for more information
            Console.WriteLine("Hello, World!");
            """,
        expected: """
        using System;
 
        // See https://aka.ms/new-console-template for more information
        Console.WriteLine("Hello, World!");
        """,
        options: new OptionsCollection(LanguageNames.CSharp)
        {
            { CSharpCodeStyleOptions.PreferTopLevelStatements, new CodeStyleOption2<bool>(value: true, notification: NotificationOption2.Suggestion) }
        });
    }
 
    [Fact]
    public async Task TestPreferProgramMain()
    {
        await TestAsync(testCode: """
            using System;
 
            // See https://aka.ms/new-console-template for more information
            Console.WriteLine("Hello, World!");
            """,
        expected: """
        using System;
 
        internal class Program
        {
            private static void Main(string[] args)
            {
                Console.WriteLine("Hello, World!");
            }
        }
        """,
        options: new OptionsCollection(LanguageNames.CSharp)
        {
            { CSharpCodeStyleOptions.PreferTopLevelStatements, new CodeStyleOption2<bool>(value: false, notification: NotificationOption2.Suggestion) }
        });
    }
}