File: ValidateBreakableRange\ValidateBreakableRangeTests.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.
 
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Roslyn.Test.Utilities;
using Xunit;
using Xunit.Abstractions;
using LSP = Roslyn.LanguageServer.Protocol;
 
namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.ValidateBreakableRange;
 
public sealed class ValidateBreakableRange(ITestOutputHelper testOutputHelper) : AbstractLanguageServerProtocolTests(testOutputHelper)
{
    [Theory, CombinatorialData]
    public async Task SimpleStatement(bool mutatingLspWorkspace)
    {
        var markup =
            """
            class A
            {
                void M()
                {
                    {|expected:{|caret:|}M();|}
                }
            }
            """;
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace);
 
        var caret = testLspServer.GetLocations("caret").Single();
 
        var expected = testLspServer.GetLocations("expected").Single().Range;
 
        var result = await RunAsync(testLspServer, caret);
        AssertJsonEquals(expected, result);
    }
 
    [Theory, CombinatorialData]
    public async Task LineBreakpoint(bool mutatingLspWorkspace)
    {
        var markup =
            """
            class A
            {
                void M()
                {
            #if FALSE
                    {|caret:|}M();
            #endif
                }
            }
            """;
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace);
 
        var caret = testLspServer.GetLocations("caret").Single();
 
        var expected = caret.Range;
 
        var result = await RunAsync(testLspServer, caret);
        AssertJsonEquals(expected, result);
    }
 
    [Theory, CombinatorialData]
    public async Task NoBreakpointSpan(bool mutatingLspWorkspace)
    {
        var markup =
            """
            class A
            {
                void M()
                {
                    {|caret:|}const int a = 1;
                }
            }
            """;
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace);
 
        var caret = testLspServer.GetLocations("caret").Single();
 
        var result = await RunAsync(testLspServer, caret);
        Assert.Null(result);
    }
 
    [Theory, CombinatorialData]
    public async Task SplitBreakpoint(bool mutatingLspWorkspace)
    {
        var markup =
            """
            class A
            {
                void M()
                {
                    {|breakpoint:int a = 1; {|expected:Console.WriteLine("hello");|}|}
                }
            }
            """;
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace);
 
        var breakpoint = testLspServer.GetLocations("breakpoint").Single();
 
        var expected = testLspServer.GetLocations("expected").Single().Range;
 
        var result = await RunAsync(testLspServer, breakpoint);
        AssertJsonEquals(expected, result);
    }
 
    [Theory, CombinatorialData, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1501785")]
    public async Task InvalidExistingBreakpoint1(bool mutatingLspWorkspace)
    {
        var markup =
            """
            class A
            {
                void M()
                {
                    {|breakpoint:int a Console.WriteLine("hello");|}
                }
            }
            """;
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace);
 
        var breakpoint = testLspServer.GetLocations("breakpoint").Single();
 
        var expected = breakpoint.Range;
 
        var result = await RunAsync(testLspServer, breakpoint);
        AssertJsonEquals(expected, result);
    }
 
    [Theory, CombinatorialData, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1501882")]
    public async Task InvalidExistingBreakpoint2(bool mutatingLspWorkspace)
    {
        var markup =
            """
            class A
            {
                void M()
                {
                    int b=
                 {|breakpoint:int a = 1;|}
                }
            }
            """;
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace);
 
        var breakpoint = testLspServer.GetLocations("breakpoint").Single();
 
        var expected = breakpoint.Range;
 
        var result = await RunAsync(testLspServer, breakpoint);
        AssertJsonEquals(expected, result);
    }
 
    [Theory, CombinatorialData]
    public async Task TypingInMultilineBreakpoint(bool mutatingLspWorkspace)
    {
        // This simulates the request we get just after the user types the semi-colon on the first line
        var markup =
            """
            class A
            {
                void M()
                {
                    {|breakpoint:int a = 1;
                    {|expected:Console.WriteLine("hello");|}|}
                }
            }
            """;
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace);
 
        var breakpoint = testLspServer.GetLocations("breakpoint").Single();
 
        var expected = testLspServer.GetLocations("expected").Single().Range;
 
        var result = await RunAsync(testLspServer, breakpoint);
        AssertJsonEquals(expected, result);
    }
 
    [Theory, CombinatorialData]
    public async Task TypingInMultilineBreakpoint2(bool mutatingLspWorkspace)
    {
        // This simulates the request we get just after the user types the semi-colon on the third line
        var markup =
            """
            class A
            {
                void M()
                {
                    {|breakpoint:int a 
                            = 
                            1;
                    {|expected:Console.WriteLine(
                            "hello"
                        );|}|}
                }
            }
            """;
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace);
 
        var breakpoint = testLspServer.GetLocations("breakpoint").Single();
 
        var expected = testLspServer.GetLocations("expected").Single().Range;
 
        var result = await RunAsync(testLspServer, breakpoint);
        AssertJsonEquals(expected, result);
    }
 
    [Theory, CombinatorialData]
    public async Task ExpandToMultilineBreakpoint(bool mutatingLspWorkspace)
    {
        // This simulates the request we get just after the user types the equals sign on the first line
        var markup =
            """
            class A
            {
                void M()
                {
                    {|expected:int a =
                    {|breakpoint:GetSomeValue();|}|}
                }
            }
            """;
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace);
 
        var breakpoint = testLspServer.GetLocations("breakpoint").Single();
 
        var expected = testLspServer.GetLocations("expected").Single().Range;
 
        var result = await RunAsync(testLspServer, breakpoint);
        AssertJsonEquals(expected, result);
    }
 
    [Theory, CombinatorialData]
    public async Task DoNotShrinkValidMultilineBreakpoints(bool mutatingLspWorkspace)
    {
        var markup =
            """
            class A
            {
                void M()
                {
                    {|breakpoint:{|expected:int a =
                    GetSomeValue();|}|}
                }
            }
            """;
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace);
 
        var breakpoint = testLspServer.GetLocations("breakpoint").Single();
 
        var expected = testLspServer.GetLocations("expected").Single().Range;
 
        var result = await RunAsync(testLspServer, breakpoint);
        AssertJsonEquals(expected, result);
    }
 
    private static async Task<LSP.Range?> RunAsync(TestLspServer testLspServer, LSP.Location caret)
    {
        return await testLspServer.ExecuteRequestAsync<LSP.VSInternalValidateBreakableRangeParams, LSP.Range?>(
            LSP.VSInternalMethods.TextDocumentValidateBreakableRangeName,
            new LSP.VSInternalValidateBreakableRangeParams()
            {
                TextDocument = new LSP.TextDocumentIdentifier { Uri = caret.Uri },
                Range = caret.Range
            },
            CancellationToken.None);
    }
}