File: CommandLineRunnerTests.cs
Web Access
Project: src\src\Scripting\CSharpTest\Microsoft.CodeAnalysis.CSharp.Scripting.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.Scripting.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.IO;
using System.Linq;
using System.Reflection;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Scripting.Hosting;
using Microsoft.CodeAnalysis.Scripting;
using Microsoft.CodeAnalysis.Scripting.Hosting;
using Microsoft.CodeAnalysis.Scripting.Test;
using Microsoft.CodeAnalysis.Scripting.TestUtilities;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Roslyn.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.CSharp.Scripting.UnitTests
{
    using static TestCompilationFactory;
 
    public class CommandLineRunnerTests : CSharpScriptTestBase
    {
        private static readonly string s_compilerVersion = CommonCompiler.GetProductVersion(typeof(CSharpInteractiveCompiler));
 
        private string LogoAndHelpPrompt => $@"{string.Format(CSharpScriptingResources.LogoLine1, s_compilerVersion)}
{CSharpScriptingResources.LogoLine2}
 
{ScriptingResources.HelpPrompt}";
 
        [ConditionalFact(typeof(ClrOnly), Reason = "https://github.com/dotnet/roslyn/issues/30303")]
        public void Await()
        {
            var runner = CreateRunner(input:
@"async Task<int[]> GetStuffAsync()
{
  return new int[] { 1, 2, 3, 4, 5 };
}
from x in await GetStuffAsync()
where x > 2
select x * x
");
            runner.RunInteractive();
 
            var iteratorType = RuntimeUtilities.IsCoreClr9OrHigherRuntime
                ? "ArrayWhereSelectIterator"
                : "WhereSelectArrayIterator";
            AssertEx.AssertEqualToleratingWhitespaceDifferences(
$@"{LogoAndHelpPrompt}
> async Task<int[]> GetStuffAsync()
. {{
.   return new int[] {{ 1, 2, 3, 4, 5 }};
. }}
«Yellow»
(1,19): warning CS1998: {CSharpResources.WRN_AsyncLacksAwaits}
«Gray»
> from x in await GetStuffAsync()
. where x > 2
. select x * x
Enumerable.{iteratorType}<int, int> {{ 9, 16, 25 }}
> ", runner.Console.Out.ToString());
 
            AssertEx.AssertEqualToleratingWhitespaceDifferences(
                $@"(1,19): warning CS1998: {CSharpResources.WRN_AsyncLacksAwaits}",
                runner.Console.Error.ToString());
        }
 
        [Fact(Skip = "https://github.com/dotnet/roslyn/issues/17043")]
        [WorkItem("http://github.com/dotnet/roslyn/issues/7133")]
        public void TestDisplayResultsWithCurrentUICulture1()
        {
            // logoOutput needs to be retrieved before the runner is started, because the runner changes the culture to de-DE. 
            var logoOutput = $@"{string.Format(CSharpScriptingResources.LogoLine1, s_compilerVersion)}
{CSharpScriptingResources.LogoLine2}
 
{ScriptingResources.HelpPrompt}";
            var runner = CreateRunner(input:
@"using System.Globalization;
CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo(""en-GB"", useUserOverride: false)
Math.PI
CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo(""de-DE"", useUserOverride: false)
Math.PI
");
            runner.RunInteractive();
 
            AssertEx.AssertEqualToleratingWhitespaceDifferences(
$@"{logoOutput}
> using System.Globalization;
> CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo(""en-GB"", useUserOverride: false)
[en-GB]
> Math.PI
3.1415926535897931
> CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo(""de-DE"", useUserOverride: false)
[de-DE]
> Math.PI
3,1415926535897931
>", runner.Console.Out.ToString());
        }
 
        [ConditionalFact(typeof(WindowsDesktopOnly), Reason = "https://github.com/dotnet/roslyn/issues/30924")]
        [WorkItem("https://github.com/dotnet/roslyn/issues/33564")]
        [WorkItem("http://github.com/dotnet/roslyn/issues/7133")]
        public void TestDisplayResultsWithCurrentUICulture2()
        {
            // logoOutput needs to be retrieved before the runner is started, because the runner changes the culture to de-DE. 
            var logoOutput = $@"{string.Format(CSharpScriptingResources.LogoLine1, s_compilerVersion)}
{CSharpScriptingResources.LogoLine2}
 
{ScriptingResources.HelpPrompt}";
            // Tests that DefaultThreadCurrentUICulture is respected and not DefaultThreadCurrentCulture.
            var runner = CreateRunner(input:
@"using System.Globalization;
CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo(""en-GB"", useUserOverride: false)
CultureInfo.DefaultThreadCurrentCulture = new CultureInfo(""en-GB"", useUserOverride: false)
Math.PI
CultureInfo.DefaultThreadCurrentCulture = new CultureInfo(""de-DE"", useUserOverride: false)
Math.PI
");
            runner.RunInteractive();
 
            AssertEx.AssertEqualToleratingWhitespaceDifferences(
$@"{logoOutput}
> using System.Globalization;
> CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo(""en-GB"", useUserOverride: false)
[en-GB]
> CultureInfo.DefaultThreadCurrentCulture = new CultureInfo(""en-GB"", useUserOverride: false)
[en-GB]
> Math.PI
3.1415926535897931
> CultureInfo.DefaultThreadCurrentCulture = new CultureInfo(""de-DE"", useUserOverride: false)
[de-DE]
> Math.PI
3.1415926535897931
>", runner.Console.Out.ToString());
        }
 
        [ConditionalFact(typeof(ClrOnly), Reason = "https://github.com/dotnet/roslyn/issues/30303")]
        public void Void()
        {
            var runner = CreateRunner(input:
@"Print(1);
Print(2)
");
            runner.RunInteractive();
 
            AssertEx.AssertEqualToleratingWhitespaceDifferences(
$@"{LogoAndHelpPrompt}
> Print(1);
1
> Print(2)
2
> ", runner.Console.Out.ToString());
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/18479")]
        public void Tuples()
        {
            var runner = CreateRunner(input: "(1,2)");
            runner.RunInteractive();
 
            AssertEx.AssertEqualToleratingWhitespaceDifferences(
$@"{LogoAndHelpPrompt}
> (1,2)
(1, 2)
> ", runner.Console.Out.ToString());
        }
 
        [ConditionalFact(typeof(ClrOnly), Reason = "https://github.com/dotnet/roslyn/issues/30303")]
        public void Exception()
        {
            var runner = CreateRunner(input:
@"int div(int a, int b) => a/b;
div(10, 2)
div(10, 0)
");
            Assert.Equal(0, runner.RunInteractive());
 
            var exception = new DivideByZeroException();
            Assert.Equal(
$@"{LogoAndHelpPrompt}
> int div(int a, int b) => a/b;
> div(10, 2)
5
> div(10, 0)
«Red»
{exception.GetType()}: {exception.Message}
  + Submission#0.div(int, int)
«Gray»
> ", runner.Console.Out.ToString());
 
            Assert.Equal(
$@"{exception.GetType()}: {exception.Message}
  + Submission#0.div(int, int)
", runner.Console.Error.ToString());
        }
 
        [ConditionalFact(typeof(ClrOnly), Reason = "https://github.com/dotnet/roslyn/issues/30303")]
        public void ExceptionInGeneric()
        {
            var runner = CreateRunner(input:
@"static class C<T> { public static int div<U>(int a, int b) => a/b; }
C<string>.div<bool>(10, 2)
C<string>.div<bool>(10, 0)
");
            Assert.Equal(0, runner.RunInteractive());
 
            var exception = new DivideByZeroException();
            Assert.Equal(
$@"{LogoAndHelpPrompt}
> static class C<T> {{ public static int div<U>(int a, int b) => a/b; }}
> C<string>.div<bool>(10, 2)
5
> C<string>.div<bool>(10, 0)
«Red»
{exception.GetType()}: {exception.Message}
  + Submission#0.C<T>.div<U>(int, int)
«Gray»
> ", runner.Console.Out.ToString());
 
            Assert.Equal(
$@"{exception.GetType()}: {exception.Message}
  + Submission#0.C<T>.div<U>(int, int)
", runner.Console.Error.ToString());
        }
 
        [Fact]
        public void Args_Interactive1()
        {
            var runner = CreateRunner(
                args: ["-i"],
                input: "1+1");
 
            runner.RunInteractive();
 
            Assert.Equal(
$@"{LogoAndHelpPrompt}
> 1+1
2
> ", runner.Console.Out.ToString());
        }
 
        [Fact]
        public void Args_Interactive2()
        {
            var runner = CreateRunner(
                args: ["/u:System", "/i", "--", "@arg1", "/arg2", "-arg3", "--arg4"],
                input: "foreach (var arg in Args) Print(arg);");
 
            runner.RunInteractive();
 
            var error = $@"error CS2001: {string.Format(CSharpResources.ERR_FileNotFound, Path.Combine(AppContext.BaseDirectory, "@arg1"))}";
            AssertEx.AssertEqualToleratingWhitespaceDifferences(error, runner.Console.Out.ToString());
            AssertEx.AssertEqualToleratingWhitespaceDifferences(error, runner.Console.Error.ToString());
        }
 
        [ConditionalFact(typeof(WindowsOnly)), WorkItem("https://github.com/dotnet/roslyn/issues/15860")]
        public void Args_InteractiveWithScript1()
        {
            var script = Temp.CreateFile(extension: ".csx").WriteAllText("foreach (var arg in Args) Print(arg);");
 
            var rsp = Temp.CreateFile(extension: ".rsp").WriteAllText($@"
/u:System
/i
""{script.Path}""
@arg1
/arg2
-arg3
--arg4");
 
            var runner = CreateRunner(
                args: [$@"@""{rsp.Path}""", "/arg5", "--", "/arg7"],
                input: "1");
 
            runner.RunInteractive();
 
            AssertEx.AssertEqualToleratingWhitespaceDifferences(
$@"""@arg1""
""/arg2""
""-arg3""
""--arg4""
""/arg5""
""--""
""/arg7""
> 1
1
> ", runner.Console.Out.ToString());
        }
 
        [ConditionalFact(typeof(WindowsOnly)), WorkItem("https://github.com/dotnet/roslyn/issues/15860")]
        public void Args_Script1()
        {
            var script = Temp.CreateFile(extension: ".csx").WriteAllText("foreach (var arg in Args) Print(arg);");
 
            var runner = CreateRunner(
                args: [script.Path, "arg1", "arg2", "arg3"]);
 
            Assert.True(runner.RunInteractive() == 0, userMessage: runner.Console.Error.ToString());
 
            AssertEx.AssertEqualToleratingWhitespaceDifferences($@"
""arg1""
""arg2""
""arg3""
", runner.Console.Out.ToString());
        }
 
        [ConditionalFact(typeof(WindowsOnly)), WorkItem("https://github.com/dotnet/roslyn/issues/15860")]
        public void Args_Script2()
        {
            var script = Temp.CreateFile(extension: ".csx").WriteAllText("foreach (var arg in Args) Print(arg);");
 
            var runner = CreateRunner(
                args: [script.Path, "@arg1", "@arg2", "@arg3"]);
 
            Assert.True(runner.RunInteractive() == 0, userMessage: runner.Console.Error.ToString());
 
            AssertEx.AssertEqualToleratingWhitespaceDifferences($@"
""@arg1""
""@arg2""
""@arg3""
", runner.Console.Out.ToString());
        }
 
        [ConditionalFact(typeof(WindowsOnly)), WorkItem("https://github.com/dotnet/roslyn/issues/15860")]
        public void Args_Script3()
        {
            var script = Temp.CreateFile(extension: ".csx").WriteAllText("foreach (var arg in Args) Print(arg);");
 
            var rsp = Temp.CreateFile(extension: ".rsp").WriteAllText($@"
/u:System
{script.Path}
--
@arg1
/arg2
-arg3
--arg4");
 
            var runner = CreateRunner(
                args: [$"@{rsp.Path}", "/arg5", "--", "/arg7"],
                input: "foreach (var arg in Args) Print(arg);");
 
            Assert.True(runner.RunInteractive() == 0, userMessage: runner.Console.Error.ToString());
 
            AssertEx.AssertEqualToleratingWhitespaceDifferences($@"
""--""
""@arg1""
""/arg2""
""-arg3""
""--arg4""
""/arg5""
""--""
""/arg7""
", runner.Console.Out.ToString());
        }
 
        [ConditionalFact(typeof(ClrOnly), Reason = "https://github.com/dotnet/roslyn/issues/30303")]
        public void Args_Script4()
        {
            var script = Temp.CreateFile(prefix: "@", extension: ".csx").WriteAllText("foreach (var arg in Args) Print(arg);");
 
            var runner = CreateRunner(
                args: ["--", script.Path, "@arg1", "@arg2", "@arg3"]);
 
            runner.RunInteractive();
 
            AssertEx.AssertEqualToleratingWhitespaceDifferences($@"
""@arg1""
""@arg2""
""@arg3""
", runner.Console.Out.ToString());
        }
 
        [ConditionalFact(typeof(ClrOnly), Reason = "https://github.com/dotnet/roslyn/issues/30303")]
        public void Args_Script5()
        {
            var dir = Temp.CreateDirectory();
            var script = dir.CreateFile("--").WriteAllText("foreach (var arg in Args) Print(arg);");
 
            var runner = CreateRunner(
                args: ["--", "--", "-", "--", "-"],
                workingDirectory: dir.Path);
 
            runner.RunInteractive();
 
            AssertEx.AssertEqualToleratingWhitespaceDifferences($@"
""-""
""--""
""-""
", runner.Console.Out.ToString());
        }
 
        [Fact]
        public void Script_NonExistingFile()
        {
            var runner = CreateRunner(["a + b"]);
 
            Assert.Equal(1, runner.RunInteractive());
 
            var error = $@"error CS2001: {string.Format(CSharpResources.ERR_FileNotFound, Path.Combine(AppContext.BaseDirectory, "a + b"))}";
            AssertEx.AssertEqualToleratingWhitespaceDifferences(error, runner.Console.Out.ToString());
            AssertEx.AssertEqualToleratingWhitespaceDifferences(error, runner.Console.Error.ToString());
        }
 
        [Fact]
        public void Help()
        {
            var runner = CreateRunner(["/help"]);
 
            Assert.Equal(0, runner.RunInteractive());
 
            AssertEx.AssertEqualToleratingWhitespaceDifferences(
$@"{string.Format(CSharpScriptingResources.LogoLine1, s_compilerVersion)}
{CSharpScriptingResources.LogoLine2}
 
{CSharpScriptingResources.InteractiveHelp}
", runner.Console.Out.ToString());
        }
 
        [Fact]
        public void Version()
        {
            var runner = CreateRunner(["/version"]);
            Assert.Equal(0, runner.RunInteractive());
            AssertEx.AssertEqualToleratingWhitespaceDifferences(s_compilerVersion, runner.Console.Out.ToString());
 
            runner = CreateRunner(["/version", "/help"]);
            Assert.Equal(0, runner.RunInteractive());
            AssertEx.AssertEqualToleratingWhitespaceDifferences(s_compilerVersion, runner.Console.Out.ToString());
 
            runner = CreateRunner(["/version", "/r:somefile"]);
            Assert.Equal(0, runner.RunInteractive());
            AssertEx.AssertEqualToleratingWhitespaceDifferences(s_compilerVersion, runner.Console.Out.ToString());
 
            runner = CreateRunner(["/version", "/nologo"]);
            Assert.Equal(0, runner.RunInteractive());
            AssertEx.AssertEqualToleratingWhitespaceDifferences(s_compilerVersion, runner.Console.Out.ToString());
        }
 
        [ConditionalFact(typeof(ClrOnly), Reason = "https://github.com/dotnet/roslyn/issues/30303")]
        public void Script_BadUsings()
        {
            var script = Temp.CreateFile(extension: ".csx").WriteAllText("WriteLine(42);");
 
            var runner = CreateRunner(
            [
                GacFileResolver.IsAvailable ? null : "/r:System.Console",
                "/u:System.Console;Alpha.Beta",
                script.Path
            ]);
 
            Assert.Equal(1, runner.RunInteractive());
 
            var error = $@"error CS0246: {string.Format(CSharpResources.ERR_SingleTypeNameNotFound, "Alpha")}";
            AssertEx.AssertEqualToleratingWhitespaceDifferences(error, runner.Console.Out.ToString());
            AssertEx.AssertEqualToleratingWhitespaceDifferences(error, runner.Console.Error.ToString());
        }
 
        [ConditionalFact(typeof(WindowsDesktopOnly), Reason = "https://github.com/dotnet/roslyn/issues/29908")]
        public void Script_NoHostNamespaces()
        {
            var runner = CreateRunner(input: "nameof(Microsoft.Missing)");
 
            runner.RunInteractive();
            AssertEx.AssertEqualToleratingWhitespaceDifferences(
$@"{LogoAndHelpPrompt}
> nameof(Microsoft.Missing)
«Red»
(1,8): error CS0234: {string.Format(CSharpResources.ERR_DottedTypeNameNotFoundInNS, "Missing", "Microsoft")}
«Gray»
> ", runner.Console.Out.ToString());
 
            AssertEx.AssertEqualToleratingWhitespaceDifferences(
                $"(1,8): error CS0234: {string.Format(CSharpResources.ERR_DottedTypeNameNotFoundInNS, "Missing", "Microsoft")}",
                runner.Console.Error.ToString());
        }
 
        [ConditionalFact(typeof(ClrOnly), Reason = "https://github.com/dotnet/roslyn/issues/30303")]
        public void RelativePath()
        {
            using (var directory = new DisposableDirectory(Temp))
            {
                const string scriptName = "c.csx";
                var script = directory.CreateFile(scriptName).WriteAllText("Print(3);");
                var scriptPath = PathUtilities.CombinePathsUnchecked(PathUtilities.GetFileName(directory.Path), scriptName);
                var workingDirectory = PathUtilities.GetDirectoryName(directory.Path);
                Assert.False(PathUtilities.IsAbsolute(scriptPath));
                var runner = CreateRunner(
                    args: [scriptPath],
                    workingDirectory: workingDirectory);
                runner.RunInteractive();
                AssertEx.AssertEqualToleratingWhitespaceDifferences("3", runner.Console.Out.ToString());
            }
        }
 
        [ConditionalTheory(typeof(WindowsOnly))]
        [InlineData(null, null)]
        [InlineData("c:", null)]
        [InlineData("c:\\", null)]
        [InlineData("c:\\first", "c:\\")]
        [InlineData("c:\\first\\", "c:\\first")]
        [InlineData("c:\\first\\second", "c:\\first")]
        [InlineData("c:\\first\\second\\", "c:\\first\\second")]
        [InlineData("c:\\first\\second\\third", "c:\\first\\second")]
        [InlineData("\\", null)]
        [InlineData("\\first", "\\")]
        [InlineData("\\first\\", "\\first")]
        [InlineData("\\first\\second", "\\first")]
        [InlineData("\\first\\second\\", "\\first\\second")]
        [InlineData("\\first\\second\\third", "\\first\\second")]
        [InlineData("first", "")]
        [InlineData("first\\", "first")]
        [InlineData("first\\second", "first")]
        [InlineData("first\\second\\", "first\\second")]
        [InlineData("first\\second\\third", "first\\second")]
        public void TestGetDirectoryName_Windows(string path, string expectedOutput)
        {
            Assert.Equal(expectedOutput, PathUtilities.GetDirectoryName(path, isUnixLike: false));
        }
 
        [ConditionalFact(typeof(ClrOnly), Reason = "https://github.com/dotnet/roslyn/issues/30289")]
        public void SourceSearchPaths1()
        {
            var main = Temp.CreateFile(extension: ".csx").WriteAllText(@"
#load ""1.csx""
#load ""2.csx""
#load ""3.csx""
Print(4);
");
 
            var dir1 = Temp.CreateDirectory();
            dir1.CreateFile("1.csx").WriteAllText(@"Print(1);");
 
            var dir2 = Temp.CreateDirectory();
            dir2.CreateFile("2.csx").WriteAllText(@"Print(2);");
 
            var dir3 = Temp.CreateDirectory();
            dir3.CreateFile("3.csx").WriteAllText(@"Print(3);");
 
            var runner = CreateRunner([$"/loadpath:{dir1.Path}", $"/loadpaths:{dir2.Path};{dir3.Path}", main.Path]);
 
            Assert.Equal(0, runner.RunInteractive());
 
            AssertEx.AssertEqualToleratingWhitespaceDifferences(@"
1
2
3
4
", runner.Console.Out.ToString());
        }
 
        [Fact(Skip = "https://github.com/dotnet/roslyn/issues/26510")]
        public void ReferenceSearchPaths1()
        {
            var main = Temp.CreateFile(extension: ".csx").WriteAllText(@"
#r ""1.dll""
#r ""2.dll""
#r ""3.dll""
Print(new C1());
Print(new C2());
Print(new C3());
Print(new C4());
");
 
            var dir1 = Temp.CreateDirectory();
            dir1.CreateFile("1.dll").WriteAllBytes(CreateCSharpCompilationWithCorlib("public class C1 {}", "1").EmitToArray());
 
            var dir2 = Temp.CreateDirectory();
            dir2.CreateFile("2.dll").WriteAllBytes(CreateCSharpCompilationWithCorlib("public class C2 {}", "2").EmitToArray());
 
            var dir3 = Temp.CreateDirectory();
            dir3.CreateFile("3.dll").WriteAllBytes(CreateCSharpCompilationWithCorlib("public class C3 {}", "3").EmitToArray());
 
            var dir4 = Temp.CreateDirectory();
            dir4.CreateFile("4.dll").WriteAllBytes(CreateCSharpCompilationWithCorlib("public class C4 {}", "4").EmitToArray());
 
            var runner = CreateRunner(["/r:4.dll", $"/lib:{dir1.Path}", $"/libpath:{dir2.Path}", $"/libpaths:{dir3.Path};{dir4.Path}", main.Path]);
 
            runner.RunInteractive();
 
            AssertEx.AssertEqualToleratingWhitespaceDifferences(@"
C1 { }
C2 { }
C3 { }
C4 { }
", runner.Console.Out.ToString());
        }
 
        [ConditionalFact(typeof(ClrOnly), Reason = "https://github.com/dotnet/roslyn/issues/30303")]
        public void SourceSearchPaths_Change1()
        {
            var dir = Temp.CreateDirectory();
            var main = dir.CreateFile("a.csx").WriteAllText("int X = 1;");
 
            var runner = CreateRunner(input:
$@"SourcePaths
#load ""a.csx""
SourcePaths.Add(@""{dir.Path}"")
#load ""a.csx""
X
");
 
            runner.RunInteractive();
 
            AssertEx.AssertEqualToleratingWhitespaceDifferences($@"
{LogoAndHelpPrompt}
> SourcePaths
SearchPaths {{ }}
> #load ""a.csx""
«Red»
(1,7): error CS1504: {string.Format(CSharpResources.ERR_NoSourceFile, "a.csx", CSharpResources.CouldNotFindFile)}
«Gray»
> SourcePaths.Add(@""{dir.Path}"")
> #load ""a.csx""
> X
1
> 
", runner.Console.Out.ToString());
 
            AssertEx.AssertEqualToleratingWhitespaceDifferences(
                $@"(1,7): error CS1504: {string.Format(CSharpResources.ERR_NoSourceFile, "a.csx", CSharpResources.CouldNotFindFile)}",
                runner.Console.Error.ToString());
        }
 
        [ConditionalFact(typeof(ClrOnly), Reason = "https://github.com/dotnet/roslyn/issues/30303")]
        public void ReferenceSearchPaths_Change1()
        {
            var dir = Temp.CreateDirectory();
            var main = dir.CreateFile("C.dll").WriteAllBytes(TestResources.General.C1);
 
            var runner = CreateRunner(input:
$@"ReferencePaths
#r ""C.dll""
ReferencePaths.Add(@""{dir.Path}"")
#r ""C.dll""
new C()
");
 
            runner.RunInteractive();
 
            AssertEx.AssertEqualToleratingWhitespaceDifferences($@"
{LogoAndHelpPrompt}
> ReferencePaths
SearchPaths {{ }}
> #r ""C.dll""
«Red»
(1,1): error CS0006: {string.Format(CSharpResources.ERR_NoMetadataFile, "C.dll")}
«Gray»
> ReferencePaths.Add(@""{dir.Path}"")
> #r ""C.dll""
> new C()
C {{ }}
> 
", runner.Console.Out.ToString());
 
            AssertEx.AssertEqualToleratingWhitespaceDifferences(
                $@"(1,1): error CS0006: {string.Format(CSharpResources.ERR_NoMetadataFile, "C.dll")}",
                runner.Console.Error.ToString());
        }
 
        [Fact]
        public void ResponseFile()
        {
            var rsp = Temp.CreateFile().WriteAllText(@"
/r:System
/r:System.Core
/r:System.Data
/r:System.Data.DataSetExtensions
/r:System.Xml
/r:System.Xml.Linq
/r:Microsoft.CSharp
/u:System
/u:System.Collections.Generic
/u:System.Linq
/u:System.Text");
 
            var csi = CreateRunner(["b.csx"], responseFile: rsp.Path);
            var arguments = ((CSharpInteractiveCompiler)csi.Compiler).Arguments;
 
            AssertEx.Equal(new[]
            {
                "System",
                "System.Core",
                "System.Data",
                "System.Data.DataSetExtensions",
                "System.Xml",
                "System.Xml.Linq",
                "Microsoft.CSharp",
            }, arguments.MetadataReferences.Select(r => r.Reference));
 
            AssertEx.Equal(new[]
            {
                "System",
                "System.Collections.Generic",
                "System.Linq",
                "System.Text",
            }, arguments.CompilationOptions.Usings.AsEnumerable());
        }
 
        [Fact]
        public void InitialScript1()
        {
            var init = Temp.CreateFile(extension: ".csx").WriteAllText(@"
int X = 1;
");
            var runner = CreateRunner(["/i", init.Path], input:
@"X");
 
            runner.RunInteractive();
 
            AssertEx.AssertEqualToleratingWhitespaceDifferences(@"
> X
1
> 
", runner.Console.Out.ToString());
        }
 
        [ConditionalFact(typeof(ClrOnly), Reason = "https://github.com/dotnet/roslyn/issues/30303")]
        public void InitialScript_Error()
        {
            var reference = Temp.CreateFile(extension: ".dll").WriteAllBytes(TestResources.General.C1);
 
            var init = Temp.CreateFile(extension: ".csx").WriteAllText(@"
1 1
");
            var runner = CreateRunner([$@"/r:""{reference.Path}""", "/i", init.Path], input:
@"new C()");
 
            runner.RunInteractive();
 
            AssertEx.AssertEqualToleratingWhitespaceDifferences($@"
«Red»
{init.Path}(2,3): error CS1002: {CSharpResources.ERR_SemicolonExpected}
«Gray»
> new C()
C {{ }}
> 
", runner.Console.Out.ToString());
 
            AssertEx.AssertEqualToleratingWhitespaceDifferences(
                $@"{init.Path}(2,3): error CS1002: {CSharpResources.ERR_SemicolonExpected}",
                runner.Console.Error.ToString());
        }
 
        [Fact(Skip = "https://github.com/dotnet/roslyn/issues/24402")]
        public void HelpCommand()
        {
            var runner = CreateRunner(input:
@"#help
");
            runner.RunInteractive();
 
            Assert.Equal(
$@"{LogoAndHelpPrompt}
> #help
{ScriptingResources.HelpText}
> ", runner.Console.Out.ToString());
        }
 
        [Fact]
        public void LangVersions()
        {
            var runner = CreateRunner(["/langversion:?"]);
            Assert.Equal(0, runner.RunInteractive());
 
            var expected = Enum.GetValues(typeof(LanguageVersion)).Cast<LanguageVersion>()
                .Select(v => v.ToDisplayString());
 
            var actual = runner.Console.Out.ToString();
            var acceptableSurroundingChar = new[] { '\r', '\n', '(', ')', ' ' };
            foreach (var version in expected)
            {
                if (version == "latest")
                    continue;
 
                var foundIndex = actual.IndexOf(version);
                Assert.True(foundIndex > 0, $"Missing version '{version}'");
                Assert.True(Array.IndexOf(acceptableSurroundingChar, actual[foundIndex - 1]) >= 0);
                Assert.True(Array.IndexOf(acceptableSurroundingChar, actual[foundIndex + version.Length]) >= 0);
            }
        }
 
        [ConditionalFact(typeof(ClrOnly), Reason = "https://github.com/dotnet/roslyn/issues/30303")]
        public void SharedLibCopy_Different()
        {
            string libBaseName = "LibBase_" + Guid.NewGuid();
            string lib1Name = "Lib1_" + Guid.NewGuid();
            string lib2Name = "Lib2_" + Guid.NewGuid();
 
            var libBase1 = TestCompilationFactory.CreateCSharpCompilation(@"
public class LibBase
{
    public readonly int X = 1;
}
", new[] { NetFramework.mscorlib }, libBaseName);
 
            var libBase2 = TestCompilationFactory.CreateCSharpCompilation(@"
public class LibBase
{
    public readonly int X = 2;
}
", new[] { NetFramework.mscorlib }, libBaseName);
 
            var lib1 = TestCompilationFactory.CreateCSharpCompilation(@"
public class Lib1
{
    public LibBase libBase = new LibBase();
}
", new MetadataReference[] { NetFramework.mscorlib, libBase1.ToMetadataReference() }, lib1Name);
 
            var lib2 = TestCompilationFactory.CreateCSharpCompilation(@"
public class Lib2
{
    public LibBase libBase = new LibBase();
}
", new MetadataReference[] { NetFramework.mscorlib, libBase1.ToMetadataReference() }, lib2Name);
 
            var libBase1Image = libBase1.EmitToArray();
            var libBase2Image = libBase2.EmitToArray();
            var lib1Image = lib1.EmitToArray();
            var lib2Image = lib2.EmitToArray();
 
            var root = Temp.CreateDirectory();
            var dir1 = root.CreateDirectory("1");
            var file1 = dir1.CreateFile(lib1Name + ".dll").WriteAllBytes(lib1Image);
            var fileBase1 = dir1.CreateFile(libBaseName + ".dll").WriteAllBytes(libBase1Image);
 
            var dir2 = root.CreateDirectory("2");
            var file2 = dir2.CreateFile(lib2Name + ".dll").WriteAllBytes(lib2Image);
            var fileBase2 = dir2.CreateFile(libBaseName + ".dll").WriteAllBytes(libBase2Image);
 
            var runner = CreateRunner(input:
$@"#r ""{file1.Path}""
var l1 = new Lib1();
#r ""{file2.Path}""
var l2 = new Lib2();
");
            runner.RunInteractive();
 
            AssertEx.AssertEqualToleratingWhitespaceDifferences(
$@"{LogoAndHelpPrompt}
> #r ""{file1.Path}""
> var l1 = new Lib1();
> #r ""{file2.Path}""
> var l2 = new Lib2();
«Red»
{string.Format(ScriptingResources.AssemblyAlreadyLoaded, libBaseName, "0.0.0.0", fileBase1.Path, fileBase2.Path)}
«Gray»
> ", runner.Console.Out.ToString());
        }
 
        [ConditionalFact(typeof(ClrOnly), Reason = "https://github.com/dotnet/roslyn/issues/30303")]
        [WorkItem("https://github.com/dotnet/roslyn/issues/6580")]
        public void PreservingDeclarationsOnException()
        {
            var runner = CreateRunner(input:
@"int i = 100;
int j = 20; throw new System.Exception(""Bang!""); int k = 3;
i + j + k
");
            runner.RunInteractive();
 
            AssertEx.AssertEqualToleratingWhitespaceDifferences(
$@"{LogoAndHelpPrompt}
> int i = 100;
> int j = 20; throw new System.Exception(""Bang!""); int k = 3;
«Yellow»
(1,58): warning CS0162: {CSharpResources.WRN_UnreachableCode}
«Red»
System.Exception: Bang!
«Gray»
> i + j + k
120
> ", runner.Console.Out.ToString());
 
            AssertEx.AssertEqualToleratingWhitespaceDifferences(
$@"(1,58): warning CS0162: {CSharpResources.WRN_UnreachableCode}
System.Exception: Bang!",
                runner.Console.Error.ToString());
        }
 
        [ConditionalFact(typeof(ClrOnly), Reason = "https://github.com/dotnet/roslyn/issues/30303")]
        [WorkItem("https://github.com/dotnet/roslyn/issues/21327")]
        public void DefaultLiteral()
        {
            var runner = CreateRunner(input:
@"int i = default;
Print(i);
");
            runner.RunInteractive();
 
            AssertEx.AssertEqualToleratingWhitespaceDifferences(
$@"{LogoAndHelpPrompt}
> int i = default;
> Print(i);
0
> ", runner.Console.Out.ToString());
        }
 
        [ConditionalFact(typeof(ClrOnly), Reason = "https://github.com/dotnet/roslyn/issues/30303")]
        [WorkItem("https://github.com/dotnet/roslyn/issues/21327")]
        public void InferredTupleNames()
        {
            var runner = CreateRunner(input:
@"var a = 1;
var t = (a, 2);
Print(t.a);
");
            runner.RunInteractive();
 
            AssertEx.AssertEqualToleratingWhitespaceDifferences(
$@"{string.Format(CSharpScriptingResources.LogoLine1, s_compilerVersion)}
{CSharpScriptingResources.LogoLine2}
{ScriptingResources.HelpPrompt}
> var a = 1;
> var t = (a, 2);
> Print(t.a);
1
> ", runner.Console.Out.ToString());
        }
    }
}