2016-02-10 13:16:23 -06: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 ;
2016-04-06 15:29:10 -07:00
using System.Collections.Generic ;
2016-12-13 14:15:35 -08:00
using System.Diagnostics ;
2016-02-10 13:16:23 -06:00
using System.IO ;
2016-04-28 16:30:32 -07:00
using System.Runtime.InteropServices ;
using FluentAssertions ;
2016-02-10 13:16:23 -06:00
using Microsoft.DotNet.Cli.Utils ;
2016-08-09 23:30:12 -07:00
using Microsoft.DotNet.TestFramework ;
2016-02-10 13:16:23 -06:00
using Microsoft.DotNet.Tools.Test.Utilities ;
2016-04-28 16:30:32 -07:00
using Microsoft.DotNet.InternalAbstractions ;
2016-02-10 13:16:23 -06:00
using Xunit ;
2016-10-27 18:46:43 -07:00
using Xunit.Abstractions ;
2016-12-20 12:15:33 -08:00
using Microsoft.Build.Construction ;
using System.Linq ;
using Microsoft.Build.Evaluation ;
2016-02-10 13:16:23 -06:00
namespace Microsoft.DotNet.Tests
{
public class PackagedCommandTests : TestBase
{
2016-10-27 18:46:43 -07:00
private readonly ITestOutputHelper _output ;
public PackagedCommandTests ( ITestOutputHelper output )
{
_output = output ;
}
2016-02-10 13:16:23 -06:00
2016-05-08 14:20:34 -07:00
public static IEnumerable < object [ ] > DependencyToolArguments
{
get
{
2016-10-10 09:52:39 -07:00
var rid = DotnetLegacyRuntimeIdentifiers . InferLegacyRestoreRuntimeIdentifier ( ) ;
2016-10-27 18:46:43 -07:00
var projectOutputPath = $"AppWithProjTool2Fx\\bin\\Debug\\net451\\{rid}\\dotnet-desktop-and-portable.exe" ;
2016-05-08 14:20:34 -07:00
return new [ ]
{
2016-08-10 03:12:03 -07:00
new object [ ] { "CoreFX" , ".NETCoreApp,Version=v1.0" , "lib\\netcoreapp1.0\\dotnet-desktop-and-portable.dll" , true } ,
new object [ ] { "NetFX" , ".NETFramework,Version=v4.5.1" , projectOutputPath , true }
2016-05-08 14:20:34 -07:00
} ;
}
}
public static IEnumerable < object [ ] > LibraryDependencyToolArguments
{
get
{
2016-10-10 09:52:39 -07:00
var rid = DotnetLegacyRuntimeIdentifiers . InferLegacyRestoreRuntimeIdentifier ( ) ;
2016-10-27 18:46:43 -07:00
var projectOutputPath = $"LibWithProjTool2Fx\\bin\\Debug\\net451\\dotnet-desktop-and-portable.exe" ;
2016-05-08 14:20:34 -07:00
return new [ ]
{
2016-08-10 03:12:03 -07:00
new object [ ] { "CoreFX" , ".NETStandard,Version=v1.6" , "lib\\netstandard1.6\\dotnet-desktop-and-portable.dll" , true } ,
new object [ ] { "NetFX" , ".NETFramework,Version=v4.5.1" , projectOutputPath , true }
2016-05-08 14:20:34 -07:00
} ;
}
}
2016-02-10 13:16:23 -06:00
[Theory]
2016-08-18 10:17:58 -07:00
[InlineData("AppWithDirectAndToolDep")]
2016-02-10 13:16:23 -06:00
[InlineData("AppWithToolDependency")]
2016-03-03 15:31:04 -08:00
public void TestProjectToolIsAvailableThroughDriver ( string appName )
2016-02-10 13:16:23 -06:00
{
2016-10-27 18:46:43 -07:00
var testInstance = TestAssets . Get ( appName )
2016-10-31 16:16:07 -07:00
. CreateInstance ( )
2016-10-27 18:46:43 -07:00
. WithSourceFiles ( )
. WithRestoreFiles ( ) ;
2016-08-09 23:30:12 -07:00
2016-10-27 18:46:43 -07:00
new BuildCommand ( )
. WithProjectDirectory ( testInstance . Root )
2016-02-10 13:16:23 -06:00
. Execute ( )
2016-10-27 18:46:43 -07:00
. Should ( ) . Pass ( ) ;
new PortableCommand ( )
. WithWorkingDirectory ( testInstance . Root )
. ExecuteWithCapturedOutput ( )
2016-12-13 14:15:35 -08:00
. Should ( ) . HaveStdOutContaining ( "Hello Portable World!" )
2016-10-27 18:46:43 -07:00
. And . NotHaveStdErr ( )
. And . Pass ( ) ;
2016-02-10 13:16:23 -06:00
}
2016-04-20 16:05:53 -07:00
[Fact]
public void CanInvokeToolWhosePackageNameIsDifferentFromDllName ( )
{
2016-10-27 18:46:43 -07:00
var testInstance = TestAssets . Get ( "AppWithDepOnToolWithOutputName" )
. CreateInstance ( )
. WithSourceFiles ( )
. WithRestoreFiles ( ) ;
2016-08-09 23:30:12 -07:00
2016-10-27 18:46:43 -07:00
new BuildCommand ( )
. WithProjectDirectory ( testInstance . Root )
2016-04-20 16:05:53 -07:00
. Execute ( )
2016-10-27 18:46:43 -07:00
. Should ( ) . Pass ( ) ;
new GenericCommand ( "tool-with-output-name" )
. WithWorkingDirectory ( testInstance . Root )
. ExecuteWithCapturedOutput ( )
. Should ( ) . HaveStdOutContaining ( "Tool with output name!" )
. And . NotHaveStdErr ( )
. And . Pass ( ) ;
2016-04-20 16:05:53 -07:00
}
[Fact]
public void CanInvokeToolFromDirectDependenciesIfPackageNameDifferentFromToolName ( )
{
2016-10-27 18:46:43 -07:00
var testInstance = TestAssets . Get ( "AppWithDirectDepWithOutputName" )
. CreateInstance ( )
. WithSourceFiles ( )
. WithRestoreFiles ( ) ;
2016-04-20 16:05:53 -07:00
const string framework = ".NETCoreApp,Version=v1.0" ;
2016-10-27 18:46:43 -07:00
new BuildCommand ( )
. WithProjectDirectory ( testInstance . Root )
. WithConfiguration ( "Debug" )
2016-04-20 16:05:53 -07:00
. Execute ( )
2016-10-27 18:46:43 -07:00
. Should ( ) . Pass ( ) ;
new DependencyToolInvokerCommand ( )
. WithWorkingDirectory ( testInstance . Root )
. WithEnvironmentVariable ( CommandContext . Variables . Verbose , "true" )
. ExecuteWithCapturedOutput ( $"tool-with-output-name" , framework , "" )
. Should ( ) . HaveStdOutContaining ( "Tool with output name!" )
. And . NotHaveStdErr ( )
. And . Pass ( ) ;
2016-04-20 16:05:53 -07:00
}
2016-12-19 14:00:25 -08:00
[Fact]
public void ItShowsErrorWhenToolIsNotRestored ( )
{
var testInstance = TestAssets . Get ( "NonRestoredTestProjects" , "AppWithNonExistingToolDependency" )
. CreateInstance ( )
. WithSourceFiles ( ) ;
new TestCommand ( "dotnet" )
. WithWorkingDirectory ( testInstance . Root )
. ExecuteWithCapturedOutput ( "nonexistingtool" )
. Should ( ) . Fail ( )
2016-12-20 14:47:11 -08:00
. And . HaveStdErrContaining ( "No executable found matching command \"dotnet-nonexistingtool\"" ) ;
2016-12-19 14:00:25 -08:00
}
2016-12-20 12:15:33 -08:00
[Fact]
public void ItRunsToolRestoredToSpecificPackageDir ( )
{
var testInstance = TestAssets . Get ( "NonRestoredTestProjects" , "ToolWithRandomPackageName" )
. CreateInstance ( )
. WithSourceFiles ( ) ;
var appWithDepOnToolDir = testInstance . Root . Sub ( "AppWithDepOnTool" ) ;
var toolWithRandPkgNameDir = testInstance . Root . Sub ( "ToolWithRandomPackageName" ) ;
var pkgsDir = testInstance . Root . CreateSubdirectory ( "pkgs" ) ;
2016-12-20 13:53:21 -08:00
// 3ebdd4f1-a194-470a-b01a-4515672791d1
// ^-- index = 24
string randomPackageName = Guid . NewGuid ( ) . ToString ( ) . Substring ( 24 ) ;
2016-12-20 12:15:33 -08:00
// TODO: This is a workround for https://github.com/dotnet/cli/issues/5020
SetGeneratedPackageName ( appWithDepOnToolDir . GetFile ( "AppWithDepOnTool.csproj" ) ,
randomPackageName ) ;
SetGeneratedPackageName ( toolWithRandPkgNameDir . GetFile ( "ToolWithRandomPackageName.csproj" ) ,
randomPackageName ) ;
new RestoreCommand ( )
. WithWorkingDirectory ( toolWithRandPkgNameDir )
2016-12-20 13:53:21 -08:00
. Execute ( )
. Should ( ) . Pass ( ) ;
2016-12-20 12:15:33 -08:00
new PackCommand ( )
. WithWorkingDirectory ( toolWithRandPkgNameDir )
2016-12-20 13:53:21 -08:00
. Execute ( $"-o \" { pkgsDir . FullName } \ "" )
. Should ( ) . Pass ( ) ;
2016-12-20 12:15:33 -08:00
new RestoreCommand ( )
. WithWorkingDirectory ( appWithDepOnToolDir )
2016-12-20 13:53:21 -08:00
. Execute ( $"--packages \" { pkgsDir . FullName } \ "" )
. Should ( ) . Pass ( ) ;
2016-12-20 12:15:33 -08:00
new TestCommand ( "dotnet" )
. WithWorkingDirectory ( appWithDepOnToolDir )
. ExecuteWithCapturedOutput ( "randompackage" )
. Should ( ) . Pass ( )
. And . HaveStdOutContaining ( "Hello World from tool!" )
. And . NotHaveStdErr ( ) ;
}
2016-03-28 01:35:25 -07:00
// need conditional theories so we can skip on non-Windows
2016-10-27 18:46:43 -07:00
//[Theory(Skip="https://github.com/dotnet/cli/issues/4514")]
//[MemberData("DependencyToolArguments")]
2016-08-10 03:12:03 -07:00
public void TestFrameworkSpecificDependencyToolsCanBeInvoked ( string identifier , string framework , string expectedDependencyToolPath , bool windowsOnly )
2016-03-28 01:15:09 -07:00
{
2016-05-08 14:20:34 -07:00
if ( ! RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) & & windowsOnly )
2016-03-28 01:35:25 -07:00
{
return ;
}
2016-04-08 15:33:32 -07:00
2016-10-27 18:46:43 -07:00
var testInstance = TestAssets . Get ( TestAssetKinds . DesktopTestProjects , "AppWithProjTool2Fx" )
. CreateInstance ( identifier : identifier )
. WithSourceFiles ( )
. WithRestoreFiles ( ) ;
2016-03-28 01:15:09 -07:00
2016-10-27 18:46:43 -07:00
new BuildCommand ( )
. WithWorkingDirectory ( testInstance . Root )
. WithConfiguration ( "Debug" )
2016-03-28 01:15:09 -07:00
. Execute ( )
2016-10-27 18:46:43 -07:00
. Should ( ) . Pass ( ) ;
new DependencyToolInvokerCommand ( )
. WithWorkingDirectory ( testInstance . Root )
. ExecuteWithCapturedOutput ( $"desktop-and-portable {framework} {identifier}" )
. Should ( ) . HaveStdOutContaining ( framework )
. And . HaveStdOutContaining ( identifier )
. And . HaveStdOutContaining ( expectedDependencyToolPath )
. And . NotHaveStdErr ( )
. And . Pass ( ) ;
2016-03-28 01:15:09 -07:00
}
2016-05-08 14:20:34 -07:00
[Theory]
[MemberData("LibraryDependencyToolArguments")]
2016-08-10 03:12:03 -07:00
public void TestFrameworkSpecificLibraryDependencyToolsCannotBeInvoked ( string identifier , string framework , string expectedDependencyToolPath , bool windowsOnly )
2016-05-08 14:20:34 -07:00
{
if ( ! RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) & & windowsOnly )
{
return ;
}
2016-10-27 18:46:43 -07:00
var testInstance = TestAssets . Get ( TestAssetKinds . DesktopTestProjects , "LibWithProjTool2Fx" )
. CreateInstance ( identifier : identifier )
. WithSourceFiles ( )
. WithRestoreFiles ( ) ;
2016-05-08 14:20:34 -07:00
2016-10-27 18:46:43 -07:00
new BuildCommand ( )
. WithWorkingDirectory ( testInstance . Root )
. WithConfiguration ( "Debug" )
2016-05-08 14:20:34 -07:00
. Execute ( )
2016-10-27 18:46:43 -07:00
. Should ( ) . Pass ( ) ;
2016-05-08 14:20:34 -07:00
2016-10-27 18:46:43 -07:00
new DependencyToolInvokerCommand ( )
. WithWorkingDirectory ( testInstance . Root )
. ExecuteWithCapturedOutput ( $"desktop-and-portable {framework} {identifier}" )
. Should ( ) . Fail ( ) ;
2016-05-08 14:20:34 -07:00
}
2016-04-08 15:33:32 -07:00
[Fact]
public void ToolsCanAccessDependencyContextProperly ( )
{
2016-10-27 18:46:43 -07:00
var testInstance = TestAssets . Get ( "DependencyContextFromTool" )
. CreateInstance ( )
. WithSourceFiles ( )
. WithRestoreFiles ( ) ;
new DependencyContextTestCommand ( )
. WithWorkingDirectory ( testInstance . Root )
. Execute ( "" )
. Should ( ) . Pass ( ) ;
2016-04-08 15:33:32 -07:00
}
2016-03-03 15:31:04 -08:00
[Fact]
public void TestProjectDependencyIsNotAvailableThroughDriver ( )
{
2016-10-27 18:46:43 -07:00
var testInstance = TestAssets . Get ( "AppWithDirectDep" )
. CreateInstance ( )
. WithSourceFiles ( )
. WithRestoreFiles ( ) ;
new BuildCommand ( )
. WithWorkingDirectory ( testInstance . Root )
. WithFramework ( NuGet . Frameworks . FrameworkConstants . CommonFrameworks . NetCoreApp10 )
2016-03-03 15:31:04 -08:00
. Execute ( )
2016-10-27 18:46:43 -07:00
. Should ( ) . Pass ( ) ;
2016-03-03 15:31:04 -08:00
var currentDirectory = Directory . GetCurrentDirectory ( ) ;
2016-10-27 18:46:43 -07:00
CommandResult result = new HelloCommand ( )
. WithWorkingDirectory ( testInstance . Root )
. ExecuteWithCapturedOutput ( ) ;
2016-03-03 15:31:04 -08:00
2016-10-27 18:46:43 -07:00
result . StdErr . Should ( ) . Contain ( "No executable found matching command" ) ;
result . Should ( ) . Fail ( ) ;
2016-03-03 15:31:04 -08:00
}
2016-11-08 23:23:13 -08:00
[Fact]
2016-12-13 14:15:35 -08:00
public void WhenToolAssetsFileIsInUseThenCLIRetriesLaunchingTheCommandForAtLeastOneSecond ( )
2016-11-08 23:23:13 -08:00
{
var testInstance = TestAssets . Get ( "AppWithToolDependency" )
. CreateInstance ( )
. WithSourceFiles ( )
. WithRestoreFiles ( ) ;
2016-12-13 14:15:35 -08:00
var assetsFile = new DirectoryInfo ( new RepoDirectoriesProvider ( ) . NugetPackages )
. GetDirectory ( ".tools" , "dotnet-portable" , "1.0.0" , "netcoreapp1.0" )
. GetFile ( "project.assets.json" ) ;
var stopWatch = Stopwatch . StartNew ( ) ;
2016-11-08 23:23:13 -08:00
using ( assetsFile . Lock ( )
. DisposeAfter ( TimeSpan . FromMilliseconds ( 1000 ) ) )
{
new PortableCommand ( )
. WithWorkingDirectory ( testInstance . Root )
. ExecuteWithCapturedOutput ( )
2016-12-13 14:15:35 -08:00
. Should ( ) . HaveStdOutContaining ( "Hello Portable World!" )
2016-11-08 23:23:13 -08:00
. And . NotHaveStdErr ( )
. And . Pass ( ) ;
}
2016-12-13 14:15:35 -08:00
stopWatch . Stop ( ) ;
stopWatch . ElapsedMilliseconds . Should ( ) . BeGreaterThan ( 1000 , "Because dotnet should respect the NuGet lock" ) ;
2016-11-08 23:23:13 -08:00
}
[Fact]
2016-12-13 14:15:35 -08:00
public void WhenToolAssetsFileIsLockedByNuGetThenCLIRetriesLaunchingTheCommandForAtLeastOneSecond ( )
2016-11-08 23:23:13 -08:00
{
var testInstance = TestAssets . Get ( "AppWithToolDependency" )
. CreateInstance ( )
. WithSourceFiles ( )
. WithRestoreFiles ( ) ;
2016-12-13 14:15:35 -08:00
var assetsFile = new DirectoryInfo ( new RepoDirectoriesProvider ( ) . NugetPackages )
. GetDirectory ( ".tools" , "dotnet-portable" , "1.0.0" , "netcoreapp1.0" )
. GetFile ( "project.assets.json" ) ;
var stopWatch = Stopwatch . StartNew ( ) ;
2016-11-08 23:23:13 -08:00
using ( assetsFile . NuGetLock ( )
. DisposeAfter ( TimeSpan . FromMilliseconds ( 1000 ) ) )
{
new PortableCommand ( )
. WithWorkingDirectory ( testInstance . Root )
. ExecuteWithCapturedOutput ( )
2016-12-13 14:15:35 -08:00
. Should ( ) . HaveStdOutContaining ( "Hello Portable World!" )
2016-11-08 23:23:13 -08:00
. And . NotHaveStdErr ( )
. And . Pass ( ) ;
}
2016-12-13 14:15:35 -08:00
stopWatch . Stop ( ) ;
stopWatch . ElapsedMilliseconds . Should ( ) . BeGreaterThan ( 1000 , "Because dotnet should respect the NuGet lock" ) ;
2016-11-08 23:23:13 -08:00
}
2016-12-20 12:15:33 -08:00
private void SetGeneratedPackageName ( FileInfo project , string packageName )
{
const string propertyName = "GeneratedPackageId" ;
var p = ProjectRootElement . Open ( project . FullName , new ProjectCollection ( ) , true ) ;
p . AddProperty ( propertyName , packageName ) ;
p . Save ( ) ;
}
2016-02-10 13:16:23 -06:00
class HelloCommand : TestCommand
{
public HelloCommand ( )
: base ( "dotnet" )
{
}
public override CommandResult Execute ( string args = "" )
{
args = $"hello {args}" ;
return base . Execute ( args ) ;
}
public override CommandResult ExecuteWithCapturedOutput ( string args = "" )
{
args = $"hello {args}" ;
return base . ExecuteWithCapturedOutput ( args ) ;
}
}
2016-03-17 11:43:17 -07:00
class PortableCommand : TestCommand
{
public PortableCommand ( )
: base ( "dotnet" )
{
}
public override CommandResult Execute ( string args = "" )
{
args = $"portable {args}" ;
return base . Execute ( args ) ;
}
public override CommandResult ExecuteWithCapturedOutput ( string args = "" )
{
args = $"portable {args}" ;
return base . ExecuteWithCapturedOutput ( args ) ;
}
}
2016-03-28 01:15:09 -07:00
2016-04-20 16:05:53 -07:00
class GenericCommand : TestCommand
{
private readonly string _commandName ;
public GenericCommand ( string commandName )
: base ( "dotnet" )
{
_commandName = commandName ;
}
public override CommandResult Execute ( string args = "" )
{
args = $"{_commandName} {args}" ;
return base . Execute ( args ) ;
}
public override CommandResult ExecuteWithCapturedOutput ( string args = "" )
{
args = $"{_commandName} {args}" ;
return base . ExecuteWithCapturedOutput ( args ) ;
}
}
2016-04-08 15:33:32 -07:00
class DependencyContextTestCommand : TestCommand
{
public DependencyContextTestCommand ( )
: base ( "dotnet" )
{
}
public override CommandResult Execute ( string path )
{
var args = $"dependency-context-test {path}" ;
return base . Execute ( args ) ;
}
public override CommandResult ExecuteWithCapturedOutput ( string path )
{
var args = $"dependency-context-test {path}" ;
return base . ExecuteWithCapturedOutput ( args ) ;
}
}
2016-02-10 13:16:23 -06:00
}
}