Dotnet publish tests
This commit is contained in:
parent
cd357cb273
commit
07eb7ef28f
43 changed files with 1826 additions and 129 deletions
|
@ -0,0 +1,30 @@
|
|||
// 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.IO;
|
||||
|
||||
namespace Microsoft.DotNet.Tools.Test.Utilities
|
||||
{
|
||||
public sealed class DisposableDirectory : TempDirectory, IDisposable
|
||||
{
|
||||
public DisposableDirectory(TempRoot root)
|
||||
: base(root)
|
||||
{
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (Path != null && Directory.Exists(Path))
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.Delete(Path, recursive: true);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
// 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.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
|
||||
namespace Microsoft.DotNet.Tools.Test.Utilities
|
||||
{
|
||||
public sealed class DisposableFile : TempFile, IDisposable
|
||||
{
|
||||
public DisposableFile(string path)
|
||||
: base(path)
|
||||
{
|
||||
}
|
||||
|
||||
public DisposableFile(string prefix = null, string extension = null, string directory = null, string callerSourcePath = null, int callerLineNumber = 0)
|
||||
: base(prefix, extension, directory, callerSourcePath, callerLineNumber)
|
||||
{
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (Path != null && File.Exists(Path))
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(Path);
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
try
|
||||
{
|
||||
// the file might still be memory-mapped, delete on close:
|
||||
DeleteFileOnClose(Path);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
throw new InvalidOperationException(string.Format(@"
|
||||
The file '{0}' seems to have been opened in a way that prevents us from deleting it on close.
|
||||
Is the file loaded as an assembly (e.g. via Assembly.LoadFile)?
|
||||
|
||||
{1}: {2}", Path, ex.GetType().Name, ex.Message), ex);
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
// We should ignore this exception if we got it the second time,
|
||||
// the most important reason is that the file has already been
|
||||
// scheduled for deletion and will be deleted when all handles
|
||||
// are closed.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll", PreserveSig = false)]
|
||||
private static extern void SetFileInformationByHandle(SafeFileHandle handle, int fileInformationClass, ref uint fileDispositionInfoDeleteFile, int bufferSize);
|
||||
|
||||
private const int FileDispositionInfo = 4;
|
||||
|
||||
internal static void PrepareDeleteOnCloseStreamForDisposal(FileStream stream)
|
||||
{
|
||||
// tomat: Set disposition to "delete" on the stream, so to avoid ForeFront EndPoint
|
||||
// Protection driver scanning the file. Note that after calling this on a file that's open with DeleteOnClose,
|
||||
// the file can't be opened again, not even by the same process.
|
||||
uint trueValue = 1;
|
||||
SetFileInformationByHandle(stream.SafeFileHandle, FileDispositionInfo, ref trueValue, sizeof(uint));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks given file for automatic deletion when all its handles are closed.
|
||||
/// Note that after doing this the file can't be opened again, not even by the same process.
|
||||
/// </summary>
|
||||
internal static void DeleteFileOnClose(string fullPath)
|
||||
{
|
||||
using (var stream = new FileStream(fullPath, FileMode.Open, FileAccess.ReadWrite, FileShare.Delete | FileShare.ReadWrite, 8, FileOptions.DeleteOnClose))
|
||||
{
|
||||
PrepareDeleteOnCloseStreamForDisposal(stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
// 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.
|
||||
|
||||
namespace Microsoft.DotNet.Tools.Test.Utilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements a few file name utilities that are needed by the compiler.
|
||||
/// In general the compiler is not supposed to understand the format of the paths.
|
||||
/// In rare cases it needs to check if a string is a valid file name or change the extension
|
||||
/// (embedded resources, netmodules, output name).
|
||||
/// The APIs are intentionally limited to cover just these rare cases. Do not add more APIs.
|
||||
/// </summary>
|
||||
internal static class FileNameUtilities
|
||||
{
|
||||
private const string DirectorySeparatorStr = "\\";
|
||||
internal const char DirectorySeparatorChar = '\\';
|
||||
internal const char AltDirectorySeparatorChar = '/';
|
||||
internal const char VolumeSeparatorChar = ':';
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the string represents an unqualified file name.
|
||||
/// The name may contain any characters but directory and volume separators.
|
||||
/// </summary>
|
||||
/// <param name="path">Path.</param>
|
||||
/// <returns>
|
||||
/// True if <paramref name="path"/> is a simple file name, false if it is null or includes a directory specification.
|
||||
/// </returns>
|
||||
internal static bool IsFileName(string path)
|
||||
{
|
||||
return IndexOfFileName(path) == 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the offset in <paramref name="path"/> where the dot that starts an extension is, or -1 if the path doesn't have an extension.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Returns 0 for path ".foo".
|
||||
/// Returns -1 for path "foo.".
|
||||
/// </remarks>
|
||||
private static int IndexOfExtension(string path)
|
||||
{
|
||||
if (path == null)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int length = path.Length;
|
||||
int i = length;
|
||||
|
||||
while (--i >= 0)
|
||||
{
|
||||
char c = path[i];
|
||||
if (c == '.')
|
||||
{
|
||||
if (i != length - 1)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (c == DirectorySeparatorChar || c == AltDirectorySeparatorChar || c == VolumeSeparatorChar)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an extension of the specified path string.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The same functionality as <see cref="System.IO.Path.GetExtension(string)"/> but doesn't throw an exception
|
||||
/// if there are invalid characters in the path.
|
||||
/// </remarks>
|
||||
internal static string GetExtension(string path)
|
||||
{
|
||||
if (path == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
int index = IndexOfExtension(path);
|
||||
return (index >= 0) ? path.Substring(index) : string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes extension from path.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Returns "foo" for path "foo.".
|
||||
/// Returns "foo.." for path "foo...".
|
||||
/// </remarks>
|
||||
private static string RemoveExtension(string path)
|
||||
{
|
||||
if (path == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
int index = IndexOfExtension(path);
|
||||
if (index >= 0)
|
||||
{
|
||||
return path.Substring(0, index);
|
||||
}
|
||||
|
||||
// trim last ".", if present
|
||||
if (path.Length > 0 && path[path.Length - 1] == '.')
|
||||
{
|
||||
return path.Substring(0, path.Length - 1);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns path with the extension changed to <paramref name="extension"/>.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Equivalent of <see cref="System.IO.Path.ChangeExtension(string, string)"/>
|
||||
///
|
||||
/// If <paramref name="path"/> is null, returns null.
|
||||
/// If path does not end with an extension, the new extension is appended to the path.
|
||||
/// If extension is null, equivalent to <see cref="RemoveExtension"/>.
|
||||
/// </returns>
|
||||
internal static string ChangeExtension(string path, string extension)
|
||||
{
|
||||
if (path == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var pathWithoutExtension = RemoveExtension(path);
|
||||
if (extension == null || path.Length == 0)
|
||||
{
|
||||
return pathWithoutExtension;
|
||||
}
|
||||
|
||||
if (extension.Length == 0 || extension[0] != '.')
|
||||
{
|
||||
return pathWithoutExtension + "." + extension;
|
||||
}
|
||||
|
||||
return pathWithoutExtension + extension;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the position in given path where the file name starts.
|
||||
/// </summary>
|
||||
/// <returns>-1 if path is null.</returns>
|
||||
internal static int IndexOfFileName(string path)
|
||||
{
|
||||
if (path == null)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int i = path.Length - 1; i >= 0; i--)
|
||||
{
|
||||
char ch = path[i];
|
||||
if (ch == DirectorySeparatorChar || ch == AltDirectorySeparatorChar || ch == VolumeSeparatorChar)
|
||||
{
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get file name from path.
|
||||
/// </summary>
|
||||
/// <remarks>Unlike <see cref="System.IO.Path.GetFileName"/> doesn't check for invalid path characters.</remarks>
|
||||
internal static string GetFileName(string path)
|
||||
{
|
||||
int fileNameStart = IndexOfFileName(path);
|
||||
return (fileNameStart <= 0) ? path : path.Substring(fileNameStart);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
// 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.Collections.Immutable;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
namespace Microsoft.DotNet.Tools.Test.Utilities
|
||||
{
|
||||
/// <summary>
|
||||
/// The collection of extension methods for the <see cref="ImmutableArray{T}"/> type
|
||||
/// </summary>
|
||||
public static class ImmutableArrayTestExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Writes read-only array of bytes to the specified file.
|
||||
/// </summary>
|
||||
/// <param name="bytes">Data to write to the file.</param>
|
||||
/// <param name="path">File path.</param>
|
||||
internal static void WriteToFile(this ImmutableArray<byte> bytes, string path)
|
||||
{
|
||||
Debug.Assert(!bytes.IsDefault);
|
||||
|
||||
const int bufferSize = 4096;
|
||||
using (FileStream fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, bufferSize))
|
||||
{
|
||||
// PERF: Consider using an ObjectPool<byte[]> here
|
||||
byte[] buffer = new byte[Math.Min(bufferSize, bytes.Length)];
|
||||
|
||||
int offset = 0;
|
||||
while (offset < bytes.Length)
|
||||
{
|
||||
int length = Math.Min(bufferSize, bytes.Length - offset);
|
||||
bytes.CopyTo(offset, buffer, 0, length);
|
||||
fileStream.Write(buffer, 0, length);
|
||||
offset += length;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
// 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.
|
||||
|
||||
namespace Microsoft.DotNet.Tools.Test.Utilities
|
||||
{
|
||||
internal enum PathKind
|
||||
{
|
||||
/// <summary>
|
||||
/// Null or empty.
|
||||
/// </summary>
|
||||
Empty,
|
||||
|
||||
/// <summary>
|
||||
/// "file"
|
||||
/// </summary>
|
||||
Relative,
|
||||
|
||||
/// <summary>
|
||||
/// ".\file"
|
||||
/// </summary>
|
||||
RelativeToCurrentDirectory,
|
||||
|
||||
/// <summary>
|
||||
/// "..\file"
|
||||
/// </summary>
|
||||
RelativeToCurrentParent,
|
||||
|
||||
/// <summary>
|
||||
/// "\dir\file"
|
||||
/// </summary>
|
||||
RelativeToCurrentRoot,
|
||||
|
||||
/// <summary>
|
||||
/// "C:dir\file"
|
||||
/// </summary>
|
||||
RelativeToDriveDirectory,
|
||||
|
||||
/// <summary>
|
||||
/// "C:\file" or "\\machine" (UNC).
|
||||
/// </summary>
|
||||
Absolute,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,379 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.DotNet.Tools.Test.Utilities
|
||||
{
|
||||
// Contains path parsing utilities.
|
||||
// We need our own because System.IO.Path is insufficient for our purposes
|
||||
// For example we need to be able to work with invalid paths or paths containing wildcards
|
||||
internal static class PathUtilities
|
||||
{
|
||||
// We consider '/' a directory separator on Unix like systems.
|
||||
// On Windows both / and \ are equally accepted.
|
||||
internal static readonly char DirectorySeparatorChar = IsUnixLikePlatform ? '/' : '\\';
|
||||
internal static readonly char AltDirectorySeparatorChar = '/';
|
||||
internal static readonly string DirectorySeparatorStr = new string(DirectorySeparatorChar, 1);
|
||||
internal const char VolumeSeparatorChar = ':';
|
||||
|
||||
private static bool IsUnixLikePlatform
|
||||
{
|
||||
get
|
||||
{
|
||||
return Path.DirectorySeparatorChar == '/';
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool IsDirectorySeparator(char c)
|
||||
{
|
||||
return c == DirectorySeparatorChar || c == AltDirectorySeparatorChar;
|
||||
}
|
||||
|
||||
internal static string TrimTrailingSeparators(string s)
|
||||
{
|
||||
int lastSeparator = s.Length;
|
||||
while (lastSeparator > 0 && IsDirectorySeparator(s[lastSeparator - 1]))
|
||||
{
|
||||
lastSeparator = lastSeparator - 1;
|
||||
}
|
||||
|
||||
if (lastSeparator != s.Length)
|
||||
{
|
||||
s = s.Substring(0, lastSeparator);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
internal static string GetExtension(string path)
|
||||
{
|
||||
return FileNameUtilities.GetExtension(path);
|
||||
}
|
||||
|
||||
internal static string ChangeExtension(string path, string extension)
|
||||
{
|
||||
return FileNameUtilities.ChangeExtension(path, extension);
|
||||
}
|
||||
|
||||
internal static string RemoveExtension(string path)
|
||||
{
|
||||
return FileNameUtilities.ChangeExtension(path, extension: null);
|
||||
}
|
||||
|
||||
internal static string GetFileName(string path)
|
||||
{
|
||||
return FileNameUtilities.GetFileName(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get directory name from path.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Unlike <see cref="System.IO.Path.GetDirectoryName"/> it
|
||||
/// doesn't check for invalid path characters,
|
||||
/// doesn't strip any trailing directory separators (TODO: tomat),
|
||||
/// doesn't recognize UNC structure \\computer-name\share\directory-name\file-name (TODO: tomat).
|
||||
/// </remarks>
|
||||
/// <returns>Prefix of path that represents a directory. </returns>
|
||||
internal static string GetDirectoryName(string path)
|
||||
{
|
||||
int fileNameStart = FileNameUtilities.IndexOfFileName(path);
|
||||
if (fileNameStart < 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return path.Substring(0, fileNameStart);
|
||||
}
|
||||
|
||||
internal static PathKind GetPathKind(string path)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
{
|
||||
return PathKind.Empty;
|
||||
}
|
||||
|
||||
// "C:\"
|
||||
// "\\machine" (UNC)
|
||||
// "/etc" (Unix)
|
||||
if (IsAbsolute(path))
|
||||
{
|
||||
return PathKind.Absolute;
|
||||
}
|
||||
|
||||
// "."
|
||||
// ".."
|
||||
// ".\"
|
||||
// "..\"
|
||||
if (path.Length > 0 && path[0] == '.')
|
||||
{
|
||||
if (path.Length == 1 || IsDirectorySeparator(path[1]))
|
||||
{
|
||||
return PathKind.RelativeToCurrentDirectory;
|
||||
}
|
||||
|
||||
if (path[1] == '.')
|
||||
{
|
||||
if (path.Length == 2 || IsDirectorySeparator(path[2]))
|
||||
{
|
||||
return PathKind.RelativeToCurrentParent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!IsUnixLikePlatform)
|
||||
{
|
||||
// "\"
|
||||
// "\foo"
|
||||
if (path.Length >= 1 && IsDirectorySeparator(path[0]))
|
||||
{
|
||||
return PathKind.RelativeToCurrentRoot;
|
||||
}
|
||||
|
||||
// "C:foo"
|
||||
|
||||
if (path.Length >= 2 && path[1] == VolumeSeparatorChar && (path.Length <= 2 || !IsDirectorySeparator(path[2])))
|
||||
{
|
||||
return PathKind.RelativeToDriveDirectory;
|
||||
}
|
||||
}
|
||||
|
||||
// "foo.dll"
|
||||
return PathKind.Relative;
|
||||
}
|
||||
|
||||
internal static bool IsAbsolute(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsUnixLikePlatform)
|
||||
{
|
||||
return path[0] == DirectorySeparatorChar;
|
||||
}
|
||||
|
||||
// "C:\"
|
||||
if (IsDriveRootedAbsolutePath(path))
|
||||
{
|
||||
// Including invalid paths (e.g. "*:\")
|
||||
return true;
|
||||
}
|
||||
|
||||
// "\\machine\share"
|
||||
// Including invalid/incomplete UNC paths (e.g. "\\foo")
|
||||
return path.Length >= 2 &&
|
||||
IsDirectorySeparator(path[0]) &&
|
||||
IsDirectorySeparator(path[1]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if given path is absolute and starts with a drive specification ("C:\").
|
||||
/// </summary>
|
||||
private static bool IsDriveRootedAbsolutePath(string path)
|
||||
{
|
||||
Debug.Assert(!IsUnixLikePlatform);
|
||||
return path.Length >= 3 && path[1] == VolumeSeparatorChar && IsDirectorySeparator(path[2]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a prefix of given path which is the root of the path.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Root of an absolute path or null if the path isn't absolute or has invalid format (e.g. "\\").
|
||||
/// It may or may not end with a directory separator (e.g. "C:\", "C:\foo", "\\machine\share", etc.) .
|
||||
/// </returns>
|
||||
internal static string GetPathRoot(string path)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
int length = GetPathRootLength(path);
|
||||
return (length != -1) ? path.Substring(0, length) : null;
|
||||
}
|
||||
|
||||
private static int GetPathRootLength(string path)
|
||||
{
|
||||
Debug.Assert(!string.IsNullOrEmpty(path));
|
||||
|
||||
if (IsUnixLikePlatform)
|
||||
{
|
||||
if (IsDirectorySeparator(path[0]))
|
||||
{
|
||||
// "/*"
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// "C:\"
|
||||
if (IsDriveRootedAbsolutePath(path))
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
|
||||
if (IsDirectorySeparator(path[0]))
|
||||
{
|
||||
// "\\machine\share"
|
||||
return GetUncPathRootLength(path);
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the length of root of an UNC path.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// "\\server\share" is root of UNC path "\\server\share\dir1\dir2\file".
|
||||
/// </remarks>
|
||||
private static int GetUncPathRootLength(string path)
|
||||
{
|
||||
Debug.Assert(IsDirectorySeparator(path[0]));
|
||||
|
||||
// root:
|
||||
// [directory-separator]{2,}[^directory-separator]+[directory-separator]+[^directory-separator]+
|
||||
|
||||
int serverIndex = IndexOfNonDirectorySeparator(path, 1);
|
||||
if (serverIndex < 2)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int separator = IndexOfDirectorySeparator(path, serverIndex);
|
||||
if (separator == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int shareIndex = IndexOfNonDirectorySeparator(path, separator);
|
||||
if (shareIndex == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int rootEnd = IndexOfDirectorySeparator(path, shareIndex);
|
||||
return rootEnd == -1 ? path.Length : rootEnd;
|
||||
}
|
||||
|
||||
private static int IndexOfDirectorySeparator(string path, int start)
|
||||
{
|
||||
for (int i = start; i < path.Length; i++)
|
||||
{
|
||||
if (IsDirectorySeparator(path[i]))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static int IndexOfNonDirectorySeparator(string path, int start)
|
||||
{
|
||||
for (int i = start; i < path.Length; i++)
|
||||
{
|
||||
if (!IsDirectorySeparator(path[i]))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Combines an absolute path with a relative.
|
||||
/// </summary>
|
||||
/// <param name="root">Absolute root path.</param>
|
||||
/// <param name="relativePath">Relative path.</param>
|
||||
/// <returns>
|
||||
/// An absolute combined path, or null if <paramref name="relativePath"/> is
|
||||
/// absolute (e.g. "C:\abc", "\\machine\share\abc"),
|
||||
/// relative to the current root (e.g. "\abc"),
|
||||
/// or relative to a drive directory (e.g. "C:abc\def").
|
||||
/// </returns>
|
||||
/// <seealso cref="CombinePossiblyRelativeAndRelativePaths"/>
|
||||
internal static string CombineAbsoluteAndRelativePaths(string root, string relativePath)
|
||||
{
|
||||
Debug.Assert(IsAbsolute(root));
|
||||
|
||||
return CombinePossiblyRelativeAndRelativePaths(root, relativePath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Combine two paths, the first of which may be absolute.
|
||||
/// </summary>
|
||||
/// <param name="rootOpt">First path: absolute, relative, or null.</param>
|
||||
/// <param name="relativePath">Second path: relative and non-null.</param>
|
||||
/// <returns>null, if <paramref name="rootOpt"/> is null; a combined path, otherwise.</returns>
|
||||
/// <seealso cref="CombineAbsoluteAndRelativePaths"/>
|
||||
internal static string CombinePossiblyRelativeAndRelativePaths(string rootOpt, string relativePath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(rootOpt))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (GetPathKind(relativePath))
|
||||
{
|
||||
case PathKind.Empty:
|
||||
return rootOpt;
|
||||
|
||||
case PathKind.Absolute:
|
||||
case PathKind.RelativeToCurrentRoot:
|
||||
case PathKind.RelativeToDriveDirectory:
|
||||
return null;
|
||||
}
|
||||
|
||||
return CombinePathsUnchecked(rootOpt, relativePath);
|
||||
}
|
||||
|
||||
internal static string CombinePathsUnchecked(string root, string relativePath)
|
||||
{
|
||||
Debug.Assert(!string.IsNullOrEmpty(root));
|
||||
|
||||
char c = root[root.Length - 1];
|
||||
if (!IsDirectorySeparator(c) && c != VolumeSeparatorChar)
|
||||
{
|
||||
return root + DirectorySeparatorStr + relativePath;
|
||||
}
|
||||
|
||||
return root + relativePath;
|
||||
}
|
||||
|
||||
internal static string RemoveTrailingDirectorySeparator(string path)
|
||||
{
|
||||
if (path.Length > 0 && IsDirectorySeparator(path[path.Length - 1]))
|
||||
{
|
||||
return path.Substring(0, path.Length - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether an assembly reference is considered an assembly file path or an assembly name.
|
||||
/// used, for example, on values of /r and #r.
|
||||
/// </summary>
|
||||
internal static bool IsFilePath(string assemblyDisplayNameOrPath)
|
||||
{
|
||||
Debug.Assert(assemblyDisplayNameOrPath != null);
|
||||
|
||||
string extension = FileNameUtilities.GetExtension(assemblyDisplayNameOrPath);
|
||||
return string.Equals(extension, ".dll", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(extension, ".exe", StringComparison.OrdinalIgnoreCase)
|
||||
|| assemblyDisplayNameOrPath.IndexOf(DirectorySeparatorChar) != -1
|
||||
|| assemblyDisplayNameOrPath.IndexOf(AltDirectorySeparatorChar) != -1;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.DotNet.Tools.Test.Utilities
|
||||
{
|
||||
public class TempDirectory
|
||||
{
|
||||
private readonly string _path;
|
||||
private readonly TempRoot _root;
|
||||
|
||||
protected TempDirectory(TempRoot root)
|
||||
: this(CreateUniqueDirectory(TempRoot.Root), root)
|
||||
{
|
||||
}
|
||||
|
||||
private TempDirectory(string path, TempRoot root)
|
||||
{
|
||||
Debug.Assert(path != null);
|
||||
Debug.Assert(root != null);
|
||||
|
||||
_path = path;
|
||||
_root = root;
|
||||
}
|
||||
|
||||
private static string CreateUniqueDirectory(string basePath)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
string dir = System.IO.Path.Combine(basePath, Guid.NewGuid().ToString());
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(dir);
|
||||
return dir;
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
// retry
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string Path
|
||||
{
|
||||
get { return _path; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a file in this directory.
|
||||
/// </summary>
|
||||
/// <param name="name">File name.</param>
|
||||
public TempFile CreateFile(string name)
|
||||
{
|
||||
string filePath = System.IO.Path.Combine(_path, name);
|
||||
TempRoot.CreateStream(filePath);
|
||||
return _root.AddFile(new DisposableFile(filePath));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a file in this directory that is a copy of the specified file.
|
||||
/// </summary>
|
||||
public TempFile CopyFile(string originalPath)
|
||||
{
|
||||
string name = System.IO.Path.GetFileName(originalPath);
|
||||
string filePath = System.IO.Path.Combine(_path, name);
|
||||
File.Copy(originalPath, filePath);
|
||||
return _root.AddFile(new DisposableFile(filePath));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a subdirectory in this directory.
|
||||
/// </summary>
|
||||
/// <param name="name">Directory name or unrooted directory path.</param>
|
||||
public TempDirectory CreateDirectory(string name)
|
||||
{
|
||||
string dirPath = System.IO.Path.Combine(_path, name);
|
||||
Directory.CreateDirectory(dirPath);
|
||||
return new TempDirectory(dirPath, _root);
|
||||
}
|
||||
|
||||
public void SetCurrentDirectory()
|
||||
{
|
||||
Directory.SetCurrentDirectory(_path);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return _path;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
// 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.Immutable;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.DotNet.Tools.Test.Utilities
|
||||
{
|
||||
public class TempFile
|
||||
{
|
||||
private readonly string _path;
|
||||
|
||||
internal TempFile(string path)
|
||||
{
|
||||
Debug.Assert(PathUtilities.IsAbsolute(path));
|
||||
_path = path;
|
||||
}
|
||||
|
||||
internal TempFile(string prefix, string extension, string directory, string callerSourcePath, int callerLineNumber)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (prefix == null)
|
||||
{
|
||||
prefix = System.IO.Path.GetFileName(callerSourcePath) + "_" + callerLineNumber.ToString() + "_";
|
||||
}
|
||||
|
||||
_path = System.IO.Path.Combine(directory ?? TempRoot.Root, prefix + Guid.NewGuid() + (extension ?? ".tmp"));
|
||||
|
||||
try
|
||||
{
|
||||
TempRoot.CreateStream(_path);
|
||||
break;
|
||||
}
|
||||
catch (PathTooLongException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (DirectoryNotFoundException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
// retry
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public FileStream Open(FileAccess access = FileAccess.ReadWrite)
|
||||
{
|
||||
return new FileStream(_path, FileMode.Open, access);
|
||||
}
|
||||
|
||||
public string Path
|
||||
{
|
||||
get { return _path; }
|
||||
}
|
||||
|
||||
public TempFile WriteAllText(string content, Encoding encoding)
|
||||
{
|
||||
File.WriteAllText(_path, content, encoding);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TempFile WriteAllText(string content)
|
||||
{
|
||||
File.WriteAllText(_path, content);
|
||||
return this;
|
||||
}
|
||||
|
||||
public async Task<TempFile> WriteAllTextAsync(string content, Encoding encoding)
|
||||
{
|
||||
using (var sw = new StreamWriter(File.Create(_path), encoding))
|
||||
{
|
||||
await sw.WriteAsync(content).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Task<TempFile> WriteAllTextAsync(string content)
|
||||
{
|
||||
return WriteAllTextAsync(content, Encoding.UTF8);
|
||||
}
|
||||
|
||||
public TempFile WriteAllBytes(byte[] content)
|
||||
{
|
||||
File.WriteAllBytes(_path, content);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TempFile WriteAllBytes(ImmutableArray<byte> content)
|
||||
{
|
||||
content.WriteToFile(_path);
|
||||
return this;
|
||||
}
|
||||
|
||||
public string ReadAllText()
|
||||
{
|
||||
return File.ReadAllText(_path);
|
||||
}
|
||||
|
||||
public TempFile CopyContentFrom(string path)
|
||||
{
|
||||
return WriteAllBytes(File.ReadAllBytes(path));
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return _path;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
// 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.Runtime.CompilerServices;
|
||||
|
||||
namespace Microsoft.DotNet.Tools.Test.Utilities
|
||||
{
|
||||
public sealed class TempRoot : IDisposable
|
||||
{
|
||||
private readonly List<IDisposable> _temps = new List<IDisposable>();
|
||||
public static readonly string Root;
|
||||
|
||||
static TempRoot()
|
||||
{
|
||||
Root = Path.Combine(Path.GetTempPath(), "DotnetCLITests");
|
||||
Directory.CreateDirectory(Root);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_temps != null)
|
||||
{
|
||||
DisposeAll(_temps);
|
||||
_temps.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private static void DisposeAll(IEnumerable<IDisposable> temps)
|
||||
{
|
||||
foreach (var temp in temps)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (temp != null)
|
||||
{
|
||||
temp.Dispose();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public TempDirectory CreateDirectory()
|
||||
{
|
||||
var dir = new DisposableDirectory(this);
|
||||
_temps.Add(dir);
|
||||
return dir;
|
||||
}
|
||||
|
||||
public TempFile CreateFile(string prefix = null, string extension = null, string directory = null, [CallerFilePath]string callerSourcePath = null, [CallerLineNumber]int callerLineNumber = 0)
|
||||
{
|
||||
return AddFile(new DisposableFile(prefix, extension, directory, callerSourcePath, callerLineNumber));
|
||||
}
|
||||
|
||||
public DisposableFile AddFile(DisposableFile file)
|
||||
{
|
||||
_temps.Add(file);
|
||||
return file;
|
||||
}
|
||||
|
||||
internal static void CreateStream(string fullPath)
|
||||
{
|
||||
using (var file = new FileStream(fullPath, FileMode.CreateNew)) { }
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue