2017-11-27 10:45:43 -08:00
// 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.Diagnostics ;
using System.IO ;
using System.Linq ;
using System.Runtime.InteropServices ;
2018-01-18 14:54:10 -08:00
using System.Xml.Linq ;
2017-11-27 10:45:43 -08:00
using FluentAssertions ;
using Microsoft.DotNet.Cli.Utils ;
using Microsoft.DotNet.TestFramework ;
using Microsoft.DotNet.Tools.Test.Utilities ;
using Xunit ;
2018-01-18 14:54:10 -08:00
using Xunit.Abstractions ;
2017-11-27 10:45:43 -08:00
2017-12-04 14:13:24 -08:00
namespace Microsoft.DotNet.ShellShim.Tests
2017-11-27 10:45:43 -08:00
{
public class ShellShimMakerTests : TestBase
{
2018-01-18 14:54:10 -08:00
private readonly ITestOutputHelper _output ;
2017-11-27 10:45:43 -08:00
2018-01-18 14:54:10 -08:00
public ShellShimMakerTests ( ITestOutputHelper output )
2017-11-27 10:45:43 -08:00
{
2018-01-18 14:54:10 -08:00
_output = output ;
}
[Theory]
[InlineData("my_native_app.exe", null)]
[InlineData("./my_native_app.js", "nodejs")]
[InlineData(@"C:\tools\my_native_app.dll", "dotnet")]
public void GivenAnRunnerOrEntryPointItCanCreateConfig ( string entryPoint , string runner )
{
if ( ! RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) )
return ;
var shellShimMaker = new ShellShimMaker ( TempRoot . Root ) ;
var tmpFile = Path . Combine ( TempRoot . Root , Path . GetRandomFileName ( ) ) ;
shellShimMaker . CreateConfigFile ( tmpFile , entryPoint , runner ) ;
new FileInfo ( tmpFile ) . Should ( ) . Exist ( ) ;
var generated = XDocument . Load ( tmpFile ) ;
generated . Descendants ( "appSettings" )
. Descendants ( "add" )
. Should ( )
. Contain ( e = > e . Attribute ( "key" ) . Value = = "runner" & & e . Attribute ( "value" ) . Value = = ( runner ? ? string . Empty ) )
. And
. Contain ( e = > e . Attribute ( "key" ) . Value = = "entryPoint" & & e . Attribute ( "value" ) . Value = = entryPoint ) ;
2017-11-27 10:45:43 -08:00
}
[Fact]
public void GivenAnExecutablePathItCanGenerateShimFile ( )
{
var outputDll = MakeHelloWorldExecutableDll ( ) ;
2018-01-18 14:54:10 -08:00
var shellShimMaker = new ShellShimMaker ( TempRoot . Root ) ;
2017-11-27 10:45:43 -08:00
var shellCommandName = nameof ( ShellShimMakerTests ) + Path . GetRandomFileName ( ) ;
shellShimMaker . CreateShim (
outputDll . FullName ,
shellCommandName ) ;
var stdOut = ExecuteInShell ( shellCommandName ) ;
stdOut . Should ( ) . Contain ( "Hello World" ) ;
}
2018-01-18 14:54:10 -08:00
[Theory]
[InlineData("arg1 arg2", new[] { "arg1" , "arg2" } ) ]
[InlineData(" \"arg1 with space\" arg2", new[] { "arg1 with space" , "arg2" } ) ]
[InlineData(" \"arg with ' quote\" ", new[] { "arg with ' quote" } ) ]
public void GivenAShimItPassesThroughArguments ( string arguments , string [ ] expectedPassThru )
{
var outputDll = MakeHelloWorldExecutableDll ( ) ;
var shellShimMaker = new ShellShimMaker ( TempRoot . Root ) ;
var shellCommandName = nameof ( ShellShimMakerTests ) + Path . GetRandomFileName ( ) ;
shellShimMaker . CreateShim (
outputDll . FullName ,
shellCommandName ) ;
var stdOut = ExecuteInShell ( shellCommandName , arguments ) ;
for ( int i = 0 ; i < expectedPassThru . Length ; i + + )
{
stdOut . Should ( ) . Contain ( $"{i} = {expectedPassThru[i]}" ) ;
}
}
2017-11-27 10:45:43 -08:00
[Fact]
public void GivenAnExecutablePathWithExistingSameNameShimItThrows ( )
{
var shellCommandName = nameof ( ShellShimMakerTests ) + Path . GetRandomFileName ( ) ;
2018-01-18 14:54:10 -08:00
MakeNameConflictingCommand ( TempRoot . Root , shellCommandName ) ;
2017-11-27 10:45:43 -08:00
2018-01-18 14:54:10 -08:00
var shellShimMaker = new ShellShimMaker ( TempRoot . Root ) ;
2017-11-27 10:45:43 -08:00
Action a = ( ) = > shellShimMaker . EnsureCommandNameUniqueness ( shellCommandName ) ;
a . ShouldThrow < GracefulException > ( )
. And . Message
. Should ( ) . Contain (
$"Failed to install tool {shellCommandName}. A command with the same name already exists." ) ;
}
[Fact]
public void GivenAnExecutablePathWithoutExistingSameNameShimItShouldNotThrow ( )
{
var shellCommandName = nameof ( ShellShimMakerTests ) + Path . GetRandomFileName ( ) ;
2018-01-18 14:54:10 -08:00
var shellShimMaker = new ShellShimMaker ( TempRoot . Root ) ;
2017-11-27 10:45:43 -08:00
Action a = ( ) = > shellShimMaker . EnsureCommandNameUniqueness ( shellCommandName ) ;
a . ShouldNotThrow ( ) ;
}
private static void MakeNameConflictingCommand ( string pathToPlaceShim , string shellCommandName )
{
File . WriteAllText ( Path . Combine ( pathToPlaceShim , shellCommandName ) , string . Empty ) ;
}
2018-01-18 14:54:10 -08:00
private string ExecuteInShell ( string shellCommandName , string arguments = "" )
2017-11-27 10:45:43 -08:00
{
ProcessStartInfo processStartInfo ;
if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) )
{
2018-01-18 14:54:10 -08:00
var file = Path . Combine ( TempRoot . Root , shellCommandName + ".exe" ) ;
2017-11-27 10:45:43 -08:00
processStartInfo = new ProcessStartInfo
{
2018-01-18 14:54:10 -08:00
FileName = file ,
UseShellExecute = false ,
Arguments = arguments ,
2017-11-27 10:45:43 -08:00
} ;
}
else
{
processStartInfo = new ProcessStartInfo
{
FileName = "sh" ,
2018-01-18 14:54:10 -08:00
Arguments = shellCommandName + " " + arguments ,
2017-11-27 10:45:43 -08:00
UseShellExecute = false
} ;
}
2018-01-18 14:54:10 -08:00
_output . WriteLine ( $"Launching '{processStartInfo.FileName} {processStartInfo.Arguments}'" ) ;
processStartInfo . WorkingDirectory = TempRoot . Root ;
2017-11-27 10:45:43 -08:00
processStartInfo . EnvironmentVariables [ "PATH" ] = Path . GetDirectoryName ( new Muxer ( ) . MuxerPath ) ;
processStartInfo . ExecuteAndCaptureOutput ( out var stdOut , out var stdErr ) ;
stdErr . Should ( ) . BeEmpty ( ) ;
return stdOut ? ? "" ;
}
private static FileInfo MakeHelloWorldExecutableDll ( )
{
const string testAppName = "TestAppSimple" ;
const string emptySpaceToTestSpaceInPath = " " ;
TestAssetInstance testInstance = TestAssets . Get ( testAppName )
. CreateInstance ( testAppName + emptySpaceToTestSpaceInPath )
. UseCurrentRuntimeFrameworkVersion ( )
. WithRestoreFiles ( )
. WithBuildFiles ( ) ;
var configuration = Environment . GetEnvironmentVariable ( "CONFIGURATION" ) ? ? "Debug" ;
FileInfo outputDll = testInstance . Root . GetDirectory ( "bin" , configuration )
. GetDirectories ( ) . Single ( )
. GetFile ( $"{testAppName}.dll" ) ;
return outputDll ;
}
}
}