copy files and direct restore and build without adjusting paths

This commit is contained in:
Krzysztof Wicher 2017-02-10 10:54:05 -08:00
parent 4952a819d4
commit f77c5d76cb
2 changed files with 161 additions and 368 deletions

View file

@ -18,33 +18,15 @@ namespace Microsoft.DotNet.TestFramework
{ {
public class TestAssetInfo public class TestAssetInfo
{ {
private const string DataDirectoryName = ".tam";
private readonly string [] FilesToExclude = { ".DS_Store", ".noautobuild" }; private readonly string [] FilesToExclude = { ".DS_Store", ".noautobuild" };
private readonly DirectoryInfo [] _directoriesToExclude; public string AssetName { get; private set; }
private readonly string _assetName; public FileInfo DotnetExeFile { get; private set; }
private readonly DirectoryInfo _dataDirectory; public string ProjectFilePattern { get; private set; }
private readonly DirectoryInfo _root; public DirectoryInfo Root { get; private set; }
private readonly DirectoryInfo _operationDirectory;
private readonly TestAssetInventoryFiles _inventoryFiles;
private readonly FileInfo _dotnetExeFile;
private readonly string _projectFilePattern;
internal DirectoryInfo Root
{
get
{
return _operationDirectory;
}
}
internal TestAssetInfo(DirectoryInfo root, string assetName, FileInfo dotnetExeFile, string projectFilePattern) internal TestAssetInfo(DirectoryInfo root, string assetName, FileInfo dotnetExeFile, string projectFilePattern)
{ {
@ -68,24 +50,13 @@ namespace Microsoft.DotNet.TestFramework
throw new ArgumentException("Argument cannot be null or whitespace", nameof(projectFilePattern)); throw new ArgumentException("Argument cannot be null or whitespace", nameof(projectFilePattern));
} }
_root = root; Root = root;
_assetName = assetName; AssetName = assetName;
_dotnetExeFile = dotnetExeFile; DotnetExeFile = dotnetExeFile;
_projectFilePattern = projectFilePattern; ProjectFilePattern = projectFilePattern;
_dataDirectory = _root.GetDirectory(DataDirectoryName);
_operationDirectory = _dataDirectory.GetDirectory("files");
_inventoryFiles = new TestAssetInventoryFiles(_dataDirectory);
_directoriesToExclude = new []
{
_dataDirectory
};
//throw new Exception($"root = {_root}\nassetName = {_assetName}\ndotnetExeFile = {_dotnetExeFile}\nprojectFilePattern = {_projectFilePattern}\ndataDir = {_dataDirectory}\ndirectoriesToExclude = {string.Join<DirectoryInfo>(";",_directoriesToExclude)}"); //throw new Exception($"root = {_root}\nassetName = {_assetName}\ndotnetExeFile = {_dotnetExeFile}\nprojectFilePattern = {_projectFilePattern}\ndataDir = {_dataDirectory}\ndirectoriesToExclude = {string.Join<DirectoryInfo>(";",_directoriesToExclude)}");
} }
@ -103,37 +74,8 @@ namespace Microsoft.DotNet.TestFramework
{ {
ThrowIfTestAssetDoesNotExist(); ThrowIfTestAssetDoesNotExist();
RemoveCacheIfSourcesHaveChanged(); return Root.GetFiles("*.*", SearchOption.AllDirectories)
.Where(f => !FilesToExclude.Contains(f.Name));
return GetInventory(
_inventoryFiles.Source,
null,
DoCopyFiles);
}
internal IEnumerable<FileInfo> GetRestoreFiles()
{
ThrowIfTestAssetDoesNotExist();
RemoveCacheIfSourcesHaveChanged();
return GetInventory(
_inventoryFiles.Restore,
GetSourceFiles,
DoRestore);
}
internal IEnumerable<FileInfo> GetBuildFiles()
{
ThrowIfTestAssetDoesNotExist();
RemoveCacheIfSourcesHaveChanged();
return GetInventory(
_inventoryFiles.Build,
() => GetRestoreFiles()
.Concat(GetSourceFiles()), // TODO: likely not needed
DoBuild);
} }
private DirectoryInfo GetTestDestinationDirectory(string callingMethod, string identifier) private DirectoryInfo GetTestDestinationDirectory(string callingMethod, string identifier)
@ -143,56 +85,7 @@ namespace Microsoft.DotNet.TestFramework
#else #else
string baseDirectory = AppContext.BaseDirectory; string baseDirectory = AppContext.BaseDirectory;
#endif #endif
return new DirectoryInfo(Path.Combine(baseDirectory, callingMethod + identifier, _assetName)); return new DirectoryInfo(Path.Combine(baseDirectory, callingMethod + identifier, AssetName));
}
private IEnumerable<FileInfo> GetOperationFileList()
{
return _operationDirectory.GetFiles("*.*", SearchOption.AllDirectories);
}
private IEnumerable<FileInfo> GetOriginalFileList()
{
return _root.GetFiles("*.*", SearchOption.AllDirectories)
.Where(f => !_directoriesToExclude.Any(d => d.Contains(f)))
.Where(f => !FilesToExclude.Contains(f.Name));
}
private IEnumerable<FileInfo> GetInventory(
FileInfo file,
Func<IEnumerable<FileInfo>> beforeAction,
Action action)
{
var inventory = Enumerable.Empty<FileInfo>();
IEnumerable<FileInfo> preInventory;
if (beforeAction == null)
{
preInventory = new List<FileInfo>();
}
else
{
preInventory = beforeAction();
}
ExclusiveFolderAccess.Do(_dataDirectory, (folder) => {
file.Refresh();
if (file.Exists)
{
inventory = folder.LoadInventory(file);
}
else
{
action();
inventory = GetOperationFileList().Where(i => !preInventory.Select(p => p.FullName).Contains(i.FullName));
folder.SaveInventory(file, inventory);
}
});
return inventory;
} }
private static string RebasePath(string path, string oldBaseDirectory, string newBaseDirectory) private static string RebasePath(string path, string oldBaseDirectory, string newBaseDirectory)
@ -201,19 +94,6 @@ namespace Microsoft.DotNet.TestFramework
return Path.Combine(newBaseDirectory, path); return Path.Combine(newBaseDirectory, path);
} }
private void RemoveOperationFiles()
{
foreach (var opFile in GetOperationFileList())
{
opFile.Delete();
}
foreach (var f in _inventoryFiles.AllInventoryFiles)
{
f.Delete();
}
}
private bool IsAncestor(FileInfo file, DirectoryInfo maybeAncestor) private bool IsAncestor(FileInfo file, DirectoryInfo maybeAncestor)
{ {
var dir = file.Directory; var dir = file.Directory;
@ -231,240 +111,69 @@ namespace Microsoft.DotNet.TestFramework
return false; return false;
} }
private void DoCopyFiles() //private void DoCopyFiles()
{ //{
Console.WriteLine($"TestAsset CopyFiles '{_assetName}'"); // Console.WriteLine($"TestAsset CopyFiles '{AssetName}'");
_operationDirectory.Refresh(); // _operationDirectory.Refresh();
if (!_operationDirectory.Exists) // if (!_operationDirectory.Exists)
{ // {
_operationDirectory.Create(); // _operationDirectory.Create();
} // }
else // else
{ // {
if (_operationDirectory.GetFiles().Any()) // if (_operationDirectory.GetFiles().Any())
{ // {
throw new Exception("operation files folder not empty"); // throw new Exception("operation files folder not empty");
} // }
} // }
foreach (var f in GetOriginalFileList()) // foreach (var f in GetOriginalFileList())
{ // {
string destinationPath = RebasePath(f.FullName, _root.FullName, _operationDirectory.FullName); // string destinationPath = RebasePath(f.FullName, Root.FullName, _operationDirectory.FullName);
var destinationDir = new FileInfo(destinationPath).Directory; // var destinationDir = new FileInfo(destinationPath).Directory;
if (!destinationDir.Exists) // if (!destinationDir.Exists)
{ // {
destinationDir.Create(); // destinationDir.Create();
} // }
if (string.Equals(f.Name, "nuget.config", StringComparison.OrdinalIgnoreCase)) // if (string.Equals(f.Name, "nuget.config", StringComparison.OrdinalIgnoreCase))
{ // {
var doc = XDocument.Load(f.FullName, LoadOptions.PreserveWhitespace); // var doc = XDocument.Load(f.FullName, LoadOptions.PreserveWhitespace);
foreach (var v in doc.Root.Element("packageSources").Elements("add").Attributes("value")) // foreach (var v in doc.Root.Element("packageSources").Elements("add").Attributes("value"))
{ // {
if (!Path.IsPathRooted(v.Value)) // if (!Path.IsPathRooted(v.Value))
{ // {
string fullPath = Path.GetFullPath(Path.Combine(f.Directory.FullName, v.Value)); // string fullPath = Path.GetFullPath(Path.Combine(f.Directory.FullName, v.Value));
if (!IsAncestor(new FileInfo(fullPath), _root)) // if (!IsAncestor(new FileInfo(fullPath), Root))
{ // {
v.Value = fullPath; // v.Value = fullPath;
} // }
} // }
//throw new Exception($"\nvalue = {v.Value}\n" + // //throw new Exception($"\nvalue = {v.Value}\n" +
// $"f.dir = {f.Directory.FullName}\n" + // // $"f.dir = {f.Directory.FullName}\n" +
// $"fullPath = {fullPath}"); // // $"fullPath = {fullPath}");
} // }
using (var file = new FileStream(destinationPath, FileMode.CreateNew, FileAccess.ReadWrite)) // using (var file = new FileStream(destinationPath, FileMode.CreateNew, FileAccess.ReadWrite))
{ // {
doc.Save(file, SaveOptions.None); // doc.Save(file, SaveOptions.None);
} // }
} // }
else // else
{ // {
f.CopyTo(destinationPath); // f.CopyTo(destinationPath);
} // }
} // }
} //}
private void DoRestore()
{
//throw new Exception("foooooo");
try
{
Console.WriteLine($"TestAsset Restore '{_assetName}'");
_operationDirectory.Refresh();
var projFiles = _operationDirectory.GetFiles(_projectFilePattern, SearchOption.AllDirectories);
foreach (var projFile in projFiles)
{
var restoreArgs = new string[] { "restore", projFile.FullName };
var commandResult = Command.Create(_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 '{_assetName}'@'{projFile.FullName}' Failed with {exitCode}");
throw new Exception($"TestAsset {_dotnetExeFile.FullName} {string.Join(" ", restoreArgs)}");
}
}
}
catch (Exception e)
{
throw new Exception($"NOOOOOOOOOOOOOOOOOOOOOOOOOOOOO:\n{e.Message}");
}
}
private void DoBuild()
{
string[] args = new string[] { "build" };
Console.WriteLine($"TestAsset Build '{_assetName}'");
var commandResult = Command.Create(_dotnetExeFile.FullName, args)
.WorkingDirectory(_operationDirectory.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 '{_assetName}' Failed with {exitCode}");
throw new Exception(message);
}
}
private bool HaveSourcesChanged(ExclusiveFolderAccess folder)
{
var originalFiles = GetOriginalFileList();
var originalFilesRebased = originalFiles.Select(f => RebasePath(f.FullName, _root.FullName, _operationDirectory.FullName));
var trackedOriginalFiles = folder.LoadInventory(_inventoryFiles.Source);
bool hasUntrackedFiles = originalFilesRebased.Any(a => !trackedOriginalFiles.Any(t => t.FullName.Equals(a)));
if (hasUntrackedFiles)
{
return true;
}
bool hasMissingFiles = trackedOriginalFiles.Any(t => !File.Exists(RebasePath(t.FullName, _operationDirectory.FullName, _root.FullName)));
if (hasMissingFiles)
{
return true;
}
foreach (var origFile in originalFiles)
{
var copiedFile = new FileInfo(RebasePath(origFile.FullName, _root.FullName, _operationDirectory.FullName));
if (origFile.LastWriteTimeUtc != copiedFile.LastWriteTimeUtc)
{
return true;
}
}
return false;
}
private void RemoveCacheIfSourcesHaveChanged()
{
ExclusiveFolderAccess.Do(_dataDirectory, (folder) => {
_operationDirectory.Refresh();
if (!_operationDirectory.Exists)
{
return;
}
if (HaveSourcesChanged(folder))
{
Console.WriteLine("Sources have changed................................");
RemoveOperationFiles();
}
});
}
private void ThrowIfTestAssetDoesNotExist() private void ThrowIfTestAssetDoesNotExist()
{ {
if (!_root.Exists) if (!Root.Exists)
{ {
throw new DirectoryNotFoundException($"Directory not found at '{_root.FullName}'"); throw new DirectoryNotFoundException($"Directory not found at '{Root.FullName}'");
}
}
private class ExclusiveFolderAccess
{
private DirectoryInfo _directory;
private ExclusiveFolderAccess(DirectoryInfo directory)
{
_directory = directory;
}
public static void Do(DirectoryInfo directory, Action<ExclusiveFolderAccess> action)
{
Task.Run(async () => await ConcurrencyUtilities.ExecuteWithFileLockedAsync<object>(
directory.FullName,
lockedToken =>
{
action(new ExclusiveFolderAccess(directory));
return Task.FromResult(new Object());
},
CancellationToken.None)).Wait();
}
public static IEnumerable<FileInfo> Read(FileInfo file)
{
IEnumerable<FileInfo> ret = null;
Do(file.Directory, (folder) => {
ret = folder.LoadInventory(file);
});
return ret;
}
public IEnumerable<FileInfo> LoadInventory(FileInfo file)
{
file.Refresh();
if (!file.Exists)
{
return Enumerable.Empty<FileInfo>();
}
var inventory = new List<FileInfo>();
foreach (var p in File.ReadAllLines(file.FullName))
{
inventory.Add(new FileInfo(p));
}
return inventory;
}
public void SaveInventory(FileInfo file, IEnumerable<FileInfo> inventory)
{
_directory.Refresh();
if (!_directory.Exists)
{
_directory.Create();
}
File.WriteAllLines(file.FullName, inventory.Select((fi) => fi.FullName).ToList());
} }
} }
} }

