Invoke compile-csc in-proc
Since processes are expensive, suppress spawning a new process when dotnet-compile is invoking dotnet-compile-csc.
This commit is contained in:
parent
37f00f24e9
commit
6afc2ca813
9 changed files with 508 additions and 53 deletions
81
src/Microsoft.DotNet.Cli.Utils/BlockingMemoryStream.cs
Normal file
81
src/Microsoft.DotNet.Cli.Utils/BlockingMemoryStream.cs
Normal file
|
@ -0,0 +1,81 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
|
||||
namespace Microsoft.DotNet.Cli.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// An in-memory stream that will block any read calls until something was written to it.
|
||||
/// </summary>
|
||||
public sealed class BlockingMemoryStream : Stream
|
||||
{
|
||||
private readonly BlockingCollection<byte[]> _buffers = new BlockingCollection<byte[]>();
|
||||
private ArraySegment<byte> _remaining;
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
byte[] tmp = new byte[count];
|
||||
Buffer.BlockCopy(buffer, offset, tmp, 0, count);
|
||||
_buffers.Add(tmp);
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (count == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (_remaining.Count == 0)
|
||||
{
|
||||
byte[] tmp;
|
||||
if (!_buffers.TryTake(out tmp, Timeout.Infinite) || tmp.Length == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
_remaining = new ArraySegment<byte>(tmp, 0, tmp.Length);
|
||||
}
|
||||
|
||||
if (_remaining.Count <= count)
|
||||
{
|
||||
count = _remaining.Count;
|
||||
Buffer.BlockCopy(_remaining.Array, _remaining.Offset, buffer, offset, count);
|
||||
_remaining = default(ArraySegment<byte>);
|
||||
}
|
||||
else
|
||||
{
|
||||
Buffer.BlockCopy(_remaining.Array, _remaining.Offset, buffer, offset, count);
|
||||
_remaining = new ArraySegment<byte>(_remaining.Array, _remaining.Offset + count, _remaining.Count - count);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public void DoneWriting()
|
||||
{
|
||||
_buffers.CompleteAdding();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_buffers.Dispose();
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
public override bool CanRead => true;
|
||||
public override bool CanSeek => false;
|
||||
public override bool CanWrite => true;
|
||||
public override long Length { get { throw new NotImplementedException(); } }
|
||||
public override long Position { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } }
|
||||
public override void Flush() { }
|
||||
public override long Seek(long offset, SeekOrigin origin) { throw new NotImplementedException(); }
|
||||
public override void SetLength(long value) { throw new NotImplementedException(); }
|
||||
}
|
||||
}
|
141
src/Microsoft.DotNet.Cli.Utils/BuiltInCommand.cs
Normal file
141
src/Microsoft.DotNet.Cli.Utils/BuiltInCommand.cs
Normal file
|
@ -0,0 +1,141 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace Microsoft.DotNet.Cli.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// A Command that is capable of running in the current process.
|
||||
/// </summary>
|
||||
public class BuiltInCommand : ICommand
|
||||
{
|
||||
private readonly IEnumerable<string> _commandArgs;
|
||||
private readonly Func<string[], int> _builtInCommand;
|
||||
private readonly StreamForwarder _stdOut;
|
||||
private readonly StreamForwarder _stdErr;
|
||||
|
||||
public string CommandName { get; }
|
||||
public string CommandArgs => string.Join(" ", _commandArgs);
|
||||
|
||||
public BuiltInCommand(string commandName, IEnumerable<string> commandArgs, Func<string[], int> builtInCommand)
|
||||
{
|
||||
CommandName = commandName;
|
||||
_commandArgs = commandArgs;
|
||||
_builtInCommand = builtInCommand;
|
||||
|
||||
_stdOut = new StreamForwarder();
|
||||
_stdErr = new StreamForwarder();
|
||||
}
|
||||
|
||||
public CommandResult Execute()
|
||||
{
|
||||
TextWriter originalConsoleOut = Console.Out;
|
||||
TextWriter originalConsoleError = Console.Error;
|
||||
|
||||
try
|
||||
{
|
||||
// redirecting the standard out and error so we can forward
|
||||
// the output to the caller
|
||||
using (BlockingMemoryStream outStream = new BlockingMemoryStream())
|
||||
using (BlockingMemoryStream errorStream = new BlockingMemoryStream())
|
||||
{
|
||||
Console.SetOut(new StreamWriter(outStream) { AutoFlush = true });
|
||||
Console.SetError(new StreamWriter(errorStream) { AutoFlush = true });
|
||||
|
||||
// Reset the Reporters to the new Console Out and Error.
|
||||
Reporter.Reset();
|
||||
|
||||
Thread threadOut = _stdOut.BeginRead(new StreamReader(outStream));
|
||||
Thread threadErr = _stdErr.BeginRead(new StreamReader(errorStream));
|
||||
|
||||
int exitCode = _builtInCommand(_commandArgs.ToArray());
|
||||
|
||||
outStream.DoneWriting();
|
||||
errorStream.DoneWriting();
|
||||
|
||||
threadOut.Join();
|
||||
threadErr.Join();
|
||||
|
||||
// fake out a ProcessStartInfo using the Muxer command name, since this is a built-in command
|
||||
ProcessStartInfo startInfo = new ProcessStartInfo(new Muxer().MuxerPath, $"{CommandName} {CommandArgs}");
|
||||
return new CommandResult(startInfo, exitCode, null, null);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Console.SetOut(originalConsoleOut);
|
||||
Console.SetError(originalConsoleError);
|
||||
|
||||
Reporter.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
public ICommand OnOutputLine(Action<string> handler)
|
||||
{
|
||||
if (handler == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(handler));
|
||||
}
|
||||
|
||||
_stdOut.ForwardTo(writeLine: handler);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public ICommand OnErrorLine(Action<string> handler)
|
||||
{
|
||||
if (handler == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(handler));
|
||||
}
|
||||
|
||||
_stdErr.ForwardTo(writeLine: handler);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandResolutionStrategy ResolutionStrategy
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public ICommand CaptureStdErr()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public ICommand CaptureStdOut()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public ICommand EnvironmentVariable(string name, string value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public ICommand ForwardStdErr(TextWriter to = null, bool onlyIfVerbose = false, bool ansiPassThrough = true)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public ICommand ForwardStdOut(TextWriter to = null, bool onlyIfVerbose = false, bool ansiPassThrough = true)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public ICommand WorkingDirectory(string projectDirectory)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,16 +14,34 @@ namespace Microsoft.DotNet.Cli.Utils
|
|||
|
||||
private readonly AnsiConsole _console;
|
||||
|
||||
static Reporter()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
private Reporter(AnsiConsole console)
|
||||
{
|
||||
_console = console;
|
||||
}
|
||||
|
||||
public static Reporter Output { get; } = new Reporter(AnsiConsole.GetOutput());
|
||||
public static Reporter Error { get; } = new Reporter(AnsiConsole.GetError());
|
||||
public static Reporter Verbose { get; } = CommandContext.IsVerbose() ?
|
||||
new Reporter(AnsiConsole.GetOutput()) :
|
||||
NullReporter;
|
||||
public static Reporter Output { get; private set; }
|
||||
public static Reporter Error { get; private set; }
|
||||
public static Reporter Verbose { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Resets the Reporters to write to the current Console Out/Error.
|
||||
/// </summary>
|
||||
public static void Reset()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
Output = new Reporter(AnsiConsole.GetOutput());
|
||||
Error = new Reporter(AnsiConsole.GetError());
|
||||
Verbose = CommandContext.IsVerbose() ?
|
||||
new Reporter(AnsiConsole.GetOutput()) :
|
||||
NullReporter;
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteLine(string message)
|
||||
{
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.DotNet.Cli.Utils;
|
||||
using NuGet.Frameworks;
|
||||
|
||||
namespace Microsoft.DotNet.Cli.Utils
|
||||
namespace Microsoft.DotNet.Cli
|
||||
{
|
||||
public class DotNetCommandFactory : ICommandFactory
|
||||
{
|
||||
|
@ -14,6 +17,15 @@ namespace Microsoft.DotNet.Cli.Utils
|
|||
NuGetFramework framework = null,
|
||||
string configuration = Constants.DefaultConfiguration)
|
||||
{
|
||||
Func<string[], int> builtInCommand;
|
||||
if (Program.TryGetBuiltInCommand(commandName, out builtInCommand))
|
||||
{
|
||||
Debug.Assert(framework == null, "BuiltInCommand doesn't support the 'framework' argument.");
|
||||
Debug.Assert(configuration == Constants.DefaultConfiguration, "BuiltInCommand doesn't support the 'configuration' argument.");
|
||||
|
||||
return new BuiltInCommand(commandName, args, builtInCommand);
|
||||
}
|
||||
|
||||
return Command.CreateDotNet(commandName, args, framework, configuration);
|
||||
}
|
||||
}
|
|
@ -6,26 +6,37 @@ using System.Collections.Generic;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.DotNet.Cli.Utils;
|
||||
using Microsoft.DotNet.Tools.Test;
|
||||
using Microsoft.Extensions.PlatformAbstractions;
|
||||
using NuGet.Frameworks;
|
||||
using Microsoft.DotNet.ProjectModel.Server;
|
||||
using Microsoft.DotNet.Tools.Build;
|
||||
using Microsoft.DotNet.Tools.Compiler;
|
||||
using Microsoft.DotNet.Tools.Compiler.Csc;
|
||||
using Microsoft.DotNet.Tools.Compiler.Native;
|
||||
using Microsoft.DotNet.Tools.Help;
|
||||
using Microsoft.DotNet.Tools.New;
|
||||
using Microsoft.DotNet.Tools.Publish;
|
||||
using Microsoft.DotNet.Tools.Repl;
|
||||
using Microsoft.DotNet.Tools.Resgen;
|
||||
using Microsoft.DotNet.Tools.Restore;
|
||||
using Microsoft.DotNet.Tools.Run;
|
||||
using Microsoft.DotNet.Tools.Test;
|
||||
using Microsoft.Extensions.PlatformAbstractions;
|
||||
using NuGet.Frameworks;
|
||||
|
||||
namespace Microsoft.DotNet.Cli
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
private static Dictionary<string, Func<string[], int>> s_builtIns = new Dictionary<string, Func<string[], int>>
|
||||
{
|
||||
["build"] = BuildCommand.Run,
|
||||
["compile-csc"] = CompileCscCommand.Run,
|
||||
["help"] = HelpCommand.Run,
|
||||
["new"] = NewCommand.Run,
|
||||
["pack"] = PackCommand.Run,
|
||||
["projectmodel-server"] = ProjectModelServerCommand.Run,
|
||||
["publish"] = PublishCommand.Run,
|
||||
["restore"] = RestoreCommand.Run,
|
||||
["run"] = RunCommand.Run,
|
||||
["test"] = TestCommand.Run
|
||||
};
|
||||
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
DebugHelper.HandleDebugSwitch(ref args);
|
||||
|
@ -100,23 +111,9 @@ namespace Microsoft.DotNet.Cli
|
|||
command = "help";
|
||||
}
|
||||
|
||||
var builtIns = new Dictionary<string, Func<string[], int>>
|
||||
{
|
||||
["build"] = BuildCommand.Run,
|
||||
["compile-csc"] = CompileCscCommand.Run,
|
||||
["help"] = HelpCommand.Run,
|
||||
["new"] = NewCommand.Run,
|
||||
["pack"] = PackCommand.Run,
|
||||
["projectmodel-server"] = ProjectModelServerCommand.Run,
|
||||
["publish"] = PublishCommand.Run,
|
||||
["restore"] = RestoreCommand.Run,
|
||||
["run"] = RunCommand.Run,
|
||||
["test"] = TestCommand.Run
|
||||
};
|
||||
|
||||
int exitCode;
|
||||
Func<string[], int> builtIn;
|
||||
if (builtIns.TryGetValue(command, out builtIn))
|
||||
if (s_builtIns.TryGetValue(command, out builtIn))
|
||||
{
|
||||
exitCode = builtIn(appArgs.ToArray());
|
||||
}
|
||||
|
@ -141,6 +138,11 @@ namespace Microsoft.DotNet.Cli
|
|||
|
||||
}
|
||||
|
||||
internal static bool TryGetBuiltInCommand(string commandName, out Func<string[], int> builtInCommand)
|
||||
{
|
||||
return s_builtIns.TryGetValue(commandName, out builtInCommand);
|
||||
}
|
||||
|
||||
private static void PrintVersion()
|
||||
{
|
||||
Reporter.Output.WriteLine(Product.Version);
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using Microsoft.DotNet.Cli;
|
||||
using Microsoft.DotNet.Cli.Compiler.Common;
|
||||
using Microsoft.DotNet.Cli.Utils;
|
||||
using Microsoft.DotNet.ProjectModel;
|
||||
|
@ -81,7 +84,7 @@ namespace Microsoft.DotNet.Tools.Compiler
|
|||
var compilationOptions = context.ResolveCompilationOptions(args.ConfigValue);
|
||||
|
||||
// Set default platform if it isn't already set and we're on desktop
|
||||
if(compilationOptions.EmitEntryPoint == true && string.IsNullOrEmpty(compilationOptions.Platform) && context.TargetFramework.IsDesktop())
|
||||
if (compilationOptions.EmitEntryPoint == true && string.IsNullOrEmpty(compilationOptions.Platform) && context.TargetFramework.IsDesktop())
|
||||
{
|
||||
// See https://github.com/dotnet/cli/issues/2428 for more details.
|
||||
compilationOptions.Platform = RuntimeInformation.ProcessArchitecture == Architecture.X64 ?
|
||||
|
@ -181,31 +184,15 @@ namespace Microsoft.DotNet.Tools.Compiler
|
|||
|
||||
_scriptRunner.RunScripts(context, ScriptNames.PreCompile, contextVariables);
|
||||
|
||||
var result = _commandFactory.Create($"compile-{compilerName}", new[] { "@" + $"{rsp}" })
|
||||
.OnErrorLine(line =>
|
||||
{
|
||||
var diagnostic = ParseDiagnostic(context.ProjectDirectory, line);
|
||||
if (diagnostic != null)
|
||||
{
|
||||
diagnostics.Add(diagnostic);
|
||||
}
|
||||
else
|
||||
{
|
||||
Reporter.Error.WriteLine(line);
|
||||
}
|
||||
})
|
||||
.OnOutputLine(line =>
|
||||
{
|
||||
var diagnostic = ParseDiagnostic(context.ProjectDirectory, line);
|
||||
if (diagnostic != null)
|
||||
{
|
||||
diagnostics.Add(diagnostic);
|
||||
}
|
||||
else
|
||||
{
|
||||
Reporter.Output.WriteLine(line);
|
||||
}
|
||||
}).Execute();
|
||||
// Cache the reporters before invoking the command in case it is a built-in command, which replaces
|
||||
// the static Reporter instances.
|
||||
Reporter errorReporter = Reporter.Error;
|
||||
Reporter outputReporter = Reporter.Output;
|
||||
|
||||
CommandResult result = _commandFactory.Create($"compile-{compilerName}", new[] { $"@{rsp}" })
|
||||
.OnErrorLine(line => HandleCompilerOutputLine(line, context, diagnostics, errorReporter))
|
||||
.OnOutputLine(line => HandleCompilerOutputLine(line, context, diagnostics, outputReporter))
|
||||
.Execute();
|
||||
|
||||
// Run post-compile event
|
||||
contextVariables["compile:CompilerExitCode"] = result.ExitCode.ToString();
|
||||
|
@ -225,5 +212,18 @@ namespace Microsoft.DotNet.Tools.Compiler
|
|||
|
||||
return PrintSummary(diagnostics, sw, success);
|
||||
}
|
||||
|
||||
private static void HandleCompilerOutputLine(string line, ProjectContext context, List<DiagnosticMessage> diagnostics, Reporter reporter)
|
||||
{
|
||||
var diagnostic = ParseDiagnostic(context.ProjectDirectory, line);
|
||||
if (diagnostic != null)
|
||||
{
|
||||
diagnostics.Add(diagnostic);
|
||||
}
|
||||
else
|
||||
{
|
||||
reporter.WriteLine(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.DotNet.Cli;
|
||||
using Microsoft.DotNet.Cli.Utils;
|
||||
|
||||
namespace Microsoft.DotNet.Tools.Compiler
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.Threading;
|
||||
using Microsoft.DotNet.Tools.Test.Utilities;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.DotNet.Cli.Utils
|
||||
{
|
||||
public class BlockingMemoryStreamTests : TestBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests reading a bigger buffer than what is available.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ReadBiggerBuffer()
|
||||
{
|
||||
using (var stream = new BlockingMemoryStream())
|
||||
{
|
||||
stream.Write(new byte[] { 1, 2, 3 }, 0, 3);
|
||||
|
||||
byte[] buffer = new byte[10];
|
||||
int count = stream.Read(buffer, 0, buffer.Length);
|
||||
Assert.Equal(3, count);
|
||||
Assert.Equal(1, buffer[0]);
|
||||
Assert.Equal(2, buffer[1]);
|
||||
Assert.Equal(3, buffer[2]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests reading smaller buffers than what is available.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ReadSmallerBuffers()
|
||||
{
|
||||
using (var stream = new BlockingMemoryStream())
|
||||
{
|
||||
stream.Write(new byte[] { 1, 2, 3, 4 }, 0, 4);
|
||||
stream.Write(new byte[] { 5, 6, 7, 8, 9 }, 0, 5);
|
||||
|
||||
byte[] buffer = new byte[3];
|
||||
|
||||
int count = stream.Read(buffer, 0, buffer.Length);
|
||||
Assert.Equal(3, count);
|
||||
Assert.Equal(1, buffer[0]);
|
||||
Assert.Equal(2, buffer[1]);
|
||||
Assert.Equal(3, buffer[2]);
|
||||
|
||||
count = stream.Read(buffer, 0, buffer.Length);
|
||||
Assert.Equal(1, count);
|
||||
Assert.Equal(4, buffer[0]);
|
||||
|
||||
count = stream.Read(buffer, 0, buffer.Length);
|
||||
Assert.Equal(3, count);
|
||||
Assert.Equal(5, buffer[0]);
|
||||
Assert.Equal(6, buffer[1]);
|
||||
Assert.Equal(7, buffer[2]);
|
||||
|
||||
count = stream.Read(buffer, 0, buffer.Length);
|
||||
Assert.Equal(2, count);
|
||||
Assert.Equal(8, buffer[0]);
|
||||
Assert.Equal(9, buffer[1]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests reading will block until the stream is written to.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void TestReadBlocksUntilWrite()
|
||||
{
|
||||
using (var stream = new BlockingMemoryStream())
|
||||
{
|
||||
ManualResetEvent readerThreadExecuting = new ManualResetEvent(false);
|
||||
bool readerThreadSuccessful = false;
|
||||
|
||||
Thread readerThread = new Thread(() =>
|
||||
{
|
||||
byte[] buffer = new byte[10];
|
||||
readerThreadExecuting.Set();
|
||||
int count = stream.Read(buffer, 0, buffer.Length);
|
||||
|
||||
Assert.Equal(3, count);
|
||||
Assert.Equal(1, buffer[0]);
|
||||
Assert.Equal(2, buffer[1]);
|
||||
Assert.Equal(3, buffer[2]);
|
||||
|
||||
readerThreadSuccessful = true;
|
||||
});
|
||||
|
||||
readerThread.IsBackground = true;
|
||||
readerThread.Start();
|
||||
|
||||
// ensure the thread is executing
|
||||
readerThreadExecuting.WaitOne();
|
||||
|
||||
Assert.True(readerThread.IsAlive);
|
||||
|
||||
// give it a little while to ensure it is blocking
|
||||
Thread.Sleep(10);
|
||||
Assert.True(readerThread.IsAlive);
|
||||
|
||||
stream.Write(new byte[] { 1, 2, 3 }, 0, 3);
|
||||
|
||||
Assert.True(readerThread.Join(1000));
|
||||
Assert.True(readerThreadSuccessful);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
89
test/Microsoft.DotNet.Cli.Utils.Tests/BuiltInCommandTests.cs
Normal file
89
test/Microsoft.DotNet.Cli.Utils.Tests/BuiltInCommandTests.cs
Normal file
|
@ -0,0 +1,89 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.DotNet.Tools.Test.Utilities;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.DotNet.Cli.Utils
|
||||
{
|
||||
public class BuiltInCommandTests : TestBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests that BuiltInCommand.Execute returns the correct exit code and a
|
||||
/// valid StartInfo FileName and Arguments.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void TestExecute()
|
||||
{
|
||||
Func<string[], int> testCommand = args => args.Length;
|
||||
string[] testCommandArgs = new[] { "1", "2" };
|
||||
|
||||
var builtInCommand = new BuiltInCommand("fakeCommand", testCommandArgs, testCommand);
|
||||
CommandResult result = builtInCommand.Execute();
|
||||
|
||||
Assert.Equal(testCommandArgs.Length, result.ExitCode);
|
||||
Assert.Equal(new Muxer().MuxerPath, result.StartInfo.FileName);
|
||||
Assert.Equal("fakeCommand 1 2", result.StartInfo.Arguments);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that BuiltInCommand.Execute raises the OnOutputLine and OnErrorLine
|
||||
/// the correct number of times and with the correct content.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void TestOnOutputLines()
|
||||
{
|
||||
int exitCode = 29;
|
||||
|
||||
Func<string[], int> testCommand = args =>
|
||||
{
|
||||
Console.Out.Write("first");
|
||||
Console.Out.WriteLine("second");
|
||||
Console.Out.WriteLine("third");
|
||||
|
||||
Console.Error.WriteLine("fourth");
|
||||
Console.Error.WriteLine("fifth");
|
||||
|
||||
return exitCode;
|
||||
};
|
||||
|
||||
int onOutputLineCallCount = 0;
|
||||
int onErrorLineCallCount = 0;
|
||||
|
||||
CommandResult result = new BuiltInCommand("fakeCommand", Enumerable.Empty<string>(), testCommand)
|
||||
.OnOutputLine(line =>
|
||||
{
|
||||
onOutputLineCallCount++;
|
||||
|
||||
if (onOutputLineCallCount == 1)
|
||||
{
|
||||
Assert.Equal($"firstsecond", line);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Equal($"third", line);
|
||||
}
|
||||
})
|
||||
.OnErrorLine(line =>
|
||||
{
|
||||
onErrorLineCallCount++;
|
||||
|
||||
if (onErrorLineCallCount == 1)
|
||||
{
|
||||
Assert.Equal($"fourth", line);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Equal($"fifth", line);
|
||||
}
|
||||
})
|
||||
.Execute();
|
||||
|
||||
Assert.Equal(exitCode, result.ExitCode);
|
||||
Assert.Equal(2, onOutputLineCallCount);
|
||||
Assert.Equal(2, onErrorLineCallCount);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue