2016-10-27 18:46:43 -07:00
// Copyright (c) .NET Foundation and contributors. All rights reserved.
2015-12-14 17:39:29 -08:00
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using Microsoft.DotNet.Cli.Utils ;
using System ;
2016-01-22 14:05:02 -08:00
using System.Diagnostics ;
2016-01-22 00:42:33 -08:00
using System.IO ;
2016-02-18 01:09:23 -08:00
using System.Collections.Generic ;
2016-04-04 17:51:36 -07:00
using System.Runtime.InteropServices ;
using System.Threading.Tasks ;
2015-12-14 17:39:29 -08:00
namespace Microsoft.DotNet.Tools.Test.Utilities
{
public class TestCommand
{
protected string _command ;
2016-04-27 15:43:12 -05:00
2016-04-21 13:41:22 -07:00
private string _baseDirectory ;
2016-02-18 01:09:23 -08:00
2016-03-28 03:18:13 -07:00
public string WorkingDirectory { get ; set ; }
2016-04-04 17:51:36 -07:00
public Process CurrentProcess { get ; set ; }
2016-02-18 01:09:23 -08:00
public Dictionary < string , string > Environment { get ; } = new Dictionary < string , string > ( ) ;
2016-10-27 18:46:43 -07:00
private List < Action < string > > _writeLines = new List < Action < string > > ( ) ;
2015-12-14 17:39:29 -08:00
public TestCommand ( string command )
{
_command = command ;
2016-04-21 13:41:22 -07:00
#if NET451
_baseDirectory = AppDomain . CurrentDomain . BaseDirectory ;
#else
_baseDirectory = AppContext . BaseDirectory ;
#endif
2015-12-14 17:39:29 -08:00
}
2016-01-08 11:03:14 -08:00
public virtual CommandResult Execute ( string args = "" )
2015-12-14 17:39:29 -08:00
{
2016-01-22 00:42:33 -08:00
var commandPath = _command ;
2016-03-15 11:50:14 -07:00
ResolveCommand ( ref commandPath , ref args ) ;
2015-12-14 17:39:29 -08:00
2016-10-27 18:46:43 -07:00
Console . WriteLine ( $"Executing - {commandPath} {args} - {WorkingDirectoryInfo()}" ) ;
2016-01-22 14:05:02 -08:00
var stdOut = new StreamForwarder ( ) ;
var stdErr = new StreamForwarder ( ) ;
2016-10-27 18:46:43 -07:00
AddWriteLine ( Reporter . Output . WriteLine ) ;
stdOut . ForwardTo ( writeLine : WriteLine ) ;
stdErr . ForwardTo ( writeLine : WriteLine ) ;
2016-01-22 14:05:02 -08:00
return RunProcess ( commandPath , args , stdOut , stdErr ) ;
2015-12-14 17:39:29 -08:00
}
2015-12-30 17:02:59 -08:00
2016-04-04 17:51:36 -07:00
public virtual Task < CommandResult > ExecuteAsync ( string args = "" )
{
var commandPath = _command ;
ResolveCommand ( ref commandPath , ref args ) ;
2016-10-27 18:46:43 -07:00
Console . WriteLine ( $"Executing - {commandPath} {args} - {WorkingDirectoryInfo()}" ) ;
2016-04-04 17:51:36 -07:00
var stdOut = new StreamForwarder ( ) ;
var stdErr = new StreamForwarder ( ) ;
2016-10-27 18:46:43 -07:00
AddWriteLine ( Reporter . Output . WriteLine ) ;
stdOut . ForwardTo ( writeLine : WriteLine ) ;
stdErr . ForwardTo ( writeLine : WriteLine ) ;
2016-04-04 17:51:36 -07:00
return RunProcessAsync ( commandPath , args , stdOut , stdErr ) ;
}
2016-01-08 11:03:14 -08:00
public virtual CommandResult ExecuteWithCapturedOutput ( string args = "" )
2015-12-30 17:02:59 -08:00
{
2016-03-15 11:50:14 -07:00
var command = _command ;
ResolveCommand ( ref command , ref args ) ;
var commandPath = Env . GetCommandPath ( command , ".exe" , ".cmd" , "" ) ? ?
2016-04-21 13:41:22 -07:00
Env . GetCommandPathFromRootPath ( _baseDirectory , command , ".exe" , ".cmd" , "" ) ;
2016-04-04 17:51:36 -07:00
2016-10-27 18:46:43 -07:00
Console . WriteLine ( $"Executing (Captured Output) - {commandPath} {args} - {WorkingDirectoryInfo()}" ) ;
2016-03-15 11:50:14 -07:00
2016-01-22 14:05:02 -08:00
var stdOut = new StreamForwarder ( ) ;
var stdErr = new StreamForwarder ( ) ;
2016-10-27 18:46:43 -07:00
stdOut . ForwardTo ( writeLine : WriteLine ) ;
stdErr . ForwardTo ( writeLine : WriteLine ) ;
2016-01-22 14:05:02 -08:00
stdOut . Capture ( ) ;
stdErr . Capture ( ) ;
return RunProcess ( commandPath , args , stdOut , stdErr ) ;
}
2016-04-04 17:51:36 -07:00
public void KillTree ( )
{
if ( CurrentProcess = = null )
{
throw new InvalidOperationException ( "No process is available to be killed" ) ;
}
CurrentProcess . KillTree ( ) ;
}
2016-10-27 18:46:43 -07:00
public void AddWriteLine ( Action < string > writeLine )
{
_writeLines . Add ( writeLine ) ;
}
2016-03-15 11:50:14 -07:00
private void ResolveCommand ( ref string executable , ref string args )
{
if ( executable . EndsWith ( ".dll" , StringComparison . OrdinalIgnoreCase ) )
{
var newArgs = ArgumentEscaper . EscapeSingleArg ( executable ) ;
if ( ! string . IsNullOrEmpty ( args ) )
{
newArgs + = " " + args ;
}
args = newArgs ;
2016-03-15 16:41:18 -07:00
executable = "dotnet" ;
2016-03-15 11:50:14 -07:00
}
if ( ! Path . IsPathRooted ( executable ) )
{
executable = Env . GetCommandPath ( executable ) ? ?
2016-04-21 13:41:22 -07:00
Env . GetCommandPathFromRootPath ( _baseDirectory , executable ) ;
2016-03-15 11:50:14 -07:00
}
}
2016-01-22 14:05:02 -08:00
private CommandResult RunProcess ( string executable , string args , StreamForwarder stdOut , StreamForwarder stdErr )
2016-04-04 17:51:36 -07:00
{
CurrentProcess = StartProcess ( executable , args ) ;
2016-04-22 15:01:56 -07:00
var taskOut = stdOut . BeginRead ( CurrentProcess . StandardOutput ) ;
var taskErr = stdErr . BeginRead ( CurrentProcess . StandardError ) ;
2016-04-04 17:51:36 -07:00
CurrentProcess . WaitForExit ( ) ;
2016-04-22 15:01:56 -07:00
Task . WaitAll ( taskOut , taskErr ) ;
2016-04-04 17:51:36 -07:00
var result = new CommandResult (
CurrentProcess . StartInfo ,
CurrentProcess . ExitCode ,
stdOut . CapturedOutput ,
stdErr . CapturedOutput ) ;
return result ;
}
private Task < CommandResult > RunProcessAsync ( string executable , string args , StreamForwarder stdOut , StreamForwarder stdErr )
{
CurrentProcess = StartProcess ( executable , args ) ;
2016-04-22 15:01:56 -07:00
var taskOut = stdOut . BeginRead ( CurrentProcess . StandardOutput ) ;
var taskErr = stdErr . BeginRead ( CurrentProcess . StandardError ) ;
2016-04-04 17:51:36 -07:00
var tcs = new TaskCompletionSource < CommandResult > ( ) ;
CurrentProcess . Exited + = ( sender , arg ) = >
{
2016-04-22 15:01:56 -07:00
Task . WaitAll ( taskOut , taskErr ) ;
2016-04-04 17:51:36 -07:00
var result = new CommandResult (
CurrentProcess . StartInfo ,
CurrentProcess . ExitCode ,
stdOut . CapturedOutput ,
stdErr . CapturedOutput ) ;
tcs . SetResult ( result ) ;
} ;
return tcs . Task ;
}
private Process StartProcess ( string executable , string args )
2016-01-22 14:05:02 -08:00
{
var psi = new ProcessStartInfo
{
FileName = executable ,
Arguments = args ,
RedirectStandardError = true ,
2016-04-04 17:51:36 -07:00
RedirectStandardOutput = true ,
2016-04-21 13:41:22 -07:00
RedirectStandardInput = true ,
UseShellExecute = false
2016-01-22 14:05:02 -08:00
} ;
2016-02-18 01:09:23 -08:00
foreach ( var item in Environment )
{
2016-04-21 13:41:22 -07:00
#if NET451
psi . EnvironmentVariables [ item . Key ] = item . Value ;
#else
2016-02-18 01:09:23 -08:00
psi . Environment [ item . Key ] = item . Value ;
2016-04-21 13:41:22 -07:00
#endif
2016-02-18 01:09:23 -08:00
}
2016-03-28 03:18:13 -07:00
if ( ! string . IsNullOrWhiteSpace ( WorkingDirectory ) )
{
psi . WorkingDirectory = WorkingDirectory ;
}
2016-01-22 14:05:02 -08:00
var process = new Process
{
2016-03-28 03:18:13 -07:00
StartInfo = psi
2016-01-22 14:05:02 -08:00
} ;
process . EnableRaisingEvents = true ;
process . Start ( ) ;
2016-04-04 17:51:36 -07:00
return process ;
2015-12-30 17:02:59 -08:00
}
2016-10-27 18:46:43 -07:00
private void WriteLine ( string line )
2016-04-22 12:17:36 -07:00
{
2016-10-27 18:46:43 -07:00
foreach ( var writeLine in _writeLines )
{
writeLine ( line ) ;
}
}
private string WorkingDirectoryInfo ( )
{
if ( WorkingDirectory = = null )
{
return "" ;
}
return $" in pwd {WorkingDirectory}" ;
2016-04-22 12:17:36 -07:00
}
2015-12-14 17:39:29 -08:00
}
2016-10-27 18:46:43 -07:00
}