2016-10-27 18:46:43 -07: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.Collections.Generic ;
using System.IO ;
using System.Linq ;
2016-10-31 16:22:10 -07:00
using System.Reflection ;
2016-10-27 18:46:43 -07:00
using System.Runtime.CompilerServices ;
2016-10-31 16:22:10 -07:00
using System.Text ;
2016-10-27 18:46:43 -07:00
using System.Threading.Tasks ;
2017-02-01 18:35:52 -06:00
using System.Xml.Linq ;
2016-10-27 18:46:43 -07:00
using Microsoft.DotNet.Cli.Utils ;
using Microsoft.DotNet.Tools.Common ;
namespace Microsoft.DotNet.TestFramework
{
public class TestAssetInstance
{
2017-02-10 10:54:05 -08:00
public DirectoryInfo MigrationBackupRoot { get ; }
public DirectoryInfo Root { get ; }
public TestAssetInfo TestAssetInfo { get ; }
private bool _filesCopied = false ;
private bool _restored = false ;
private bool _built = false ;
2017-02-13 17:56:25 -08:00
public static string CurrentRuntimeFrameworkVersion = new Muxer ( ) . SharedFxVersion ;
2016-12-20 18:17:19 -08:00
public TestAssetInstance ( TestAssetInfo testAssetInfo , DirectoryInfo root )
2016-10-27 18:46:43 -07:00
{
2016-12-20 18:17:19 -08:00
if ( testAssetInfo = = null )
2016-10-27 18:46:43 -07:00
{
2016-12-20 18:17:19 -08:00
throw new ArgumentException ( nameof ( testAssetInfo ) ) ;
2016-10-27 18:46:43 -07:00
}
2017-02-10 10:57:36 -08:00
2016-12-20 18:17:19 -08:00
if ( root = = null )
{
throw new ArgumentException ( nameof ( root ) ) ;
}
TestAssetInfo = testAssetInfo ;
2016-10-27 18:46:43 -07:00
2016-12-20 18:17:19 -08:00
Root = root ;
2016-10-27 18:46:43 -07:00
2016-12-20 18:17:19 -08:00
MigrationBackupRoot = new DirectoryInfo ( Path . Combine ( root . Parent . FullName , "backup" ) ) ;
if ( Root . Exists )
2016-10-27 18:46:43 -07:00
{
2017-08-09 23:47:36 -07:00
try
{
Root . Delete ( recursive : true ) ;
}
catch ( IOException ex )
{
throw new InvalidOperationException ( "Unable to delete directory: " + Root . FullName , ex ) ;
}
2016-10-27 18:46:43 -07:00
}
2016-12-20 18:17:19 -08:00
Root . Create ( ) ;
if ( MigrationBackupRoot . Exists )
{
MigrationBackupRoot . Delete ( recursive : true ) ;
}
2016-10-27 18:46:43 -07:00
}
public TestAssetInstance WithSourceFiles ( )
{
2017-02-10 10:54:05 -08:00
if ( ! _filesCopied )
{
2017-02-10 10:57:36 -08:00
CopySourceFiles ( ) ;
2017-02-10 10:54:05 -08:00
_filesCopied = true ;
}
2016-10-27 18:46:43 -07:00
return this ;
}
public TestAssetInstance WithRestoreFiles ( )
{
2017-02-10 10:54:05 -08:00
if ( ! _restored )
{
WithSourceFiles ( ) ;
RestoreAllProjects ( ) ;
2016-10-27 18:46:43 -07:00
2017-02-10 10:54:05 -08:00
_restored = true ;
}
2016-10-27 18:46:43 -07:00
return this ;
}
public TestAssetInstance WithBuildFiles ( )
{
2017-02-10 10:54:05 -08:00
if ( ! _built )
{
WithRestoreFiles ( ) ;
BuildRootProjectOrSolution ( ) ;
2016-10-27 18:46:43 -07:00
2017-02-10 10:54:05 -08:00
_built = true ;
}
2016-10-27 18:46:43 -07:00
return this ;
}
2016-10-31 16:22:10 -07:00
public TestAssetInstance WithNuGetConfig ( string nugetCache )
{
var thisAssembly = typeof ( TestAssetInstance ) . GetTypeInfo ( ) . Assembly ;
2017-02-13 13:06:30 -08:00
var newNuGetConfig = Root . GetFile ( "NuGet.Config" ) ;
2016-10-31 16:22:10 -07:00
2016-11-01 12:46:29 -07:00
var content = @"<?xml version=""1.0"" encoding=""utf-8" "?>
< configuration >
< packageSources >
2017-04-03 22:15:40 -07:00
< add key = "" dotnet - core "" value = "" https : //dotnet.myget.org/F/dotnet-core/api/v3/index.json"" />
2016-11-01 12:46:29 -07:00
< add key = "" test - packages "" value = "" $ fullpath $"" / >
< / packageSources >
< / configuration > ";
content = content . Replace ( "$fullpath$" , nugetCache ) ;
2016-12-20 18:17:19 -08:00
2016-11-01 12:46:29 -07:00
using ( var newNuGetConfigStream =
new FileStream ( newNuGetConfig . FullName , FileMode . Create , FileAccess . Write ) )
2016-10-31 16:22:10 -07:00
{
2016-11-01 12:46:29 -07:00
var contentBytes = new UTF8Encoding ( true ) . GetBytes ( content ) ;
newNuGetConfigStream . Write ( contentBytes , 0 , contentBytes . Length ) ;
2016-10-31 16:22:10 -07:00
}
return this ;
}
2017-01-25 23:48:31 -08:00
public TestAssetInstance WithEmptyGlobalJson ( )
{
var file = Root . Parent . GetFile ( "global.json" ) ;
File . WriteAllText ( file . FullName , @"{}" ) ;
return this ;
}
2017-02-01 18:35:52 -06:00
public TestAssetInstance WithProjectChanges ( Action < XDocument > xmlAction )
{
return WithProjectChanges ( ( path , project ) = > xmlAction ( project ) ) ;
}
public TestAssetInstance WithProjectChanges ( Action < string , XDocument > xmlAction )
{
var projectFileInfos = Root . GetFiles ( "*.*proj" , SearchOption . AllDirectories ) ;
foreach ( var projectFileInfo in projectFileInfos )
{
var projectFile = projectFileInfo . FullName ;
var project = XDocument . Load ( projectFile ) ;
xmlAction ( projectFile , project ) ;
using ( var file = File . CreateText ( projectFile ) )
{
project . Save ( file ) ;
}
}
return this ;
}
public TestAssetInstance UseCurrentRuntimeFrameworkVersion ( )
{
return WithProjectChanges ( project = >
{
var ns = project . Root . Name . Namespace ;
var propertyGroup = project . Root . Elements ( ns + "PropertyGroup" ) . LastOrDefault ( ) ;
if ( propertyGroup = = null )
{
propertyGroup = new XElement ( ns + "PropertyGroup" ) ;
project . Root . Add ( propertyGroup ) ;
}
2017-02-13 17:56:25 -08:00
propertyGroup . Add ( new XElement ( ns + "RuntimeFrameworkVersion" , CurrentRuntimeFrameworkVersion ) ) ;
2017-02-01 18:35:52 -06:00
} ) ;
}
2017-02-10 10:57:36 -08:00
private static string RebasePath ( string path , string oldBaseDirectory , string newBaseDirectory )
{
path = Path . IsPathRooted ( path ) ? PathUtility . GetRelativePath ( PathUtility . EnsureTrailingSlash ( oldBaseDirectory ) , path ) : path ;
return Path . Combine ( newBaseDirectory , path ) ;
}
private void CopySourceFiles ( )
2016-10-27 18:46:43 -07:00
{
2017-02-10 10:57:36 -08:00
var filesToCopy = TestAssetInfo . GetSourceFiles ( ) ;
2016-10-27 18:46:43 -07:00
foreach ( var file in filesToCopy )
{
2017-02-10 10:57:36 -08:00
var newPath = RebasePath ( file . FullName , TestAssetInfo . Root . FullName , Root . FullName ) ;
2016-10-27 18:46:43 -07:00
var newFile = new FileInfo ( newPath ) ;
2016-12-20 16:03:00 -08:00
PathUtility . EnsureDirectoryExists ( newFile . Directory . FullName ) ;
2016-10-27 18:46:43 -07:00
2017-02-10 10:57:36 -08:00
CopyFileAdjustingPaths ( file , newFile ) ;
}
}
private void CopyFileAdjustingPaths ( FileInfo source , FileInfo destination )
{
if ( string . Equals ( source . Name , "nuget.config" , StringComparison . OrdinalIgnoreCase ) )
{
CopyNugetConfigAdjustingPath ( source , destination ) ;
}
else
{
source . CopyTo ( destination . FullName ) ;
}
}
private void CopyNugetConfigAdjustingPath ( FileInfo source , FileInfo destination )
{
var doc = XDocument . Load ( source . FullName , LoadOptions . PreserveWhitespace ) ;
foreach ( var packageSource in doc . Root . Element ( "packageSources" ) . Elements ( "add" ) . Attributes ( "value" ) )
{
if ( ! Path . IsPathRooted ( packageSource . Value ) )
{
string fullPathAtSource = Path . GetFullPath ( Path . Combine ( source . Directory . FullName , packageSource . Value ) ) ;
2017-02-10 15:50:48 -08:00
if ( ! PathUtility . IsChildOfDirectory ( TestAssetInfo . Root . FullName , fullPathAtSource ) )
2017-02-10 10:57:36 -08:00
{
packageSource . Value = fullPathAtSource ;
}
}
using ( var file = new FileStream (
destination . FullName ,
FileMode . CreateNew ,
FileAccess . ReadWrite ) )
{
doc . Save ( file , SaveOptions . None ) ;
}
2016-10-27 18:46:43 -07:00
}
}
2017-02-10 10:54:05 -08:00
private void BuildRootProjectOrSolution ( )
{
string [ ] args = new string [ ] { "build" } ;
Console . WriteLine ( $"TestAsset Build '{TestAssetInfo.AssetName}'" ) ;
var commandResult = Command . Create ( TestAssetInfo . DotnetExeFile . FullName , args )
. WorkingDirectory ( Root . FullName )
. CaptureStdOut ( )
. CaptureStdErr ( )
. Execute ( ) ;
int exitCode = commandResult . ExitCode ;
if ( exitCode ! = 0 )
{
Console . WriteLine ( commandResult . StdOut ) ;
Console . WriteLine ( commandResult . StdErr ) ;
string message = string . Format ( $"TestAsset Build '{TestAssetInfo.AssetName}' Failed with {exitCode}" ) ;
throw new Exception ( message ) ;
}
}
private IEnumerable < FileInfo > GetProjectFiles ( )
{
return Root . GetFiles ( TestAssetInfo . ProjectFilePattern , SearchOption . AllDirectories ) ;
}
private void Restore ( FileInfo projectFile )
{
var restoreArgs = new string [ ] { "restore" , projectFile . FullName } ;
var commandResult = Command . Create ( TestAssetInfo . DotnetExeFile . FullName , restoreArgs )
. CaptureStdOut ( )
. CaptureStdErr ( )
. Execute ( ) ;
int exitCode = commandResult . ExitCode ;
if ( exitCode ! = 0 )
{
Console . WriteLine ( commandResult . StdOut ) ;
Console . WriteLine ( commandResult . StdErr ) ;
string message = string . Format ( $"TestAsset Restore '{TestAssetInfo.AssetName}'@'{projectFile.FullName}' Failed with {exitCode}" ) ;
2017-02-10 14:27:41 -08:00
throw new Exception ( message ) ;
2017-02-10 10:54:05 -08:00
}
}
private void RestoreAllProjects ( )
{
Console . WriteLine ( $"TestAsset Restore '{TestAssetInfo.AssetName}'" ) ;
foreach ( var projFile in GetProjectFiles ( ) )
{
Restore ( projFile ) ;
}
}
2016-10-27 18:46:43 -07:00
}
}