File: Rename\WillRenameTests.cs
Web Access
Project: src\src\LanguageServer\ProtocolUnitTests\Microsoft.CodeAnalysis.LanguageServer.Protocol.UnitTests.csproj (Microsoft.CodeAnalysis.LanguageServer.Protocol.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;
using System.Collections.Generic;
using System.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.LanguageServer.Handler;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.LanguageServer.Protocol;
using Roslyn.Test.Utilities;
using Xunit;
using Xunit.Abstractions;
using LSP = Roslyn.LanguageServer.Protocol;
 
namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.Rename;
 
public sealed class WillRenameTests(ITestOutputHelper testOutputHelper) : AbstractLanguageServerProtocolTests(testOutputHelper)
{
    protected override TestComposition Composition => base.Composition
        .AddParts(typeof(TestWillRenameListener1))
        .AddParts(typeof(TestWillRenameListener2));
 
    [Theory, CombinatorialData]
    public async Task SetsCapabilities(bool mutatingLspWorkspace)
    {
        var clientCapabilities = new ClientCapabilities
        {
            Workspace = new WorkspaceClientCapabilities
            {
                FileOperations = new FileOperationsWorkspaceClientCapabilities
                {
                    WillRename = true
                },
            },
        };
 
        await using var testLspServer = await CreateTestLspServerAsync("", mutatingLspWorkspace, clientCapabilities);
 
        var capabilities = testLspServer.GetServerCapabilities();
 
        Assert.Collection(capabilities.Workspace.FileOperations.WillRename.Filters,
            filter => Assert.Equal("*.cs", filter.Pattern.Glob),
            filter => Assert.Equal("*.vb", filter.Pattern.Glob));
    }
 
    [Theory, CombinatorialData]
    public async Task CallsListener(bool mutatingLspWorkspace)
    {
        await using var testLspServer = await CreateTestLspServerAsync("", mutatingLspWorkspace);
 
        var listeners = ((IMefHostExportProvider)testLspServer.TestWorkspace.Services.HostServices).GetExportedValues<ILspWillRenameListener>().OfType<TestWillRenameListener1>().ToArray();
 
        // Need at least one change, or the result will be dropped
        listeners[0].Result = new WorkspaceEdit() { DocumentChanges = new TextDocumentEdit[] { new() { } } };
 
        var edit = await RunWillRenameAsync(testLspServer);
        Assert.NotNull(edit);
    }
 
    [Theory, CombinatorialData]
    public async Task CombinesDocumentChanges(bool mutatingLspWorkspace)
    {
        await using var testLspServer = await CreateTestLspServerAsync("", mutatingLspWorkspace);
 
        var listeners = ((IMefHostExportProvider)testLspServer.TestWorkspace.Services.HostServices).GetExportedValues<ILspWillRenameListener>().OfType<TestWillRenameListener1>().ToArray();
 
        var expected = new WorkspaceEdit()
        {
            DocumentChanges = new TextDocumentEdit[] {
                new() { TextDocument = new() { DocumentUri = new("file://file1.cs") } },
                new() { TextDocument = new() { DocumentUri = new("file://file2.cs") } }
            }
        };
 
        listeners[0].Result = new WorkspaceEdit() { DocumentChanges = new TextDocumentEdit[] { new() { TextDocument = new() { DocumentUri = new("file://file1.cs") } } } };
        listeners[1].Result = new WorkspaceEdit() { DocumentChanges = new TextDocumentEdit[] { new() { TextDocument = new() { DocumentUri = new("file://file2.cs") } } } };
 
        var edit = await RunWillRenameAsync(testLspServer);
        Assert.NotNull(edit);
 
        AssertJsonEquals(expected, edit);
    }
 
    [Theory, CombinatorialData]
    public async Task CombinesDocumentChanges_SumType(bool mutatingLspWorkspace)
    {
        await using var testLspServer = await CreateTestLspServerAsync("", mutatingLspWorkspace);
 
        var listeners = ((IMefHostExportProvider)testLspServer.TestWorkspace.Services.HostServices).GetExportedValues<ILspWillRenameListener>().OfType<TestWillRenameListener1>().ToArray();
 
        var expected = new WorkspaceEdit()
        {
            DocumentChanges = new SumType<TextDocumentEdit, CreateFile, RenameFile, DeleteFile>[] {
                new TextDocumentEdit() { TextDocument = new() { DocumentUri = new("file://file1.cs") } },
                new RenameFile() { OldDocumentUri = new("file://file2.cs") }
            }
        };
 
        listeners[0].Result = new WorkspaceEdit() { DocumentChanges = new TextDocumentEdit[] { new() { TextDocument = new() { DocumentUri = new("file://file1.cs") } } } };
        listeners[1].Result = new WorkspaceEdit() { DocumentChanges = new SumType<TextDocumentEdit, CreateFile, RenameFile, DeleteFile>[] { new RenameFile() { OldDocumentUri = new("file://file2.cs") } } };
 
        var edit = await RunWillRenameAsync(testLspServer);
        Assert.NotNull(edit);
 
        AssertJsonEquals(expected, edit);
    }
 
    [Theory, CombinatorialData]
    public async Task CombinesDocumentChanges_Changes(bool mutatingLspWorkspace)
    {
        await using var testLspServer = await CreateTestLspServerAsync("", mutatingLspWorkspace);
 
        var listeners = ((IMefHostExportProvider)testLspServer.TestWorkspace.Services.HostServices).GetExportedValues<ILspWillRenameListener>().OfType<TestWillRenameListener1>().ToArray();
 
        var expected = new WorkspaceEdit()
        {
            Changes = new Dictionary<string, TextEdit[]>
            {
                { "file://file1.cs", []},
                { "file://file2.cs", [] }
            }
        };
 
        listeners[0].Result = new WorkspaceEdit() { Changes = new Dictionary<string, TextEdit[]> { { "file://file1.cs", [] } } };
        listeners[1].Result = new WorkspaceEdit() { Changes = new Dictionary<string, TextEdit[]> { { "file://file2.cs", [] } } };
 
        var edit = await RunWillRenameAsync(testLspServer);
        Assert.NotNull(edit);
 
        AssertJsonEquals(expected, edit);
    }
 
    private static async Task<WorkspaceEdit> RunWillRenameAsync(TestLspServer testLspServer)
    {
        var renameParams = new RenameFilesParams();
        return await testLspServer.ExecuteRequestAsync<LSP.RenameFilesParams, LSP.WorkspaceEdit>(LSP.Methods.WorkspaceWillRenameFilesName, renameParams, CancellationToken.None);
    }
 
    [ExportLspWillRenameListener("*.cs")]
    [Shared]
    [PartNotDiscoverable]
    [method: ImportingConstructor]
    [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
    private class TestWillRenameListener1() : ILspWillRenameListener
    {
        public WorkspaceEdit Result { get; set; }
 
        public Task<WorkspaceEdit> HandleWillRenameAsync(RenameFilesParams renameParams, RequestContext context, CancellationToken cancellationToken)
        {
            return Task.FromResult(Result);
        }
    }
 
    [ExportLspWillRenameListener("*.vb")]
    [Shared]
    [PartNotDiscoverable]
    [method: ImportingConstructor]
    [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
    private class TestWillRenameListener2() : TestWillRenameListener1
    {
    }
}