File: Interactive\Commands\InteractiveCommandHandlerTests.cs
Web Access
Project: src\src\VisualStudio\CSharp\Test\Microsoft.VisualStudio.LanguageServices.CSharp.UnitTests.csproj (Microsoft.VisualStudio.LanguageServices.CSharp.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.Collections.Generic;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.VisualStudio.Commanding;
using Roslyn.Test.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Interactive.Commands;
 
[UseExportProvider]
[Trait(Traits.Feature, Traits.Features.Interactive)]
public sealed class InteractiveCommandHandlerTests
{
    private const string Caret = "$$";
    private const string ExampleCode1 = @"var x = 1;";
    private const string ExampleCode2 =
        """
        var x = 1;
        Task.Run(() => { return 1; });
        """;
    private const string ExampleCode2Line2 =
@"Task.Run(() => { return 1; });";
    private const string ExampleMultiline =
        """
        namespace N {
            void goo() {
                Console.WriteLine(
                    $$"LLL");
            }
        }
        """;
    private const string ExpectedMultilineSelection =
        """
        Console.WriteLine(
                    "LLL");
        """;
 
    [WpfFact]
    public void TestExecuteInInteractiveWithoutSelection()
    {
        AssertExecuteInInteractive(Caret, []);
 
        AssertExecuteInInteractive(
            """
            var x = 1;
            $$
            var y = 2;
            """, []);
 
        AssertExecuteInInteractive(ExampleCode1 + Caret, ExampleCode1);
        AssertExecuteInInteractive(ExampleCode1.Insert(3, Caret), ExampleCode1);
        AssertExecuteInInteractive(ExampleCode2 + Caret, ExampleCode2Line2);
        AssertExecuteInInteractive(ExampleMultiline, ExpectedMultilineSelection);
    }
 
    [WpfFact]
    public void TestExecuteInInteractiveWithEmptyBuffer()
    {
        AssertExecuteInInteractive(
            """
            {|Selection:|}var x = 1;
            {|Selection:$$|}var y = 2;
            """, []);
 
        AssertExecuteInInteractive($@"{{|Selection:{ExampleCode1}$$|}}", ExampleCode1);
 
        AssertExecuteInInteractive($@"{{|Selection:{ExampleCode2}$$|}}", ExampleCode2);
 
        AssertExecuteInInteractive(
            $$"""
            var o = new object[] { 1, 2, 3 };
            Console.WriteLine(o);
            {|Selection:{{ExampleCode2}}$$|}
 
            Console.WriteLine(x);
            """, ExampleCode2);
    }
 
    [WpfFact]
    public void TestExecuteInInteractiveWithBoxSelection()
    {
        var expectedBoxSubmissionResult = """
            int x;
            int y;
            """;
        AssertExecuteInInteractive(
            $$"""
            some text {|Selection:$$int x;|} also here
            text some {|Selection:int y;|} here also
            """, expectedBoxSubmissionResult);
 
        AssertExecuteInInteractive(
            $$"""
            some text {|Selection:int x;$$|} also here
            text some {|Selection:int y;|} here also
            """, expectedBoxSubmissionResult);
 
        AssertExecuteInInteractive(
            $$"""
            some text {|Selection:int x;|} also here
            text some {|Selection:$$int y;|} here also
            """, expectedBoxSubmissionResult);
 
        AssertExecuteInInteractive(
            $$"""
            some text {|Selection:int x;|} also here
            text some {|Selection:int y;$$|} here also
            """, expectedBoxSubmissionResult);
    }
 
    [WpfFact]
    public void TestExecuteInInteractiveWithNonEmptyBuffer()
    {
        // Execute in interactive clears the existing current buffer before execution.
        // Therefore `var x = 1;` will not be executed.
        AssertExecuteInInteractive(
            @"{|Selection:var y = 2;$$|}",
            "var y = 2;",
            submissionBuffer: "var x = 1;");
    }
 
    [WpfFact(Skip = "https://github.com/dotnet/roslyn/issues/23200")]
    public void TestExecuteInInteractiveWithDefines()
    {
        var exampleWithIfDirective =
            """
            #if DEF
            public void $$Run()
            {
            }
            #endif
            """;
 
        AssertExecuteInInteractive(exampleWithIfDirective,
@"public void Run()");
        AssertExecuteInInteractive($"""
            #define DEF
            {exampleWithIfDirective}
            """,
            """
            public void Run()
            {
            }
            """);
    }
 
    [WpfFact]
    public void TestCopyToInteractiveWithoutSelection()
    {
        AssertCopyToInteractive(Caret, "");
 
        AssertCopyToInteractive($"{ExampleCode2}$$", ExampleCode2Line2);
 
        AssertCopyToInteractive(
            code: ExampleCode2 + Caret,
            submissionBuffer: ExampleCode1,
            expectedBufferText: ExampleCode1 + "\r\n" + ExampleCode2Line2);
 
        AssertCopyToInteractive(
            code: ExampleCode2 + Caret,
            submissionBuffer: "x = 2;",
            expectedBufferText: "x = 2;\r\n" + ExampleCode2Line2);
    }
 
    [WpfFact]
    public void TestCopyToInteractive()
    {
        AssertCopyToInteractive($"{{|Selection:{ExampleCode2}$$|}}", ExampleCode2);
    }
 
    [WpfFact]
    public void TestCopyToInteractiveWithNonEmptyBuffer()
    {
        // Copy to interactive does not clear the existing buffer.
        // Therefore `var x = 1;` will still be present in the final buffer.
        AssertCopyToInteractive(
            $"{{|Selection:{ExampleCode2}$$|}}",
            $"var x = 1;\r\n{ExampleCode2}",
            submissionBuffer: "var x = 1;");
    }
 
    private static void AssertCopyToInteractive(string code, string expectedBufferText, string submissionBuffer = null)
    {
        using var workspace = InteractiveWindowCommandHandlerTestState.CreateTestState(code);
        PrepareSubmissionBuffer(submissionBuffer, workspace);
        workspace.SendCopyToInteractive();
        Assert.Equal(expectedBufferText, workspace.WindowCurrentLanguageBuffer.CurrentSnapshot.GetText());
    }
 
    private static void AssertExecuteInInteractive(string code, string expectedSubmission, string submissionBuffer = null)
    {
        AssertExecuteInInteractive(code, [expectedSubmission], submissionBuffer);
    }
 
    private static void AssertExecuteInInteractive(string code, string[] expectedSubmissions, string submissionBuffer = null)
    {
        var submissions = new List<string>();
        void appendSubmission(object _, string item)
        {
            submissions.Add(item.TrimEnd());
        }
 
        using var workspace = InteractiveWindowCommandHandlerTestState.CreateTestState(code);
        PrepareSubmissionBuffer(submissionBuffer, workspace);
        Assert.Equal(CommandState.Available, workspace.GetStateForExecuteInInteractive());
 
        workspace.Evaluator.OnExecute += appendSubmission;
        workspace.ExecuteInInteractive();
        AssertEx.Equal(expectedSubmissions, submissions);
    }
 
    private static void PrepareSubmissionBuffer(string submissionBuffer, InteractiveWindowCommandHandlerTestState workspace)
    {
        if (!string.IsNullOrEmpty(submissionBuffer))
        {
            workspace.WindowCurrentLanguageBuffer.Insert(0, submissionBuffer);
        }
    }
}