View file

@ -17,6 +17,18 @@ namespace Microsoft.DotNet.TestFramework
{ {
public class TestAssetInstance public class TestAssetInstance
{ {
public DirectoryInfo MigrationBackupRoot { get; }
public DirectoryInfo Root { get; }
public TestAssetInfo TestAssetInfo { get; }
private bool _filesCopied = false;
private bool _restored = false;
private bool _built = false;
public TestAssetInstance(TestAssetInfo testAssetInfo, DirectoryInfo root) public TestAssetInstance(TestAssetInfo testAssetInfo, DirectoryInfo root)
{ {
if (testAssetInfo == null) if (testAssetInfo == null)
@ -48,36 +60,44 @@ namespace Microsoft.DotNet.TestFramework
} }
} }
public DirectoryInfo MigrationBackupRoot { get; }
public DirectoryInfo Root { get; }
public TestAssetInfo TestAssetInfo { get; }
public TestAssetInstance WithSourceFiles() public TestAssetInstance WithSourceFiles()
{ {
var filesToCopy = TestAssetInfo.GetSourceFiles(); if (!_filesCopied)
{
var filesToCopy = TestAssetInfo.GetSourceFiles();
CopyFiles(filesToCopy); CopyFiles(filesToCopy);
_filesCopied = true;
}
return this; return this;
} }
public TestAssetInstance WithRestoreFiles() public TestAssetInstance WithRestoreFiles()
{ {
var filesToCopy = TestAssetInfo.GetRestoreFiles(); if (!_restored)
{
WithSourceFiles();
CopyFiles(filesToCopy); RestoreAllProjects();
_restored = true;
}
return this; return this;
} }
public TestAssetInstance WithBuildFiles() public TestAssetInstance WithBuildFiles()
{ {
var filesToCopy = TestAssetInfo.GetBuildFiles(); if (!_built)
{
WithRestoreFiles();
CopyFiles(filesToCopy); BuildRootProjectOrSolution();
_built = true;
}
return this; return this;
} }
@ -171,5 +191,69 @@ namespace Microsoft.DotNet.TestFramework
file.CopyTo(newPath); file.CopyTo(newPath);
} }
} }
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}");
throw new Exception($"TestAsset {TestAssetInfo.DotnetExeFile.FullName} {string.Join(" ", restoreArgs)}");
}
}
private void RestoreAllProjects()
{
Console.WriteLine($"TestAsset Restore '{TestAssetInfo.AssetName}'");
foreach (var projFile in GetProjectFiles())
{
Restore(projFile);
}
}
} }
} }