From 468b4c7be53d32933921226c69793bc1793649a4 Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Fri, 8 Jun 2018 16:24:56 -0700 Subject: [PATCH] Remove obsolete MSBuild tasks: DownloadFile, ZipArchive, UnzipArchive --- src/Microsoft.DotNet.Build.Tasks.IO/README.md | 76 ----- .../src/DownloadFile.cs | 114 ------- .../src/UnzipArchive.cs | 98 ------ .../src/ZipArchive.cs | 183 ------------ .../Microsoft.DotNet.Build.Tasks.IO.props | 3 - .../DownloadFileTests.cs | 74 ----- .../TestHelpers.cs | 57 ---- .../UnzipArchiveTest.cs | 197 ------------ .../ZipArchiveTest.cs | 280 ------------------ 9 files changed, 1082 deletions(-) delete mode 100644 src/Microsoft.DotNet.Build.Tasks.IO/src/DownloadFile.cs delete mode 100644 src/Microsoft.DotNet.Build.Tasks.IO/src/UnzipArchive.cs delete mode 100644 src/Microsoft.DotNet.Build.Tasks.IO/src/ZipArchive.cs delete mode 100644 test/Microsoft.DotNet.Build.Tasks.IO.Tests/DownloadFileTests.cs delete mode 100644 test/Microsoft.DotNet.Build.Tasks.IO.Tests/TestHelpers.cs delete mode 100644 test/Microsoft.DotNet.Build.Tasks.IO.Tests/UnzipArchiveTest.cs delete mode 100644 test/Microsoft.DotNet.Build.Tasks.IO.Tests/ZipArchiveTest.cs diff --git a/src/Microsoft.DotNet.Build.Tasks.IO/README.md b/src/Microsoft.DotNet.Build.Tasks.IO/README.md index 1204a51303a..bbfbfe66df8 100644 --- a/src/Microsoft.DotNet.Build.Tasks.IO/README.md +++ b/src/Microsoft.DotNet.Build.Tasks.IO/README.md @@ -8,89 +8,13 @@ See ["Task Packages"](../../Documentation/TaskPackages.md#usage) for guidance on Tasks in this package - Chmod - - DownloadFile - GetFileHash - - UnzipArchive - VerifyFileHash - - ZipArchive ## Tasks This package contains the following MSBuild tasks. -### `UnzipArchive` - -Unzips a .zip archive file. - -Task parameter | Type | Description --------------------|-------------|-------------------------------------------------------------------------------- -File | string | **[Required]** The path to the file to unzip. -DestinationFolder | string | **[Required]** The directory where files will be unzipped. -Overwrite | boolean | Overwrite files if they exists already in DestinationFolder. Defaults to false. -OutputFiles | ITaskItem[] | **[Output]** The files that were unzipped. - -```xml - -``` - -### `ZipArchive` - -Creates a .zip archive file - -#### Common parameters -Task parameter | Type | Description --------------------|---------|--------------------------------------------------------------- -OutputPath | string | **[Required]** The path where the zip file should be created. The containing directory will be created if it doesn't already exist. -Overwrite | boolean | Overwrite output path if it exists - -There are two valid usages: a list of files, or an entire directory - -#### Parameter set - files - -Task parameter | Type | Description --------------------|-------------|---------------------------------------------------------------------------------------------------------------------------------- -SourceFiles | ITaskItem[] | **[Required]** Files to be included in the zip.
The `Link` metadata item can be set to explicitly set the zip entry path. -BaseDirectory | string | **[Required]** The directory to use as the base directory. The entry path for each item in SourceFiles is relative to this. - -```xml - - - - - - -``` - - -#### Parameter set - directory - -Task parameter | Type | Description ------------------------|---------|------------- -SourceDirectory | string | **[Required]** Creates a zip for an entire directory. -IncludeSourceDirectory | bool | Include the source directory in the zip. Defaults to false. - -Example: -```xml - -``` - - -### `DownloadFile` - -Downloads a file. - -Task parameter | Type | Description --------------------|---------|------------- -Uri | string | **[Required]** The file to download. Can be prefixed with `file://` for local file paths (results in a copy). -OutputPath | string | **[Required]** The full file path destination for the downloaded file, including file name. The containing directory will be created if it doesn't already exist. -Overwrite | boolean | Overwrite output path if it exists -TimeoutSeconds | int | The maximum amount of time (in seconds) to allow for downloading the file. Defaults to 15 minutes. - -Example: -```xml - -``` - ### `GetFileHash` Computes the checksums for files. diff --git a/src/Microsoft.DotNet.Build.Tasks.IO/src/DownloadFile.cs b/src/Microsoft.DotNet.Build.Tasks.IO/src/DownloadFile.cs deleted file mode 100644 index 45cc159d0b5..00000000000 --- a/src/Microsoft.DotNet.Build.Tasks.IO/src/DownloadFile.cs +++ /dev/null @@ -1,114 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.IO; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Build.Framework; - -namespace Microsoft.DotNet.Build.Tasks.IO -{ - /// - /// Downloads a file. - /// - public class DownloadFile : Microsoft.Build.Utilities.Task, ICancelableTask - { - private readonly CancellationTokenSource _cts = new CancellationTokenSource(); - - /// - /// The file to download. Can be prefixed with file:// for local file paths. - /// - [Required] - public string Uri { get; set; } - - /// - /// Destination for the downloaded file. - /// - [Required] - public string OutputPath { get; set; } - - /// - /// Overwrite if it already exists. Defaults to false. - /// - public bool Overwrite { get; set; } - - /// - /// The maximum amount of time to allow for downloading the file. Defaults to 15 minutes. - /// - public int TimeoutSeconds { get; set; } = 60 * 15; - - public void Cancel() => _cts.Cancel(); - - public override bool Execute() => ExecuteAsync().Result; - - public async Task ExecuteAsync() - { - if (File.Exists(OutputPath) && !Overwrite) - { - Log.LogError($"{OutputPath} already exists. Set Overwrite=true to replace it."); - return false; - } - - _cts.CancelAfter(TimeSpan.FromSeconds(TimeoutSeconds)); - - Directory.CreateDirectory(Path.GetDirectoryName(OutputPath)); - - const string FileUriProtocol = "file://"; - - if (Uri.StartsWith(FileUriProtocol, StringComparison.OrdinalIgnoreCase)) - { - var filePath = Uri.Substring(FileUriProtocol.Length); - Log.LogMessage($"Copying '{filePath}' to '{OutputPath}'"); - File.Copy(filePath, OutputPath, Overwrite); - } - else - { - Log.LogMessage($"Downloading '{Uri}' to '{OutputPath}'"); - - using (var httpClient = new HttpClient - { - // Set operation timeout to 2 minutes (doesn't represent overall timeout) - Timeout = TimeSpan.FromMinutes(2), - }) - { - try - { - // Only fetch the headers first. This operation will timeout in 2 minutes if headers are not returned. - var response = await httpClient.GetAsync(Uri, HttpCompletionOption.ResponseHeadersRead, _cts.Token); - - response.EnsureSuccessStatusCode(); - _cts.Token.ThrowIfCancellationRequested(); - - // Get a stream that represents the body. This operation will timeout in 2 minutes if body response doesn't begin. - var responseStream = await response.Content.ReadAsStreamAsync(); - - _cts.Token.ThrowIfCancellationRequested(); - - using (var outStream = File.Create(OutputPath)) - { - await responseStream.CopyToAsync(outStream, 4096, _cts.Token); - } - } - catch (Exception ex) - { - Log.LogError($"Downloading '{Uri}' failed."); - Log.LogErrorFromException(ex, showStackTrace: true); - - if (File.Exists(OutputPath)) - { - // cleanup any partially downloaded results. - File.Delete(OutputPath); - } - - return false; - } - } - } - - return !Log.HasLoggedErrors; - } - } -} diff --git a/src/Microsoft.DotNet.Build.Tasks.IO/src/UnzipArchive.cs b/src/Microsoft.DotNet.Build.Tasks.IO/src/UnzipArchive.cs deleted file mode 100644 index fbb442a1ba0..00000000000 --- a/src/Microsoft.DotNet.Build.Tasks.IO/src/UnzipArchive.cs +++ /dev/null @@ -1,98 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Generic; -using System.IO; -using System.IO.Compression; -using System.Linq; -using Microsoft.Build.Framework; -using Microsoft.Build.Utilities; -using IOFile = System.IO.File; -using ZipArchiveStream = System.IO.Compression.ZipArchive; - -namespace Microsoft.DotNet.Build.Tasks.IO -{ - /// - /// Unzips an archive file. - /// - public class UnzipArchive : Task - { - /// - /// The path to the file to unzip. - /// - [Required] - public string File { get; set; } - - /// - /// The directory where files will be unzipped. The directory will be created if it does not exist. - /// - [Required] - public string DestinationFolder { get; set; } - - /// - /// Overwrite files if they exists already in . Defaults to false. - /// - public bool Overwrite { get; set; } = false; - - /// - /// The files that were unzipped. - /// - [Output] - public ITaskItem[] OutputFiles { get; set; } - - public override bool Execute() - { - if (!IOFile.Exists(File)) - { - Log.LogError("'{0}' does not exist", File); - return false; - } - - Directory.CreateDirectory(DestinationFolder); - - var backslashIsInvalidFileNameChar = Path.GetInvalidFileNameChars().Any(c => c == '\\'); - - var output = new List(); - using (var stream = IOFile.OpenRead(File)) - using (var zip = new ZipArchiveStream(stream, ZipArchiveMode.Read)) - { - foreach (var entry in zip.Entries) - { - var entryPath = entry.FullName; - - if (!backslashIsInvalidFileNameChar) - { - // On non-Windows platforms, a backslash is a valid file name character. - // In almost all cases, a backslash in the zip entry was unintentional due - // to misuse of ZipArchiveStream. This normalizes backslashes to forward slash - // so the backslash is treated as a directory separator. - - if (entry.FullName.IndexOf('\\') >= 0) - { - // Normalize backslashes in zip entry. - entryPath = entry.FullName.Replace('\\', '/'); - } - } - - var fileDest = Path.Combine(DestinationFolder, entryPath); - var dirName = Path.GetDirectoryName(fileDest); - Directory.CreateDirectory(dirName); - - // Do not try to extract directories - if (Path.GetFileName(fileDest) != string.Empty) - { - entry.ExtractToFile(fileDest, Overwrite); - Log.LogMessage(MessageImportance.Low, "Extracted '{0}'", fileDest); - output.Add(new TaskItem(fileDest)); - } - } - } - - Log.LogMessage(MessageImportance.High, "Extracted {0} file(s) to '{1}'", output.Count, DestinationFolder); - OutputFiles = output.ToArray(); - - return true; - } - } -} diff --git a/src/Microsoft.DotNet.Build.Tasks.IO/src/ZipArchive.cs b/src/Microsoft.DotNet.Build.Tasks.IO/src/ZipArchive.cs deleted file mode 100644 index 59edbd3a4b3..00000000000 --- a/src/Microsoft.DotNet.Build.Tasks.IO/src/ZipArchive.cs +++ /dev/null @@ -1,183 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.IO; -using System.IO.Compression; -using System.Runtime.InteropServices; -using Microsoft.Build.Framework; -using Microsoft.Build.Utilities; -using Microsoft.DotNet.Build.Tasks.IO.Internal; -using IOFile = System.IO.File; -using ZipArchiveStream = System.IO.Compression.ZipArchive; - -namespace Microsoft.DotNet.Build.Tasks.IO -{ - /// - /// Creates a zip archive. - /// - public class ZipArchive : Task - { - /// - /// The path where the zip file should be created. The containing directory will be created if it doesn't already exist. - /// - [Required] - public string OutputPath { get; set; } - - /// - /// Overwrite if it exists. Defaults to false. - /// - public bool Overwrite { get; set; } - - // - // Parameter set 1 - files - // - - /// - /// Files to be added to . The `Link` metadata item can be set to explicitly set the zip entry path. - /// - public ITaskItem[] SourceFiles { get; set; } - - /// - /// The directory to use as the base directory. The entry path - /// for each item in is relative to this. - /// - public string BaseDirectory { get; set; } - - // - // Parameter set 2 - directory - // - - /// - /// Creates a zip for an entire directory. - /// - public string SourceDirectory { get; set; } - - /// - /// Include the source directory in the zip. Defaults to false. - /// - public bool IncludeSourceDirectory { get; set; } = false; - - public override bool Execute() - { - if (IOFile.Exists(OutputPath)) - { - if (Overwrite) - { - Log.LogMessage(MessageImportance.Low, $"'{OutputPath}' already exists and Overwrite is '{Overwrite}', deleting before zipping..."); - IOFile.Delete(OutputPath); - } - else - { - Log.LogError($"Zip file {OutputPath} already exists. Set {nameof(Overwrite)}=true to replace it."); - return !Log.HasLoggedErrors; - } - } - - Directory.CreateDirectory(Path.GetDirectoryName(OutputPath)); - - if (!string.IsNullOrEmpty(SourceDirectory)) - { - return CompressDirectory(); - } - else - { - return CompressFiles(); - } - } - - private bool CompressDirectory() - { - if (SourceFiles?.Length > 0) - { - Log.LogError($"{nameof(ZipArchive)} does not support setting both {nameof(SourceDirectory)} and {nameof(SourceFiles)}. Use one or the other, but not both."); - return false; - } - - ZipFile.CreateFromDirectory(SourceDirectory, OutputPath, CompressionLevel.Optimal, IncludeSourceDirectory); - - var fileInfo = new FileInfo(OutputPath); - Log.LogMessage(MessageImportance.High, - $"Added {SourceDirectory} to '{OutputPath}' ({fileInfo.Length / 1024:n0} KB)"); - - return !Log.HasLoggedErrors; - } - - private bool CompressFiles() - { - if (string.IsNullOrEmpty(BaseDirectory)) - { - Log.LogError($"Missing value for required parameter {nameof(BaseDirectory)}"); - return false; - } - - var workDir = FileHelpers.EnsureTrailingSlash(BaseDirectory).Replace('\\', '/'); - - foreach (var file in SourceFiles) - { - if (!string.IsNullOrEmpty(file.GetMetadata("Link"))) - { - continue; - } - - var filePath = file.ItemSpec.Replace('\\', '/'); - if (!filePath.StartsWith(workDir)) - { - Log.LogError("Item {0} is not inside the working directory {1}. Set the metadata 'Link' to file path that should be used within the zip archive", - filePath, - workDir); - return false; - } - - file.SetMetadata("Link", filePath.Substring(workDir.Length)); - } - - Directory.CreateDirectory(Path.GetDirectoryName(OutputPath)); - - using (var stream = IOFile.Create(OutputPath)) - using (var zip = new ZipArchiveStream(stream, ZipArchiveMode.Create)) - { - foreach (var file in SourceFiles) - { - var entryName = file.GetMetadata("Link").Replace('\\', '/'); - if (string.IsNullOrEmpty(Path.GetFileName(entryName))) - { - Log.LogError("Empty file names not allowed. The effective entry path for item '{0}' is '{1}'", file.ItemSpec, entryName); - return false; - } - - var entry = zip.CreateEntryFromFile(file.ItemSpec, entryName); -#if NET45 -#elif NETCOREAPP2_0 - if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - // This isn't required when creating a zip on Windows. unzip will check which - // platform was used to create the zip file. If the zip was created on Windows, - // unzip will use a default set of permissions. However, if the zip was created - // on a Unix-y system, it will set the permissions as defined in the external_attr - // field. - - // Set the file permissions on each entry so they are extracted correctly on Unix. - // Picking -rw-rw-r-- by default because we don't yet have a good way to access existing - // Unix permissions. If we don't set this, files may be extracted as ---------- (0000), - // which means the files are completely unusable. - - // FYI - this may not be necessary in future versions of .NET Core. See https://github.com/dotnet/corefx/issues/17342. - const int rw_rw_r = (0x8000 + 0x0100 + 0x0080 + 0x0020 + 0x0010 + 0x0004) << 16; - entry.ExternalAttributes = rw_rw_r; - } -#else -#error Update target frameworks -#endif - Log.LogMessage("Added '{0}' to archive", entry.FullName); - } - } - - var fileInfo = new FileInfo(OutputPath); - Log.LogMessage(MessageImportance.High, - $"Added {SourceFiles.Length} file(s) to '{OutputPath}' ({fileInfo.Length / 1024:n0} KB)"); - - return true; - } - } -} diff --git a/src/Microsoft.DotNet.Build.Tasks.IO/targets/Microsoft.DotNet.Build.Tasks.IO.props b/src/Microsoft.DotNet.Build.Tasks.IO/targets/Microsoft.DotNet.Build.Tasks.IO.props index 5bd6739918f..09edcd80ce2 100644 --- a/src/Microsoft.DotNet.Build.Tasks.IO/targets/Microsoft.DotNet.Build.Tasks.IO.props +++ b/src/Microsoft.DotNet.Build.Tasks.IO/targets/Microsoft.DotNet.Build.Tasks.IO.props @@ -6,9 +6,6 @@ - - - diff --git a/test/Microsoft.DotNet.Build.Tasks.IO.Tests/DownloadFileTests.cs b/test/Microsoft.DotNet.Build.Tasks.IO.Tests/DownloadFileTests.cs deleted file mode 100644 index 52dafeede0e..00000000000 --- a/test/Microsoft.DotNet.Build.Tasks.IO.Tests/DownloadFileTests.cs +++ /dev/null @@ -1,74 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.IO; -using Xunit; -using Xunit.Abstractions; -using Task = System.Threading.Tasks.Task; - -namespace Microsoft.DotNet.Build.Tasks.IO.Tests -{ - public class DownloadFileTests - { - private readonly ITestOutputHelper _output; - - public DownloadFileTests(ITestOutputHelper output) - { - _output = output; - } - - [Fact] - public async Task ItDownloadAFile() - { - var expectedPath = Path.Combine(AppContext.BaseDirectory, Path.GetRandomFileName()); - var task = new DownloadFile - { - Uri = "http://example.org/index.html", - OutputPath = expectedPath, - BuildEngine = new MockEngine(_output), - }; - - if (File.Exists(expectedPath)) - { - File.Delete(expectedPath); - } - - Assert.True(await task.ExecuteAsync(), "Task should pass"); - Assert.True(File.Exists(expectedPath), "The file should exist"); - } - - [Fact] - public async Task ItFailsForFilesThatDoNotExist() - { - var engine = new MockEngine(_output) { ContinueOnError = true }; - var task = new DownloadFile - { - Uri = "http://localhost/this/file/does/not/exist", - OutputPath = Path.Combine(AppContext.BaseDirectory, "dummy.txt"), - BuildEngine = engine, - }; - - Assert.False(await task.ExecuteAsync(), "Task should fail"); - Assert.NotEmpty(engine.Errors); - } - - [Fact] - public async Task ItFailsIfFileAlreadyExists() - { - var engine = new MockEngine(_output) { ContinueOnError = true }; - var expectedPath = Path.Combine(AppContext.BaseDirectory, Path.GetRandomFileName()); - File.WriteAllText(expectedPath, ""); - var task = new DownloadFile - { - Uri = "http://localhost/this/file/does/not/exist", - OutputPath = expectedPath, - BuildEngine = engine, - }; - - Assert.False(await task.ExecuteAsync(), "Task should fail"); - Assert.NotEmpty(engine.Errors); - } - } -} diff --git a/test/Microsoft.DotNet.Build.Tasks.IO.Tests/TestHelpers.cs b/test/Microsoft.DotNet.Build.Tasks.IO.Tests/TestHelpers.cs deleted file mode 100644 index 0a13a62e77b..00000000000 --- a/test/Microsoft.DotNet.Build.Tasks.IO.Tests/TestHelpers.cs +++ /dev/null @@ -1,57 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading; - -namespace Microsoft.DotNet.Build.Tasks.IO.Tests -{ - public class TestHelpers - { - public static void DeleteDirectory(string path) - { - var retries = 10; - while (retries > 0) - { - retries--; - try - { - Directory.Delete(path, recursive: true); - return; - } - catch (IOException ex) - { - if (retries > 0) - { - Thread.Sleep(TimeSpan.FromMilliseconds(100)); - } - else - { - IList lockedFiles; - try - { - lockedFiles = Directory.EnumerateFileSystemEntries(path, "*", SearchOption.AllDirectories).ToList(); - } - catch - { - // throw original exception if we can't figure out which files still exist - throw ex; - } - - var sb = new StringBuilder("Failed to cleanup files:"); - foreach (var file in lockedFiles) - { - sb.AppendLine(file); - } - throw new IOException(sb.ToString(), ex); - } - } - } - } - } -} diff --git a/test/Microsoft.DotNet.Build.Tasks.IO.Tests/UnzipArchiveTest.cs b/test/Microsoft.DotNet.Build.Tasks.IO.Tests/UnzipArchiveTest.cs deleted file mode 100644 index ff4bb7d16b8..00000000000 --- a/test/Microsoft.DotNet.Build.Tasks.IO.Tests/UnzipArchiveTest.cs +++ /dev/null @@ -1,197 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.IO; -using System.IO.Compression; -using Xunit; - -using ZipArchiveStream = System.IO.Compression.ZipArchive; - -namespace Microsoft.DotNet.Build.Tasks.IO.Tests -{ - public class UnzipArchiveTest : IDisposable - { - private readonly string _tempDir; - - public UnzipArchiveTest() - { - _tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); - Directory.CreateDirectory(_tempDir); - } - - [Fact] - public void UnzipsFile() - { - var files = new[] - { - "a.txt", - "dir/b.txt", - }; - - var dest = CreateZip(files); - var outDir = Path.Combine(_tempDir, "out"); - - var task = new UnzipArchive - { - File = dest, - DestinationFolder = outDir, - BuildEngine = new MockEngine(), - }; - - Assert.True(task.Execute(), "The task failed but should have passed."); - Assert.True(Directory.Exists(outDir), outDir + " does not exist"); - Assert.Equal(files.Length, task.OutputFiles.Length); - - Assert.All(task.OutputFiles, - f => Assert.True(Path.IsPathRooted(f.ItemSpec), $"Entry {f} should be a fullpath rooted")); - - foreach (var file in files) - { - var outFile = Path.Combine(outDir, file); - Assert.True(File.Exists(outFile), outFile + " does not exist"); - } - } - - [Fact] - public void UnzipsSubdirectories() - { - var files = new[] - { - "a/b/c/d.dll", - "e/f/j/k/l.json", - "e/f/m/n/o.json" - }; - - var dest = CreateZip(files); - var outDir = Path.Combine(_tempDir, "out"); - - var task = new UnzipArchive - { - File = dest, - DestinationFolder = outDir, - BuildEngine = new MockEngine(), - }; - - Assert.True(task.Execute(), "The task failed but should have passed."); - Assert.True(Directory.Exists(outDir), outDir + " does not exist"); - Assert.Equal(files.Length, task.OutputFiles.Length); - - Assert.All(task.OutputFiles, - f => Assert.True(Path.IsPathRooted(f.ItemSpec), $"Entry {f} should be a fullpath rooted")); - - foreach (var file in files) - { - var outFile = Path.Combine(outDir, file); - Assert.True(File.Exists(outFile), outFile + " does not exist"); - } - } - - [Fact] - public void Overwrites() - { - var files = new[] - { - "a.txt", - "dir/b.txt" - }; - - var dest = CreateZip(files); - var outDir = Path.Combine(_tempDir, "out"); - - var task = new UnzipArchive - { - File = dest, - DestinationFolder = outDir, - BuildEngine = new MockEngine(), - Overwrite = true - }; - - Directory.CreateDirectory(outDir); - - // Create a.txt before trying to unzip - var path = Path.Combine(outDir, "a.txt"); - File.WriteAllText(path, "contents!"); - Assert.True(task.Execute(), "The task failed but should have passed."); - Assert.Empty(File.ReadAllText(path)); - } - - [Fact] - public void DoesNotOverwrite() - { - var files = new[] - { - "a.txt", - "dir/b.txt" - }; - - var dest = CreateZip(files); - var outDir = Path.Combine(_tempDir, "out"); - - var task = new UnzipArchive - { - File = dest, - DestinationFolder = outDir, - BuildEngine = new MockEngine(), - Overwrite = false - }; - - Directory.CreateDirectory(outDir); - - // Create a.txt before trying to unzip - var path = Path.Combine(outDir, "a.txt"); - var contents = "contents!"; - File.WriteAllText(path, contents); - - Assert.Throws(() => task.Execute()); - - Assert.Equal(contents, File.ReadAllText(path)); - } - - [Fact] - public void ItNormalizesBacklashesInPath() - { - var files = new[] - { - @"dir\b.txt" - }; - - var dest = CreateZip(files); - var outDir = Path.Combine(_tempDir, "out"); - - var engine = new MockEngine(); - var task = new UnzipArchive - { - File = dest, - DestinationFolder = outDir, - BuildEngine = engine, - Overwrite = false - }; - - Assert.True(task.Execute(), "The task failed but should have passed."); - Assert.True(File.Exists(Path.Combine(outDir, "dir", "b.txt")), "File should exist."); - } - - private string CreateZip(string[] files) - { - var dest = Path.Combine(_tempDir, "test.zip"); - - using (var fileStream = new FileStream(dest, FileMode.Create)) - using (var zipStream = new ZipArchiveStream(fileStream, ZipArchiveMode.Create)) - { - foreach (var file in files) - { - zipStream.CreateEntry(file); - } - } - - return dest; - } - - public void Dispose() - { - TestHelpers.DeleteDirectory(_tempDir); - } - } -} diff --git a/test/Microsoft.DotNet.Build.Tasks.IO.Tests/ZipArchiveTest.cs b/test/Microsoft.DotNet.Build.Tasks.IO.Tests/ZipArchiveTest.cs deleted file mode 100644 index 47076e22873..00000000000 --- a/test/Microsoft.DotNet.Build.Tasks.IO.Tests/ZipArchiveTest.cs +++ /dev/null @@ -1,280 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using Microsoft.Build.Framework; -using Microsoft.Build.Utilities; -using Xunit; - -using ZipArchiveStream = System.IO.Compression.ZipArchive; - -namespace Microsoft.DotNet.Build.Tasks.IO.Tests -{ - public class ZipArchiveTest : IDisposable - { - private readonly string _tempDir; - - public ZipArchiveTest() - { - _tempDir = Path.Combine(AppContext.BaseDirectory, Path.GetRandomFileName()); - Directory.CreateDirectory(_tempDir); - } - - [Fact] - public void ZipsLinkItems() - { - var inputFile = Path.Combine(_tempDir, "..", Guid.NewGuid().ToString()); - var dest = Path.Combine(_tempDir, "test.zip"); - var linkItem = new TaskItem(inputFile); - linkItem.SetMetadata("Link", "temp/temp/temp/file.txt"); - try - { - File.WriteAllText(inputFile, ""); - var task = new ZipArchive - { - SourceFiles = new[] { linkItem }, - BaseDirectory = Path.Combine(_tempDir, "temp"), - OutputPath = dest, - BuildEngine = new MockEngine(), - }; - Assert.True(task.Execute()); - - using (var fileStream = new FileStream(dest, FileMode.Open)) - using (var zipStream = new ZipArchiveStream(fileStream)) - { - var entry = Assert.Single(zipStream.Entries); - Assert.Equal("temp/temp/temp/file.txt", entry.FullName); - } - } - finally - { - File.Delete(inputFile); - } - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void CreatesZipFromDirectory(bool includeBaseDirectory) - { - var files = new[] - { - "a.txt", - "dir/b.txt", - @"dir\c.txt", - }; - CreateItems(files); - - var dest = Path.Combine(AppContext.BaseDirectory, Path.GetRandomFileName() + ".zip"); - Assert.False(File.Exists(dest)); - - var task = new ZipArchive - { - SourceDirectory = _tempDir, - IncludeSourceDirectory = includeBaseDirectory, - OutputPath = dest, - Overwrite = true, - BuildEngine = new MockEngine(), - }; - - Assert.True(task.Execute()); - Assert.True(File.Exists(dest)); - - var entryPrefix = includeBaseDirectory ? Path.GetFileName(_tempDir) + "/" : null; - - using (var fileStream = new FileStream(dest, FileMode.Open)) - using (var zipStream = new ZipArchiveStream(fileStream)) - { - Assert.Equal(files.Length, zipStream.Entries.Count); - Assert.Collection(zipStream.Entries.OrderBy(d => d.FullName), - a => Assert.Equal(entryPrefix + "a.txt", a.FullName), - b => Assert.Equal(entryPrefix + "dir/b.txt", b.FullName), - c => Assert.Equal(entryPrefix + "dir/c.txt", c.FullName)); - } - } - - [Fact] - public void CreatesZipFromFiles() - { - var files = new[] - { - "a.txt", - "dir/b.txt", - @"dir\c.txt", - }; - - var dest = Path.Combine(_tempDir, "test.zip"); - Assert.False(File.Exists(dest)); - - var task = new ZipArchive - { - SourceFiles = CreateItems(files), - BaseDirectory = _tempDir, - OutputPath = dest, - Overwrite = true, - BuildEngine = new MockEngine(), - }; - - Assert.True(task.Execute()); - Assert.True(File.Exists(dest)); - - using (var fileStream = new FileStream(dest, FileMode.Open)) - using (var zipStream = new ZipArchiveStream(fileStream)) - { - Assert.Equal(files.Length, zipStream.Entries.Count); - Assert.Collection(zipStream.Entries, - a => Assert.Equal("a.txt", a.FullName), - b => Assert.Equal("dir/b.txt", b.FullName), - c => Assert.Equal("dir/c.txt", c.FullName)); - } - } - - [Fact] - public void FailsIfFileExists() - { - var files = new[] - { - "test.txt", - }; - - var dest = Path.Combine(_tempDir, "test.zip"); - File.WriteAllText(dest, "Original"); - - var task = new ZipArchive - { - SourceFiles = CreateItems(files), - BaseDirectory = _tempDir, - OutputPath = dest, - Overwrite = false, - BuildEngine = new MockEngine { ContinueOnError = true }, - }; - - Assert.False(task.Execute(), "Task should fail"); - Assert.Equal("Original", File.ReadAllText(dest)); - } - - [Fact] - public void FailsIfMixingParameterSets() - { - var task = new ZipArchive - { - SourceFiles = new[] { new TaskItem() }, - BaseDirectory = _tempDir, - SourceDirectory = _tempDir, - OutputPath = Path.Combine(_tempDir, "test.zip"), - Overwrite = false, - BuildEngine = new MockEngine { ContinueOnError = true }, - }; - - Assert.False(task.Execute(), "Task should fail"); - } - - [Fact] - public void OverwriteReplacesEntireZip() - { - var files1 = new[] - { - "a.txt", - "dir/b.txt", - @"dir\c.txt", - }; - - var files2 = new[] - { - "test.txt", - }; - - var dest = Path.Combine(_tempDir, "test.zip"); - Assert.False(File.Exists(dest)); - - var task = new ZipArchive - { - SourceFiles = CreateItems(files1), - BaseDirectory = _tempDir, - OutputPath = dest, - BuildEngine = new MockEngine(), - }; - - Assert.True(task.Execute()); - Assert.True(File.Exists(dest)); - - task = new ZipArchive - { - SourceFiles = CreateItems(files2), - BaseDirectory = _tempDir, - OutputPath = dest, - Overwrite = true, - BuildEngine = new MockEngine(), - }; - - Assert.True(task.Execute()); - Assert.True(File.Exists(dest)); - - using (var fileStream = File.OpenRead(dest)) - using (var zipStream = new ZipArchiveStream(fileStream)) - { - var entry = Assert.Single(zipStream.Entries); - Assert.Equal("test.txt", entry.FullName); - } - } - - [Fact] - public void FailsForEmptyFileName() - { - var inputFile = Path.Combine(_tempDir, "..", Guid.NewGuid().ToString()); - var dest = Path.Combine(_tempDir, "test.zip"); - var linkItem = new TaskItem(inputFile); - linkItem.SetMetadata("Link", "temp/"); - try - { - File.WriteAllText(inputFile, ""); - var mock = new MockEngine { ContinueOnError = true }; - var task = new ZipArchive - { - SourceFiles = new[] { linkItem }, - BaseDirectory = Path.Combine(_tempDir, "temp"), - OutputPath = dest, - BuildEngine = mock, - }; - - Assert.False(task.Execute(), "Task should fail"); - Assert.NotEmpty(mock.Errors); - - using (var fileStream = new FileStream(dest, FileMode.Open)) - using (var zipStream = new ZipArchiveStream(fileStream)) - { - Assert.Empty(zipStream.Entries); - } - } - finally - { - File.Delete(inputFile); - } - } - - private ITaskItem[] CreateItems(string[] files) - { - var items = new ITaskItem[files.Length]; - for (var i = 0; i < files.Length; i++) - { - var file = files[i]; - var path = Path.Combine(_tempDir, file); - Directory.CreateDirectory(Path.GetDirectoryName(path)); - File.WriteAllText(path.Replace('\\', '/'), ""); - // intentionally allow item spec to contain \ and / - // this tests that MSBuild normalizes before we create zip entries - items[i] = new TaskItem(path); - } - return items; - } - - public void Dispose() - { - TestHelpers.DeleteDirectory(_tempDir); - } - } -}