File: CommandTests\Run\FileBasedAppSourceEditorTests.cs
Web Access
Project: ..\..\..\test\dotnet.Tests\dotnet.Tests.csproj (dotnet.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using Microsoft.CodeAnalysis.Text;
using Microsoft.DotNet.Cli.Commands.Run;
 
namespace Microsoft.DotNet.Cli.Run.Tests;
 
public sealed class FileBasedAppSourceEditorTests(ITestOutputHelper log) : SdkTest(log)
{
    private static FileBasedAppSourceEditor CreateEditor(string source)
    {
        return FileBasedAppSourceEditor.Load(new SourceFile("/app/Program.cs", SourceText.From(source, Encoding.UTF8)));
    }
 
    [Theory]
    [InlineData("#:package MyPackage@1.0.1")]
    [InlineData("#:package   MyPackage @ abc")]
    [InlineData("#:package MYPACKAGE")]
    public void ReplaceExisting(string inputLine)
    {
        Verify(
            $"""
            {inputLine}
            Console.WriteLine();
            """,
            (static editor => editor.Add(new CSharpDirective.Package(default) { Name = "MyPackage", Version = "1.0.0" }),
            """
            #:package MyPackage@1.0.0
            Console.WriteLine();
            """));
    }
 
    [Fact]
    public void OnlyStatement()
    {
        Verify(
            """
            Console.WriteLine();
            """,
            (static editor => editor.Add(new CSharpDirective.Package(default) { Name = "MyPackage", Version = "1.0.0" }),
            """
            #:package MyPackage@1.0.0
 
            Console.WriteLine();
            """),
            (static editor => editor.Remove(editor.Directives.Single()),
            """
            Console.WriteLine();
            """));
    }
 
    [Theory]
    [InlineData("// only comment")]
    [InlineData("/* only comment */")]
    public void OnlyComment(string comment)
    {
        Verify(
            comment,
            (static editor => editor.Add(new CSharpDirective.Package(default) { Name = "MyPackage", Version = "1.0.0" }),
            $"""
            {comment}
 
            #:package MyPackage@1.0.0

            """),
            (static editor => editor.Remove(editor.Directives.Single()),
            $"""
            {comment}
 

            """));
    }
 
    [Fact]
    public void Empty()
    {
        Verify(
            "",
            (static editor => editor.Add(new CSharpDirective.Package(default) { Name = "MyPackage", Version = "1.0.0" }),
            """
            #:package MyPackage@1.0.0
 
            """),
            (static editor => editor.Remove(editor.Directives.Single()),
            ""));
    }
 
    [Fact]
    public void PreExistingWhiteSpace()
    {
        Verify(
            """
 
 
            Console.WriteLine();
            """,
            (static editor => editor.Add(new CSharpDirective.Package(default) { Name = "MyPackage", Version = "1.0.0" }),
            """
            #:package MyPackage@1.0.0
 
 
            Console.WriteLine();
            """),
            (static editor => editor.Remove(editor.Directives.Single()),
            """
            Console.WriteLine();
            """));
    }
 
    [Fact]
    public void Comments()
    {
        Verify(
            """
            // Comment1a
            // Comment1b
 
            // Comment2a
            // Comment2b
            Console.WriteLine();
            // Comment3
            """,
            (static editor => editor.Add(new CSharpDirective.Package(default) { Name = "MyPackage", Version = "1.0.0" }),
            """
            // Comment1a
            // Comment1b
 
            // Comment2a
            // Comment2b
 
            #:package MyPackage@1.0.0
 
            Console.WriteLine();
            // Comment3
            """),
            (static editor => editor.Remove(editor.Directives.Single()),
            """
            // Comment1a
            // Comment1b
            
            // Comment2a
            // Comment2b
 
            Console.WriteLine();
            // Comment3
            """));
    }
 
    [Fact]
    public void CommentsWithWhiteSpaceAfter()
    {
        Verify(
            """
            // Comment
 
 
            Console.WriteLine();
            """,
            (static editor => editor.Add(new CSharpDirective.Package(default) { Name = "MyPackage", Version = "1.0.0" }),
            """
            // Comment
 
 
            #:package MyPackage@1.0.0
 
            Console.WriteLine();
            """),
            (static editor => editor.Remove(editor.Directives.Single()),
            """
            // Comment
 
 
            Console.WriteLine();
            """));
    }
 
    [Fact]
    public void Comment_Documentation()
    {
        Verify(
            """
            /// doc comment
            Console.WriteLine();
            """,
            (static editor => editor.Add(new CSharpDirective.Package(default) { Name = "MyPackage", Version = "1.0.0" }),
            """
            /// doc comment
 
            #:package MyPackage@1.0.0
 
            Console.WriteLine();
            """),
            (static editor => editor.Remove(editor.Directives.Single()),
            """
            /// doc comment
 
            Console.WriteLine();
            """));
    }
 
    [Fact]
    public void Comment_MultiLine()
    {
        Verify(
            """
            /* test */
            Console.WriteLine();
            """,
            (static editor => editor.Add(new CSharpDirective.Package(default) { Name = "MyPackage", Version = "1.0.0" }),
            """
            /* test */
 
            #:package MyPackage@1.0.0
 
            Console.WriteLine();
            """),
            (static editor => editor.Remove(editor.Directives.Single()),
            """
            /* test */
 
            Console.WriteLine();
            """));
    }
 
    [Fact]
    public void Comment_MultiLine_NoNewLine()
    {
        Verify(
            """
            /* test */Console.WriteLine();
            """,
            (static editor => editor.Add(new CSharpDirective.Package(default) { Name = "MyPackage", Version = "1.0.0" }),
            """
            #:package MyPackage@1.0.0
 
            /* test */Console.WriteLine();
            """),
            (static editor => editor.Remove(editor.Directives.Single()),
            """
            /* test */Console.WriteLine();
            """));
    }
 
    [Fact]
    public void Comment_MultiLine_NoNewLine_Multiple()
    {
        Verify(
            """
            // test
            /* test */Console.WriteLine();
            """,
            (static editor => editor.Add(new CSharpDirective.Package(default) { Name = "MyPackage", Version = "1.0.0" }),
            """
            // test
 
            #:package MyPackage@1.0.0
 
            /* test */Console.WriteLine();
            """),
            (static editor => editor.Remove(editor.Directives.Single()),
            """
            // test
 
            /* test */Console.WriteLine();
            """));
    }
 
    [Fact]
    public void Group()
    {
        Verify(
            """
            #:property X=Y
            #:package B@C
            #:project D
            #:package E
 
            Console.WriteLine();
            """,
            (static editor => editor.Add(new CSharpDirective.Package(default) { Name = "MyPackage", Version = "1.0.0" }),
            """
            #:property X=Y
            #:package B@C
            #:package MyPackage@1.0.0
            #:project D
            #:package E
 
            Console.WriteLine();
            """),
            (static editor => editor.Remove(editor.Directives[2]),
            """
            #:property X=Y
            #:package B@C
            #:project D
            #:package E
 
            Console.WriteLine();
            """));
    }
 
    [Fact]
    public void GroupEnd()
    {
        Verify(
            """
            #:property X=Y
            #:package B@C
 
            Console.WriteLine();
            """,
            (static editor => editor.Add(new CSharpDirective.Package(default) { Name = "MyPackage", Version = "1.0.0" }),
            """
            #:property X=Y
            #:package B@C
            #:package MyPackage@1.0.0
 
            Console.WriteLine();
            """),
            (static editor => editor.Remove(editor.Directives[2]),
            """
            #:property X=Y
            #:package B@C
 
            Console.WriteLine();
            """));
    }
 
    [Fact]
    public void GroupWithoutSpace()
    {
        Verify(
            """
            #:package B@C
            Console.WriteLine();
            """,
            (static editor => editor.Add(new CSharpDirective.Package(default) { Name = "MyPackage", Version = "1.0.0" }),
            """
            #:package B@C
            #:package MyPackage@1.0.0
            Console.WriteLine();
            """),
            (static editor => editor.Remove(editor.Directives[1]),
            """
            #:package B@C
            Console.WriteLine();
            """));
    }
 
    [Fact]
    public void OtherDirectives()
    {
        Verify(
            """
            #:property A
            #:project D
            Console.WriteLine();
            """,
            (static editor => editor.Add(new CSharpDirective.Package(default) { Name = "MyPackage", Version = "1.0.0" }),
            """
            #:package MyPackage@1.0.0
            #:property A
            #:project D
            Console.WriteLine();
            """),
            (static editor => editor.Remove(editor.Directives[0]),
            """
            #:property A
            #:project D
            Console.WriteLine();
            """));
    }
 
    [Fact]
    public void AfterTokens()
    {
        Verify(
            """
            using System;
 
            #:package A
 
            Console.WriteLine();
            """,
            (static editor => editor.Add(new CSharpDirective.Package(default) { Name = "MyPackage", Version = "1.0.0" }),
            """
            #:package MyPackage@1.0.0
 
            using System;
 
            #:package A
 
            Console.WriteLine();
            """),
            (static editor => editor.Remove(editor.Directives[0]),
            """
            using System;
 
            #:package A
 
            Console.WriteLine();
            """));
    }
 
    [Fact]
    public void SkippedTokensTrivia()
    {
        Verify(
            """
            #if false
            Test
            #endif
            """,
            (static editor => editor.Add(new CSharpDirective.Package(default) { Name = "MyPackage", Version = "1.0.0" }),
            """
            #:package MyPackage@1.0.0
 
            #if false
            Test
            #endif
            """),
            (static editor => editor.Remove(editor.Directives[0]),
            """
            #if false
            Test
            #endif
            """));
    }
 
    [Fact]
    public void RemoveMultiple()
    {
        Verify(
            """
            #:package Humanizer@2.14.1
            #:property X=Y
            #:package Humanizer@2.9.9
 
            Console.WriteLine();
            """,
            (static editor =>
            {
                editor.Remove(editor.Directives.OfType<CSharpDirective.Package>().First());
                editor.Remove(editor.Directives.OfType<CSharpDirective.Package>().First());
            },
            """
            #:property X=Y
 
            Console.WriteLine();
            """));
    }
 
    private void Verify(
        string input,
        params ReadOnlySpan<(Action<FileBasedAppSourceEditor> action, string expectedOutput)> verify)
    {
        var editor = CreateEditor(input);
        int index = 0;
        foreach (var (action, expectedOutput) in verify)
        {
            action(editor);
            var actualOutput = editor.SourceFile.Text.ToString();
            if (actualOutput != expectedOutput)
            {
                Log.WriteLine("Expected output:\n---");
                Log.WriteLine(expectedOutput);
                Log.WriteLine("---\nActual output:\n---");
                Log.WriteLine(actualOutput);
                Log.WriteLine("---");
                Assert.Fail($"Output mismatch at index {index}.");
            }
            index++;
        }
    }
}