File: AddMissingImports\CSharpAddMissingImportsRefactoringProviderTests.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.
 
#nullable disable
 
using System.Linq;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.CSharp.CodeRefactorings.AddMissingImports;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeRefactorings;
using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions;
using Microsoft.CodeAnalysis.PasteTracking;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.AddMissingImports;
 
[UseExportProvider]
[Trait(Traits.Feature, Traits.Features.AddMissingImports)]
public class CSharpAddMissingImportsRefactoringProviderTests : AbstractCSharpCodeActionTest
{
    protected override CodeRefactoringProvider CreateCodeRefactoringProvider(EditorTestWorkspace workspace, TestParameters parameters)
        => new CSharpAddMissingImportsRefactoringProvider();
 
    protected override void InitializeWorkspace(EditorTestWorkspace workspace, TestParameters parameters)
    {
        // Treat the span being tested as the pasted span
        var hostDocument = workspace.Documents.First();
        var pastedTextSpan = hostDocument.SelectedSpans.FirstOrDefault();
 
        if (!pastedTextSpan.IsEmpty)
        {
            var pasteTrackingService = workspace.ExportProvider.GetExportedValue<PasteTrackingService>();
 
            // This tests the paste tracking service's resiliancy to failing when multiple pasted spans are
            // registered consecutively and that the last registered span wins.
            pasteTrackingService.RegisterPastedTextSpan(hostDocument.GetTextBuffer(), default);
            pasteTrackingService.RegisterPastedTextSpan(hostDocument.GetTextBuffer(), pastedTextSpan);
        }
    }
 
    private Task TestInRegularAndScriptAsync(
        string initialMarkup, string expectedMarkup,
        bool placeSystemNamespaceFirst, bool separateImportDirectiveGroups)
    {
        var options =
            new OptionsCollection(GetLanguage())
            {
                { GenerationOptions.PlaceSystemNamespaceFirst, placeSystemNamespaceFirst },
                { GenerationOptions.SeparateImportDirectiveGroups, separateImportDirectiveGroups },
            };
        return TestInRegularAndScriptAsync(initialMarkup, expectedMarkup, options: options);
    }
 
    [WpfFact]
    public async Task AddMissingImports_AddImport_PasteContainsSingleMissingImport()
    {
        var code = """
            class C
            {
                public [|D|] Foo { get; }
            }
 
            namespace A
            {
                public class D { }
            }
            """;
 
        var expected = """
            using A;
 
            class C
            {
                public D Foo { get; }
            }
 
            namespace A
            {
                public class D { }
            }
            """;
 
        await TestInRegularAndScriptAsync(code, expected);
    }
 
    [WpfFact]
    public async Task AddMissingImports_AddImportsBelowSystem_PlaceSystemFirstPasteContainsMultipleMissingImports()
    {
        var code = """
            using System;
 
            class C
            {
                [|public D Foo { get; }
                public E Bar { get; }|]
            }
 
            namespace A
            {
                public class D { }
            }
 
            namespace B
            {
                public class E { }
            }
            """;
 
        var expected = """
            using System;
            using A;
            using B;
 
            class C
            {
                public D Foo { get; }
                public E Bar { get; }
            }
 
            namespace A
            {
                public class D { }
            }
 
            namespace B
            {
                public class E { }
            }
            """;
 
        await TestInRegularAndScriptAsync(code, expected, placeSystemNamespaceFirst: true, separateImportDirectiveGroups: false);
    }
 
    [WpfFact]
    public async Task AddMissingImports_AddImportsAboveSystem_DoNotPlaceSystemFirstPasteContainsMultipleMissingImports()
    {
        var code = """
            using System;
 
            class C
            {
                [|public D Foo { get; }
                public E Bar { get; }|]
            }
 
            namespace A
            {
                public class D { }
            }
 
            namespace B
            {
                public class E { }
            }
            """;
 
        var expected = """
            using A;
            using B;
            using System;
 
            class C
            {
                public D Foo { get; }
                public E Bar { get; }
            }
 
            namespace A
            {
                public class D { }
            }
 
            namespace B
            {
                public class E { }
            }
            """;
 
        await TestInRegularAndScriptAsync(code, expected, placeSystemNamespaceFirst: false, separateImportDirectiveGroups: false);
    }
 
    [WpfFact, WorkItem("https://github.com/dotnet/roslyn/pull/42221")]
    public async Task AddMissingImports_AddImportsUngrouped_SeparateImportGroupsPasteContainsMultipleMissingImports()
    {
        var code = """
            using System;
 
            class C
            {
                [|public D Foo { get; }
                public E Bar { get; }|]
            }
 
            namespace A
            {
                public class D { }
            }
 
            namespace B
            {
                public class E { }
            }
            """;
 
        var expected = """
            using A;
            using B;
 
            using System;
 
            class C
            {
                public D Foo { get; }
                public E Bar { get; }
            }
 
            namespace A
            {
                public class D { }
            }
 
            namespace B
            {
                public class E { }
            }
            """;
 
        await TestInRegularAndScriptAsync(code, expected, placeSystemNamespaceFirst: false, separateImportDirectiveGroups: true);
    }
 
    [WpfFact]
    public async Task AddMissingImports_PartialFix_PasteContainsFixableAndAmbiguousMissingImports()
    {
        var code = """
            class C
            {
                [|public D Foo { get; }
                public E Bar { get; }|]
            }
 
            namespace A
            {
                public class D { }
            }
 
            namespace B
            {
                public class D { }
                public class E { }
            }
            """;
 
        var expected = """
            using B;
 
            class C
            {
                public D Foo { get; }
                public E Bar { get; }
            }
 
            namespace A
            {
                public class D { }
            }
 
            namespace B
            {
                public class D { }
                public class E { }
            }
            """;
 
        await TestInRegularAndScriptAsync(code, expected);
    }
 
    [WpfFact]
    public async Task AddMissingImports_NoAction_NoPastedSpan()
    {
        var code = """
            class C
            {
                public D[||] Foo { get; }
            }
 
            namespace A
            {
                public class D { }
            }
            """;
 
        await TestMissingInRegularAndScriptAsync(code);
    }
 
    [WpfFact]
    public async Task AddMissingImports_NoAction_PasteIsNotMissingImports()
    {
        var code = """
            class [|C|]
            {
                public D Foo { get; }
            }
 
            namespace A
            {
                public class D { }
            }
            """;
 
        await TestMissingInRegularAndScriptAsync(code);
    }
 
    [WpfFact]
    public async Task AddMissingImports_NoAction_PasteContainsAmibiguousMissingImport()
    {
        var code = """
            class C
            {
                public [|D|] Foo { get; }
            }
 
            namespace A
            {
                public class D { }
            }
 
            namespace B
            {
                public class D { }
            }
            """;
 
        await TestMissingInRegularAndScriptAsync(code);
    }
 
    [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/31768")]
    public async Task AddMissingImports_AddMultipleImports_NoPreviousImports()
    {
        var code = """
            class C
            {
                [|public D Foo { get; }
                public E Bar { get; }|]
            }
 
            namespace A
            {
                public class D { }
            }
 
            namespace B
            {
                public class E { }
            }
            """;
 
        var expected = """
            using A;
            using B;
 
            class C
            {
                public D Foo { get; }
                public E Bar { get; }
            }
 
            namespace A
            {
                public class D { }
            }
 
            namespace B
            {
                public class E { }
            }
            """;
 
        await TestInRegularAndScriptAsync(code, expected, placeSystemNamespaceFirst: false, separateImportDirectiveGroups: false);
    }
}