diff --git a/Directory.Build.props b/Directory.Build.props index 907effa3..e685da3b 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -7,10 +7,5 @@ - - - true - true - Recommended - + \ No newline at end of file diff --git a/Pipelines/recursive-extractor-pr.yml b/Pipelines/recursive-extractor-pr.yml index 3476419e..97a45149 100644 --- a/Pipelines/recursive-extractor-pr.yml +++ b/Pipelines/recursive-extractor-pr.yml @@ -42,12 +42,16 @@ extends: poolName: MSSecurity-1ES-Build-Agents-Pool poolImage: MSSecurity-1ES-Windows-2022 poolOs: windows - dotnetTestArgs: '-- --coverage --report-trx' + dotnetTestArgs: '--settings coverlet.runsettings --collect:"XPlat Code Coverage"' includeNuGetOrg: false nugetFeedsToUse: 'config' nugetConfigPath: 'nuget.config' onInit: - task: NuGetAuthenticate@1 + postTest: + - task: PublishCodeCoverageResults@2 + inputs: + summaryFileLocation: '$(Agent.TempDirectory)/**/coverage.cobertura.xml' - template: dotnet-test-job.yml@templates parameters: @@ -57,12 +61,16 @@ extends: poolName: MSSecurity-1ES-Build-Agents-Pool poolImage: MSSecurity-1ES-Windows-2022 poolOs: windows - dotnetTestArgs: '-- --coverage --report-trx' + dotnetTestArgs: '--settings coverlet.runsettings --collect:"XPlat Code Coverage"' includeNuGetOrg: false nugetFeedsToUse: 'config' nugetConfigPath: 'nuget.config' onInit: - task: NuGetAuthenticate@1 + postTest: + - task: PublishCodeCoverageResults@2 + inputs: + summaryFileLocation: '$(Agent.TempDirectory)/**/coverage.cobertura.xml' - stage: Build dependsOn: diff --git a/Pipelines/recursive-extractor-release.yml b/Pipelines/recursive-extractor-release.yml index b2a85fc8..6cdd928a 100644 --- a/Pipelines/recursive-extractor-release.yml +++ b/Pipelines/recursive-extractor-release.yml @@ -43,12 +43,16 @@ extends: poolName: MSSecurity-1ES-Build-Agents-Pool poolImage: MSSecurity-1ES-Windows-2022 poolOs: windows - dotnetTestArgs: '-- --coverage --report-trx' + dotnetTestArgs: '--settings coverlet.runsettings --collect:"XPlat Code Coverage"' includeNuGetOrg: false nugetFeedsToUse: 'config' nugetConfigPath: 'nuget.config' onInit: - task: NuGetAuthenticate@1 + postTest: + - task: PublishCodeCoverageResults@2 + inputs: + summaryFileLocation: '$(Agent.TempDirectory)/**/coverage.cobertura.xml' - template: dotnet-test-job.yml@templates parameters: jobName: 'cli_dotnet_test_windows' @@ -57,12 +61,16 @@ extends: poolName: MSSecurity-1ES-Build-Agents-Pool poolImage: MSSecurity-1ES-Windows-2022 poolOs: windows - dotnetTestArgs: '-- --coverage --report-trx' + dotnetTestArgs: '--settings coverlet.runsettings --collect:"XPlat Code Coverage"' includeNuGetOrg: false nugetFeedsToUse: 'config' nugetConfigPath: 'nuget.config' onInit: - task: NuGetAuthenticate@1 + postTest: + - task: PublishCodeCoverageResults@2 + inputs: + summaryFileLocation: '$(Agent.TempDirectory)/**/coverage.cobertura.xml' - stage: Build dependsOn: diff --git a/RecursiveExtractor.Cli.Tests/CliTests/CliTests.cs b/RecursiveExtractor.Cli.Tests/CliTests/CliTests.cs index d430bca3..a2d633ad 100644 --- a/RecursiveExtractor.Cli.Tests/CliTests/CliTests.cs +++ b/RecursiveExtractor.Cli.Tests/CliTests/CliTests.cs @@ -3,41 +3,40 @@ using Microsoft.CST.RecursiveExtractor; using Microsoft.CST.RecursiveExtractor.Cli; using Microsoft.CST.RecursiveExtractor.Tests; -using Microsoft.VisualStudio.TestTools.UnitTesting; using RecursiveExtractor.Tests.ExtractorTests; using System; using System.IO; using System.Linq; using System.Threading; +using Xunit; namespace RecursiveExtractor.Tests.CliTests { - [TestClass] - public class CliTests : BaseExtractorTestClass + public class CliTests : IClassFixture { - [DataTestMethod] - [DataRow("TestData.zip", 5)] - [DataRow("TestData.7z")] - [DataRow("TestData.tar", 6)] - [DataRow("TestData.rar")] - [DataRow("TestData.rar4")] - [DataRow("TestData.tar.bz2", 6)] - [DataRow("TestData.tar.gz", 6)] - [DataRow("TestData.tar.xz")] - [DataRow("sysvbanner_1.0-17fakesync1_amd64.deb", 8)] - [DataRow("TestData.a")] - [DataRow("TestData.bsd.ar")] - [DataRow("TestData.iso")] - [DataRow("TestData.vhdx")] - [DataRow("TestData.wim")] - [DataRow("EmptyFile.txt", 1)] - [DataRow("TestDataArchivesNested.Zip", 54)] + [Theory] + [InlineData("TestData.zip", 5)] + [InlineData("TestData.7z")] + [InlineData("TestData.tar", 6)] + [InlineData("TestData.rar")] + [InlineData("TestData.rar4")] + [InlineData("TestData.tar.bz2", 6)] + [InlineData("TestData.tar.gz", 6)] + [InlineData("TestData.tar.xz")] + [InlineData("sysvbanner_1.0-17fakesync1_amd64.deb", 8)] + [InlineData("TestData.a")] + [InlineData("TestData.bsd.ar")] + [InlineData("TestData.iso")] + [InlineData("TestData.vhdx")] + [InlineData("TestData.wim")] + [InlineData("EmptyFile.txt", 1)] + [InlineData("TestDataArchivesNested.Zip", 54)] public void ExtractArchiveParallel(string fileName, int expectedNumFiles = 3) { - ExtractArchive(fileName, expectedNumFiles, false); + CliTests.ExtractArchive(fileName, expectedNumFiles, false); } - internal void ExtractArchive(string fileName, int expectedNumFiles, bool singleThread) + internal static void ExtractArchive(string fileName, int expectedNumFiles, bool singleThread) { var directory = TestPathHelpers.GetFreshTestDirectory(); var path = Path.Combine(Directory.GetCurrentDirectory(), "TestData", "TestDataArchives", fileName); @@ -46,35 +45,35 @@ internal void ExtractArchive(string fileName, int expectedNumFiles, bool singleT Thread.Sleep(100); if (Directory.Exists(directory)) { - files = Directory.EnumerateFiles(directory, "*.*", SearchOption.AllDirectories).ToArray(); + files = [.. Directory.EnumerateFiles(directory, "*.*", SearchOption.AllDirectories)]; } - Assert.AreEqual(expectedNumFiles, files.Length); + Assert.Equal(expectedNumFiles, files.Length); } - [DataTestMethod] - [DataRow("TestData.zip", 5)] - [DataRow("TestData.7z")] - [DataRow("TestData.tar", 6)] - [DataRow("TestData.rar")] - [DataRow("TestData.rar4")] - [DataRow("TestData.tar.bz2", 6)] - [DataRow("TestData.tar.gz", 6)] - [DataRow("TestData.tar.xz")] - [DataRow("sysvbanner_1.0-17fakesync1_amd64.deb", 8)] - [DataRow("TestData.a")] - [DataRow("TestData.bsd.ar")] - [DataRow("TestData.iso")] - [DataRow("TestData.vhdx")] - [DataRow("TestData.wim")] - [DataRow("EmptyFile.txt", 1)] - [DataRow("TestDataArchivesNested.Zip", 54)] + [Theory] + [InlineData("TestData.zip", 5)] + [InlineData("TestData.7z")] + [InlineData("TestData.tar", 6)] + [InlineData("TestData.rar")] + [InlineData("TestData.rar4")] + [InlineData("TestData.tar.bz2", 6)] + [InlineData("TestData.tar.gz", 6)] + [InlineData("TestData.tar.xz")] + [InlineData("sysvbanner_1.0-17fakesync1_amd64.deb", 8)] + [InlineData("TestData.a")] + [InlineData("TestData.bsd.ar")] + [InlineData("TestData.iso")] + [InlineData("TestData.vhdx")] + [InlineData("TestData.wim")] + [InlineData("EmptyFile.txt", 1)] + [InlineData("TestDataArchivesNested.Zip", 54)] public void ExtractArchiveSingleThread(string fileName, int expectedNumFiles = 3) { - ExtractArchive(fileName, expectedNumFiles, true); + CliTests.ExtractArchive(fileName, expectedNumFiles, true); } - [DataTestMethod] - [DataRow("TestDataForFilters.7z")] + [Theory] + [InlineData("TestDataForFilters.7z")] public void ExtractArchiveWithAllowFilters(string fileName, int expectedNumFiles = 1) { var directory = TestPathHelpers.GetFreshTestDirectory(); @@ -86,21 +85,21 @@ public void ExtractArchiveWithAllowFilters(string fileName, int expectedNumFiles Input = newpath, Output = directory, Verbose = true, - AllowFilters = new string[] - { + AllowFilters = + [ "*.cs" - } + ] }); var files = Array.Empty(); if (Directory.Exists(directory)) { - files = Directory.EnumerateFiles(directory, "*.*", SearchOption.AllDirectories).ToArray(); + files = [.. Directory.EnumerateFiles(directory, "*.*", SearchOption.AllDirectories)]; } - Assert.AreEqual(expectedNumFiles, files.Length); + Assert.Equal(expectedNumFiles, files.Length); } - [DataTestMethod] - [DataRow("TestDataForFilters.7z")] + [Theory] + [InlineData("TestDataForFilters.7z")] public void ExtractArchiveWithDenyFilters(string fileName, int expectedNumFiles = 2) { var directory = TestPathHelpers.GetFreshTestDirectory(); @@ -112,33 +111,31 @@ public void ExtractArchiveWithDenyFilters(string fileName, int expectedNumFiles Input = newpath, Output = directory, Verbose = true, - DenyFilters = new string[] - { + DenyFilters = + [ "*.cs" - } + ] }); var files = Array.Empty(); if (Directory.Exists(directory)) { - files = Directory.EnumerateFiles(directory, "*.*", SearchOption.AllDirectories).ToArray(); + files = [.. Directory.EnumerateFiles(directory, "*.*", SearchOption.AllDirectories)]; } - Assert.AreEqual(expectedNumFiles, files.Length); + Assert.Equal(expectedNumFiles, files.Length); } - [DataTestMethod] - [DataRow("TestDataEncrypted.7z")] - [DataRow("TestDataEncryptedAes.zip")] - [DataRow("TestDataEncrypted.rar4")] + [Theory] + [InlineData("TestDataEncrypted.7z")] + [InlineData("TestDataEncryptedAes.zip")] + [InlineData("TestDataEncrypted.rar4")] public void ExtractEncryptedArchive(string fileName, int expectedNumFiles = 3) { var directory = TestPathHelpers.GetFreshTestDirectory(); var path = Path.Combine(Directory.GetCurrentDirectory(), "TestData", "TestDataArchives", fileName); var passwords = EncryptedArchiveTests.TestArchivePasswords.Values.SelectMany(x => x); RecursiveExtractorClient.ExtractCommand(new ExtractCommandOptions() { Input = path, Output = directory, Verbose = true, Passwords = passwords }); - string[] files = Directory.EnumerateFiles(directory, "*.*", SearchOption.AllDirectories).ToArray(); - Assert.AreEqual(expectedNumFiles, files.Length); - } - - protected static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger(); + string[] files = [.. Directory.EnumerateFiles(directory, "*.*", SearchOption.AllDirectories)]; + Assert.Equal(expectedNumFiles, files.Length); + } } } \ No newline at end of file diff --git a/RecursiveExtractor.Cli.Tests/RecursiveExtractor.Cli.Tests.csproj b/RecursiveExtractor.Cli.Tests/RecursiveExtractor.Cli.Tests.csproj index f9792e34..41d07b76 100644 --- a/RecursiveExtractor.Cli.Tests/RecursiveExtractor.Cli.Tests.csproj +++ b/RecursiveExtractor.Cli.Tests/RecursiveExtractor.Cli.Tests.csproj @@ -6,13 +6,13 @@ enable false - Exe - - - + + + + diff --git a/RecursiveExtractor.Cli.Tests/Usings.cs b/RecursiveExtractor.Cli.Tests/Usings.cs index bed1fa29..2af8a547 100644 --- a/RecursiveExtractor.Cli.Tests/Usings.cs +++ b/RecursiveExtractor.Cli.Tests/Usings.cs @@ -1,2 +1,2 @@ -global using Microsoft.VisualStudio.TestTools.UnitTesting; +global using Xunit; diff --git a/RecursiveExtractor.Tests/AssemblyInfo.cs b/RecursiveExtractor.Tests/AssemblyInfo.cs new file mode 100644 index 00000000..b928bc0e --- /dev/null +++ b/RecursiveExtractor.Tests/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using Xunit; + +[assembly: CollectionBehavior(DisableTestParallelization = false)] diff --git a/RecursiveExtractor.Tests/ExtractorTests/BaseExtractorTestClass.cs b/RecursiveExtractor.Tests/ExtractorTests/BaseExtractorTestClass.cs index d223a1a1..3c6ad5e9 100644 --- a/RecursiveExtractor.Tests/ExtractorTests/BaseExtractorTestClass.cs +++ b/RecursiveExtractor.Tests/ExtractorTests/BaseExtractorTestClass.cs @@ -1,21 +1,19 @@ using Microsoft.CST.RecursiveExtractor.Tests; -using Microsoft.VisualStudio.TestTools.UnitTesting; using NLog; using NLog.Config; using NLog.Targets; +using System; namespace RecursiveExtractor.Tests.ExtractorTests; -public class BaseExtractorTestClass +/// +/// XUnit test fixture class for extractor tests. Sets up logging and test directories. Tests should use this class as a fixture via IClassFixture<BaseExtractorTestClass> to get the benefits of the setup and teardown. +/// +public class BaseExtractorTestClass : IDisposable { - [ClassCleanup] - public static void ClassCleanup() - { - TestPathHelpers.DeleteTestDirectory(); - } + protected static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger(); - [ClassInitialize] - public static void ClassInitialize(TestContext context) + static BaseExtractorTestClass() { var config = new LoggingConfiguration(); var consoleTarget = new ConsoleTarget @@ -27,5 +25,11 @@ public static void ClassInitialize(TestContext context) LogManager.Configuration = config; } - protected static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger(); + + /// + public void Dispose() + { + TestPathHelpers.DeleteTestDirectory(); + GC.SuppressFinalize(this); + } } \ No newline at end of file diff --git a/RecursiveExtractor.Tests/ExtractorTests/CustomExtractorTests.cs b/RecursiveExtractor.Tests/ExtractorTests/CustomExtractorTests.cs index 547ba87d..f107709f 100644 --- a/RecursiveExtractor.Tests/ExtractorTests/CustomExtractorTests.cs +++ b/RecursiveExtractor.Tests/ExtractorTests/CustomExtractorTests.cs @@ -1,15 +1,14 @@ using Microsoft.CST.RecursiveExtractor; using Microsoft.CST.RecursiveExtractor.Extractors; -using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; +using Xunit; namespace RecursiveExtractor.Tests.ExtractorTests; -[TestClass] public class CustomExtractorTests { /// @@ -121,43 +120,43 @@ public async IAsyncEnumerable ExtractAsync(FileEntry fileEntry, Extra } } - [TestMethod] + [Fact] public void Constructor_WithCustomExtractors_RegistersExtractors() { var customExtractor = new TestCustomExtractor(null!); var extractor = new Extractor(new[] { customExtractor }); - Assert.AreEqual(1, extractor.CustomExtractors.Count); + Assert.Single(extractor.CustomExtractors); } - [TestMethod] + [Fact] public void Constructor_WithMultipleCustomExtractors_RegistersAll() { var customExtractor1 = new TestCustomExtractor(null!); var customExtractor2 = new SecondTestCustomExtractor(null!); var extractor = new Extractor(new ICustomAsyncExtractor[] { customExtractor1, customExtractor2 }); - Assert.AreEqual(2, extractor.CustomExtractors.Count); + Assert.Equal(2, extractor.CustomExtractors.Count()); } - [TestMethod] + [Fact] public void Constructor_WithNullInCollection_IgnoresNull() { var customExtractor = new TestCustomExtractor(null!); var extractor = new Extractor(new ICustomAsyncExtractor[] { customExtractor, null! }); - Assert.AreEqual(1, extractor.CustomExtractors.Count); + Assert.Single(extractor.CustomExtractors); } - [TestMethod] + [Fact] public void Constructor_WithNullCollection_CreatesEmptyExtractor() { var extractor = new Extractor((IEnumerable)null!); - Assert.AreEqual(0, extractor.CustomExtractors.Count); + Assert.Empty(extractor.CustomExtractors); } - [TestMethod] + [Fact] public void Extract_WithMatchingCustomExtractor_UsesCustomExtractor() { var customExtractor = new TestCustomExtractor(null!); @@ -167,17 +166,17 @@ public void Extract_WithMatchingCustomExtractor_UsesCustomExtractor() var testData = System.Text.Encoding.ASCII.GetBytes("CUSTOM1 This is test data"); var results = extractor.Extract("test.custom", testData).ToList(); - Assert.AreEqual(1, results.Count); - Assert.AreEqual("extracted_from_custom.txt", results[0].Name); + Assert.Single(results); + Assert.Equal("extracted_from_custom.txt", results[0].Name); // Read the content to verify it was processed by our custom extractor using var reader = new StreamReader(results[0].Content); results[0].Content.Position = 0; var content = reader.ReadToEnd(); - Assert.AreEqual("Extracted by TestCustomExtractor", content); + Assert.Equal("Extracted by TestCustomExtractor", content); } - [TestMethod] + [Fact] public async Task ExtractAsync_WithMatchingCustomExtractor_UsesCustomExtractor() { var customExtractor = new TestCustomExtractor(null!); @@ -187,17 +186,17 @@ public async Task ExtractAsync_WithMatchingCustomExtractor_UsesCustomExtractor() var testData = System.Text.Encoding.ASCII.GetBytes("CUSTOM1 This is test data"); var results = await extractor.ExtractAsync("test.custom", testData).ToListAsync(); - Assert.AreEqual(1, results.Count); - Assert.AreEqual("extracted_from_custom.txt", results[0].Name); + Assert.Single(results); + Assert.Equal("extracted_from_custom.txt", results[0].Name); // Read the content to verify it was processed by our custom extractor using var reader = new StreamReader(results[0].Content); results[0].Content.Position = 0; var content = reader.ReadToEnd(); - Assert.AreEqual("Extracted by TestCustomExtractor", content); + Assert.Equal("Extracted by TestCustomExtractor", content); } - [TestMethod] + [Fact] public void Extract_WithoutMatchingCustomExtractor_ReturnsOriginalFile() { var customExtractor = new TestCustomExtractor(null!); @@ -208,17 +207,17 @@ public void Extract_WithoutMatchingCustomExtractor_ReturnsOriginalFile() var results = extractor.Extract("test.txt", testData).ToList(); // Should return the original file since no custom extractor matched - Assert.AreEqual(1, results.Count); - Assert.AreEqual("test.txt", results[0].Name); + Assert.Single(results); + Assert.Equal("test.txt", results[0].Name); // Verify it's the original content using var reader = new StreamReader(results[0].Content); results[0].Content.Position = 0; var content = reader.ReadToEnd(); - Assert.AreEqual("NOTCUSTOM This is test data", content); + Assert.Equal("NOTCUSTOM This is test data", content); } - [TestMethod] + [Fact] public void Extract_MultipleCustomExtractors_UsesCorrectOne() { var extractor = new Extractor(new ICustomAsyncExtractor[] @@ -230,17 +229,17 @@ public void Extract_MultipleCustomExtractors_UsesCorrectOne() // Test with first custom format var testData1 = System.Text.Encoding.ASCII.GetBytes("CUSTOM1 data"); var results1 = extractor.Extract("test1.custom", testData1).ToList(); - Assert.AreEqual(1, results1.Count); - Assert.AreEqual("extracted_from_custom.txt", results1[0].Name); + Assert.Single(results1); + Assert.Equal("extracted_from_custom.txt", results1[0].Name); // Test with second custom format var testData2 = System.Text.Encoding.ASCII.GetBytes("CUSTOM2 data"); var results2 = extractor.Extract("test2.custom", testData2).ToList(); - Assert.AreEqual(1, results2.Count); - Assert.AreEqual("extracted_from_second_custom.txt", results2[0].Name); + Assert.Single(results2); + Assert.Equal("extracted_from_second_custom.txt", results2[0].Name); } - [TestMethod] + [Fact] public void Extract_NoCustomExtractors_ReturnsOriginalFile() { var extractor = new Extractor(); @@ -250,11 +249,11 @@ public void Extract_NoCustomExtractors_ReturnsOriginalFile() var results = extractor.Extract("test.custom", testData).ToList(); // Should return the original file since no custom extractor is registered - Assert.AreEqual(1, results.Count); - Assert.AreEqual("test.custom", results[0].Name); + Assert.Single(results); + Assert.Equal("test.custom", results[0].Name); } - [TestMethod] + [Fact] public void Extract_CustomExtractorForKnownFormat_UsesBuiltInExtractor() { var customExtractor = new TestCustomExtractor(null!); @@ -267,8 +266,8 @@ public void Extract_CustomExtractorForKnownFormat_UsesBuiltInExtractor() var results = extractor.Extract(path).ToList(); // Should extract the ZIP normally, not use the custom extractor - Assert.IsTrue(results.Count > 0); - Assert.IsTrue(results.Any(r => r.Name.Contains("EmptyFile"))); + Assert.True(results.Count > 0); + Assert.Contains(results, r => r.Name.Contains("EmptyFile")); } } } diff --git a/RecursiveExtractor.Tests/ExtractorTests/DisposeBehaviorTests.cs b/RecursiveExtractor.Tests/ExtractorTests/DisposeBehaviorTests.cs index bab30610..4d7a6277 100644 --- a/RecursiveExtractor.Tests/ExtractorTests/DisposeBehaviorTests.cs +++ b/RecursiveExtractor.Tests/ExtractorTests/DisposeBehaviorTests.cs @@ -1,50 +1,48 @@ using Microsoft.CST.RecursiveExtractor; using Microsoft.CST.RecursiveExtractor.Tests; -using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Collections.Generic; using System.IO; using System.Threading.Tasks; +using Xunit; namespace RecursiveExtractor.Tests.ExtractorTests; -[TestClass] -public class DisposeBehaviorTests : BaseExtractorTestClass +[Collection(ExtractorTestCollection.Name)] +public class DisposeBehaviorTests { - [DataTestMethod] - [DataRow("TestData.7z", 3, false)] - [DataRow("TestData.tar", 6, false)] - [DataRow("TestData.rar", 3, false)] - [DataRow("TestData.rar4", 3, false)] - [DataRow("TestData.tar.bz2", 6, false)] - [DataRow("TestData.tar.gz", 6, false)] - [DataRow("TestData.tar.xz", 3, false)] - [DataRow("sysvbanner_1.0-17fakesync1_amd64.deb", 8, false)] - [DataRow("TestData.a", 3, false)] - [DataRow("TestData.bsd.ar", 3, false)] - [DataRow("TestData.iso", 3, false)] - [DataRow("TestData.vhdx", 3, false)] - [DataRow("TestData.wim", 3, false)] - [DataRow("EmptyFile.txt", 1, false)] - [DataRow("TestData.zip", 5, true)] - [DataRow("TestData.7z", 3, true)] - [DataRow("TestData.tar", 6, true)] - [DataRow("TestData.rar", 3, true)] - [DataRow("TestData.rar4", 3, true)] - [DataRow("TestData.tar.bz2", 6, true)] - [DataRow("TestData.tar.gz", 6, true)] - [DataRow("TestData.tar.xz", 3, true)] - [DataRow("sysvbanner_1.0-17fakesync1_amd64.deb", 8, true)] - [DataRow("TestData.a", 3, true)] - [DataRow("TestData.bsd.ar", 3, true)] - [DataRow("TestData.iso", 3, true)] - [DataRow("TestData.vhdx", 3, true)] - [DataRow("TestData.wim", 3, true)] - [DataRow("EmptyFile.txt", 1, true)] - [DataRow("TestDataArchivesNested.Zip", 54, true)] - [DataRow("TestDataArchivesNested.Zip", 54, false)] - [DataRow("TestDataArchivesNested.Zip", 54, true)] - [DataRow("TestDataArchivesNested.Zip", 54, false)] + [Theory] + [InlineData("TestData.7z", 3, false)] + [InlineData("TestData.tar", 6, false)] + [InlineData("TestData.rar", 3, false)] + [InlineData("TestData.rar4", 3, false)] + [InlineData("TestData.tar.bz2", 6, false)] + [InlineData("TestData.tar.gz", 6, false)] + [InlineData("TestData.tar.xz", 3, false)] + [InlineData("sysvbanner_1.0-17fakesync1_amd64.deb", 8, false)] + [InlineData("TestData.a", 3, false)] + [InlineData("TestData.bsd.ar", 3, false)] + [InlineData("TestData.iso", 3, false)] + [InlineData("TestData.vhdx", 3, false)] + [InlineData("TestData.wim", 3, false)] + [InlineData("EmptyFile.txt", 1, false)] + [InlineData("TestData.zip", 5, true)] + [InlineData("TestData.7z", 3, true)] + [InlineData("TestData.tar", 6, true)] + [InlineData("TestData.rar", 3, true)] + [InlineData("TestData.rar4", 3, true)] + [InlineData("TestData.tar.bz2", 6, true)] + [InlineData("TestData.tar.gz", 6, true)] + [InlineData("TestData.tar.xz", 3, true)] + [InlineData("sysvbanner_1.0-17fakesync1_amd64.deb", 8, true)] + [InlineData("TestData.a", 3, true)] + [InlineData("TestData.bsd.ar", 3, true)] + [InlineData("TestData.iso", 3, true)] + [InlineData("TestData.vhdx", 3, true)] + [InlineData("TestData.wim", 3, true)] + [InlineData("EmptyFile.txt", 1, true)] + [InlineData("TestDataArchivesNested.Zip", 54, true)] + [InlineData("TestDataArchivesNested.Zip", 54, false)] public void ExtractArchiveAndDisposeWhileEnumerating(string fileName, int expectedNumFiles = 3, bool parallel = false) { @@ -60,47 +58,45 @@ public void ExtractArchiveAndDisposeWhileEnumerating(string fileName, int expect _ = theStream.ReadByte(); } - Assert.AreEqual(expectedNumFiles, disposedResults.Count); + Assert.Equal(expectedNumFiles, disposedResults.Count); foreach (var disposedResult in disposedResults) { - Assert.ThrowsException(() => disposedResult.Content.Position); + Assert.Throws(() => disposedResult.Content.Position); } } - [DataTestMethod] - [DataRow("TestData.7z", 3, false)] - [DataRow("TestData.tar", 6, false)] - [DataRow("TestData.rar", 3, false)] - [DataRow("TestData.rar4", 3, false)] - [DataRow("TestData.tar.bz2", 6, false)] - [DataRow("TestData.tar.gz", 6, false)] - [DataRow("TestData.tar.xz", 3, false)] - [DataRow("sysvbanner_1.0-17fakesync1_amd64.deb", 8, false)] - [DataRow("TestData.a", 3, false)] - [DataRow("TestData.bsd.ar", 3, false)] - [DataRow("TestData.iso", 3, false)] - [DataRow("TestData.vhdx", 3, false)] - [DataRow("TestData.wim", 3, false)] - [DataRow("EmptyFile.txt", 1, false)] - [DataRow("TestData.zip", 5, true)] - [DataRow("TestData.7z", 3, true)] - [DataRow("TestData.tar", 6, true)] - [DataRow("TestData.rar", 3, true)] - [DataRow("TestData.rar4", 3, true)] - [DataRow("TestData.tar.bz2", 6, true)] - [DataRow("TestData.tar.gz", 6, true)] - [DataRow("TestData.tar.xz", 3, true)] - [DataRow("sysvbanner_1.0-17fakesync1_amd64.deb", 8, true)] - [DataRow("TestData.a", 3, true)] - [DataRow("TestData.bsd.ar", 3, true)] - [DataRow("TestData.iso", 3, true)] - [DataRow("TestData.vhdx", 3, true)] - [DataRow("TestData.wim", 3, true)] - [DataRow("EmptyFile.txt", 1, true)] - [DataRow("TestDataArchivesNested.Zip", 54, true)] - [DataRow("TestDataArchivesNested.Zip", 54, false)] - [DataRow("TestDataArchivesNested.Zip", 54, true)] - [DataRow("TestDataArchivesNested.Zip", 54, false)] + [Theory] + [InlineData("TestData.7z", 3, false)] + [InlineData("TestData.tar", 6, false)] + [InlineData("TestData.rar", 3, false)] + [InlineData("TestData.rar4", 3, false)] + [InlineData("TestData.tar.bz2", 6, false)] + [InlineData("TestData.tar.gz", 6, false)] + [InlineData("TestData.tar.xz", 3, false)] + [InlineData("sysvbanner_1.0-17fakesync1_amd64.deb", 8, false)] + [InlineData("TestData.a", 3, false)] + [InlineData("TestData.bsd.ar", 3, false)] + [InlineData("TestData.iso", 3, false)] + [InlineData("TestData.vhdx", 3, false)] + [InlineData("TestData.wim", 3, false)] + [InlineData("EmptyFile.txt", 1, false)] + [InlineData("TestData.zip", 5, true)] + [InlineData("TestData.7z", 3, true)] + [InlineData("TestData.tar", 6, true)] + [InlineData("TestData.rar", 3, true)] + [InlineData("TestData.rar4", 3, true)] + [InlineData("TestData.tar.bz2", 6, true)] + [InlineData("TestData.tar.gz", 6, true)] + [InlineData("TestData.tar.xz", 3, true)] + [InlineData("sysvbanner_1.0-17fakesync1_amd64.deb", 8, true)] + [InlineData("TestData.a", 3, true)] + [InlineData("TestData.bsd.ar", 3, true)] + [InlineData("TestData.iso", 3, true)] + [InlineData("TestData.vhdx", 3, true)] + [InlineData("TestData.wim", 3, true)] + [InlineData("EmptyFile.txt", 1, true)] + [InlineData("TestDataArchivesNested.Zip", 54, true)] + [InlineData("TestDataArchivesNested.Zip", 54, false)] public async Task ExtractArchiveAndDisposeWhileEnumeratingAsync(string fileName, int expectedNumFiles = 3, bool parallel = false) { @@ -116,15 +112,15 @@ public async Task ExtractArchiveAndDisposeWhileEnumeratingAsync(string fileName, _ = theStream.ReadByte(); } - Assert.AreEqual(expectedNumFiles, disposedResults.Count); + Assert.Equal(expectedNumFiles, disposedResults.Count); foreach (var disposedResult in disposedResults) { - Assert.ThrowsException(() => disposedResult.Content.Position); + Assert.Throws(() => disposedResult.Content.Position); } } - [DataTestMethod] - [DataRow("TestData.zip")] + [Theory] + [InlineData("TestData.zip")] public void EnsureDisposedWithExtractToDirectory(string fileName) { var directory = TestPathHelpers.GetFreshTestDirectory(); @@ -137,17 +133,26 @@ public void EnsureDisposedWithExtractToDirectory(string fileName) var extractor = new Extractor(); extractor.ExtractToDirectory(directory, copyPath); File.Delete(copyPath); - if (Directory.Exists(directory)) + try { - Directory.Delete(directory, true); + if (Directory.Exists(directory)) + { + Directory.Delete(directory, true); + } } - if (Directory.Exists(copyDirectory)) { - Directory.Delete(copyDirectory, true); + catch (DirectoryNotFoundException) { } + try + { + if (Directory.Exists(copyDirectory)) + { + Directory.Delete(copyDirectory, true); + } } + catch (DirectoryNotFoundException) { } } - [DataTestMethod] - [DataRow("TestData.zip")] + [Theory] + [InlineData("TestData.zip")] public async Task EnsureDisposedWithExtractToDirectoryAsync(string fileName) { var directory = TestPathHelpers.GetFreshTestDirectory(); @@ -160,13 +165,21 @@ public async Task EnsureDisposedWithExtractToDirectoryAsync(string fileName) var extractor = new Extractor(); await extractor.ExtractToDirectoryAsync(directory, copyPath); File.Delete(copyPath); - if (Directory.Exists(directory)) + try { - Directory.Delete(directory, true); + if (Directory.Exists(directory)) + { + Directory.Delete(directory, true); + } } - if (Directory.Exists(copyDirectory)) + catch (DirectoryNotFoundException) { } + try { - Directory.Delete(copyDirectory, true); + if (Directory.Exists(copyDirectory)) + { + Directory.Delete(copyDirectory, true); + } } + catch (DirectoryNotFoundException) { } } } \ No newline at end of file diff --git a/RecursiveExtractor.Tests/ExtractorTests/EncryptedArchiveTests.cs b/RecursiveExtractor.Tests/ExtractorTests/EncryptedArchiveTests.cs index 9d034e73..50a5d003 100644 --- a/RecursiveExtractor.Tests/ExtractorTests/EncryptedArchiveTests.cs +++ b/RecursiveExtractor.Tests/ExtractorTests/EncryptedArchiveTests.cs @@ -1,37 +1,36 @@ using Microsoft.CST.RecursiveExtractor; -using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; +using Xunit; namespace RecursiveExtractor.Tests.ExtractorTests; -[TestClass] -public class EncryptedArchiveTests : BaseExtractorTestClass +public class EncryptedArchiveTests { - [DataTestMethod] - [DataRow("TestDataEncryptedZipCrypto.zip")] - [DataRow("TestDataEncryptedAes.zip")] - [DataRow("TestDataEncrypted.7z")] - [DataRow("TestDataEncrypted.rar4")] - [DataRow("TestDataEncrypted.rar")] + [Theory] + [InlineData("TestDataEncryptedZipCrypto.zip")] + [InlineData("TestDataEncryptedAes.zip")] + [InlineData("TestDataEncrypted.7z")] + [InlineData("TestDataEncrypted.rar4")] + [InlineData("TestDataEncrypted.rar")] public void FileTypeSetCorrectlyForEncryptedArchives(string fileName, int expectedNumFiles = 1) { var extractor = new Extractor(); var path = Path.Combine(Directory.GetCurrentDirectory(), "TestData", "TestDataArchives", fileName); var results = extractor.Extract(path, new ExtractorOptions() { Parallel = false }).ToList(); - Assert.AreEqual(expectedNumFiles, results.Count()); - Assert.AreEqual(FileEntryStatus.EncryptedArchive, results.First().EntryStatus); + Assert.Equal(expectedNumFiles, results.Count()); + Assert.Equal(FileEntryStatus.EncryptedArchive, results.First().EntryStatus); } - [DataTestMethod] - [DataRow("TestDataEncryptedZipCrypto.zip")] - [DataRow("TestDataEncryptedAes.zip")] - [DataRow("TestDataEncrypted.7z")] - [DataRow("TestDataEncrypted.rar4")] - [DataRow("TestDataEncrypted.rar")] + [Theory] + [InlineData("TestDataEncryptedZipCrypto.zip")] + [InlineData("TestDataEncryptedAes.zip")] + [InlineData("TestDataEncrypted.7z")] + [InlineData("TestDataEncrypted.rar4")] + [InlineData("TestDataEncrypted.rar")] public async Task FileTypeSetCorrectlyForEncryptedArchivesAsync(string fileName, int expectedNumFiles = 1) { var extractor = new Extractor(); @@ -42,36 +41,36 @@ public async Task FileTypeSetCorrectlyForEncryptedArchivesAsync(string fileName, results.Add(entry); } - Assert.AreEqual(expectedNumFiles, results.Count); - Assert.AreEqual(FileEntryStatus.EncryptedArchive, results.First().EntryStatus); + Assert.Equal(expectedNumFiles, results.Count); + Assert.Equal(FileEntryStatus.EncryptedArchive, results.First().EntryStatus); } - [DataTestMethod] - [DataRow("TestDataEncryptedZipCrypto.zip")] - [DataRow("TestDataEncryptedAes.zip")] - [DataRow("TestDataEncrypted.7z")] - [DataRow("TestDataEncrypted.rar4")] - [DataRow("EncryptedWithPlainNames.7z")] - [DataRow("EncryptedWithPlainNames.rar4")] - //[DataRow("TestDataEncrypted.rar")] // RAR5 is not yet supported by SharpCompress: https://github.com/adamhathcock/sharpcompress/issues/517 + [Theory] + [InlineData("TestDataEncryptedZipCrypto.zip")] + [InlineData("TestDataEncryptedAes.zip")] + [InlineData("TestDataEncrypted.7z")] + [InlineData("TestDataEncrypted.rar4")] + [InlineData("EncryptedWithPlainNames.7z")] + [InlineData("EncryptedWithPlainNames.rar4")] + //[InlineData("TestDataEncrypted.rar")] // RAR5 is not yet supported by SharpCompress: https://github.com/adamhathcock/sharpcompress/issues/517 public void ExtractEncryptedArchive(string fileName, int expectedNumFiles = 3) { var extractor = new Extractor(); var path = Path.Combine(Directory.GetCurrentDirectory(), "TestData", "TestDataArchives", fileName); var results = extractor.Extract(path, new ExtractorOptions() { Passwords = TestArchivePasswords, ExtractSelfOnFail = false }) .ToList(); // Make this a list so it fully populates - Assert.AreEqual(expectedNumFiles, results.Count); - Assert.AreEqual(0, results.Count(x => x.EntryStatus == FileEntryStatus.EncryptedArchive || x.EntryStatus == FileEntryStatus.FailedArchive)); + Assert.Equal(expectedNumFiles, results.Count); + Assert.Equal(0, results.Count(x => x.EntryStatus == FileEntryStatus.EncryptedArchive || x.EntryStatus == FileEntryStatus.FailedArchive)); } - [DataTestMethod] - [DataRow("TestDataEncryptedZipCrypto.zip")] - [DataRow("TestDataEncryptedAes.zip")] - [DataRow("TestDataEncrypted.7z")] - [DataRow("TestDataEncrypted.rar4")] - [DataRow("EncryptedWithPlainNames.7z")] - [DataRow("EncryptedWithPlainNames.rar4")] - //[DataRow("TestDataEncrypted.rar")] // RAR5 is not yet supported by SharpCompress: https://github.com/adamhathcock/sharpcompress/issues/517 + [Theory] + [InlineData("TestDataEncryptedZipCrypto.zip")] + [InlineData("TestDataEncryptedAes.zip")] + [InlineData("TestDataEncrypted.7z")] + [InlineData("TestDataEncrypted.rar4")] + [InlineData("EncryptedWithPlainNames.7z")] + [InlineData("EncryptedWithPlainNames.rar4")] + //[InlineData("TestDataEncrypted.rar")] // RAR5 is not yet supported by SharpCompress: https://github.com/adamhathcock/sharpcompress/issues/517 public async Task ExtractEncryptedArchiveAsync(string fileName, int expectedNumFiles = 3) { var extractor = new Extractor(); @@ -88,8 +87,8 @@ public async Task ExtractEncryptedArchiveAsync(string fileName, int expectedNumF } } - Assert.AreEqual(expectedNumFiles, numEntries); - Assert.AreEqual(0, numEntriesEncrypted); + Assert.Equal(expectedNumFiles, numEntries); + Assert.Equal(0, numEntriesEncrypted); } diff --git a/RecursiveExtractor.Tests/ExtractorTests/ExpectedNumFilesTests.cs b/RecursiveExtractor.Tests/ExtractorTests/ExpectedNumFilesTests.cs index ab9544cf..7535716a 100644 --- a/RecursiveExtractor.Tests/ExtractorTests/ExpectedNumFilesTests.cs +++ b/RecursiveExtractor.Tests/ExtractorTests/ExpectedNumFilesTests.cs @@ -2,48 +2,48 @@ using Microsoft.CST.RecursiveExtractor; using Microsoft.CST.RecursiveExtractor.Tests; -using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; +using Xunit; namespace RecursiveExtractor.Tests.ExtractorTests { - [TestClass] - public class ExpectedNumFilesTests : BaseExtractorTestClass + [Collection(ExtractorTestCollection.Name)] + public class ExpectedNumFilesTests { /// /// Mapping from Test archive name to expected number of files to extract /// - public static IEnumerable ArchiveData + public static TheoryData ArchiveData { get { - return new[] + return new TheoryData { - new object[] { "100Trees.7z", 101 }, - new object[] { "TestData.zip", 5 }, - new object[] { "TestData.7z",3 }, - new object[] { "TestData.tar", 6 }, - new object[] { "TestData.rar",3 }, - new object[] { "TestData.rar4",3 }, - new object[] { "TestData.tar.bz2", 6 }, - new object[] { "TestData.tar.gz", 6 }, - new object[] { "TestData.tar.xz",3 }, - new object[] { "sysvbanner_1.0-17fakesync1_amd64.deb", 8 }, - new object[] { "TestData.a",3 }, - new object[] { "TestData.bsd.ar",3 }, - new object[] { "TestData.iso",3 }, - new object[] { "TestData.vhdx",3 }, - new object[] { "TestData.wim",3 }, - new object[] { "EmptyFile.txt", 1 }, - new object[] { "TestDataArchivesNested.Zip", 54 }, - new object[] { "UdfTest.iso", 3 }, - new object[] { "UdfTestWithMultiSystem.iso", 3 }, -// new object[] { "HfsSampleUDCO.dmg", 2 } + { "100Trees.7z", 101 }, + { "TestData.zip", 5 }, + { "TestData.7z",3 }, + { "TestData.tar", 6 }, + { "TestData.rar",3 }, + { "TestData.rar4",3 }, + { "TestData.tar.bz2", 6 }, + { "TestData.tar.gz", 6 }, + { "TestData.tar.xz",3 }, + { "sysvbanner_1.0-17fakesync1_amd64.deb", 8 }, + { "TestData.a",3 }, + { "TestData.bsd.ar",3 }, + { "TestData.iso",3 }, + { "TestData.vhdx",3 }, + { "TestData.wim",3 }, + { "EmptyFile.txt", 1 }, + { "TestDataArchivesNested.Zip", 54 }, + { "UdfTest.iso", 3 }, + { "UdfTestWithMultiSystem.iso", 3 }, +// { "HfsSampleUDCO.dmg", 2 } }; } } @@ -51,31 +51,31 @@ public static IEnumerable ArchiveData /// /// Mapping from Test archive name to expected number of files to extract when recursion is disabled /// - public static IEnumerable NoRecursionData + public static TheoryData NoRecursionData { get { - return new[] + return new TheoryData { - new object[] { "100Trees.7z", 101 }, - new object[] { "TestData.zip", 5 }, - new object[] { "TestData.7z", 3 }, - new object[] { "TestData.tar", 6 }, - new object[] { "TestData.rar", 3 }, - new object[] { "TestData.rar4", 3 }, - new object[] { "TestData.tar.bz2", 1 }, - new object[] { "TestData.tar.gz", 1 }, - new object[] { "TestData.tar.xz", 1 }, - new object[] { "sysvbanner_1.0-17fakesync1_amd64.deb", 2 }, - new object[] { "TestData.a", 3 }, - new object[] { "TestData.bsd.ar", 3 }, - new object[] { "TestData.iso", 3 }, - new object[] { "TestData.vhdx", 3 }, - new object[] { "TestData.wim", 3 }, - new object[] { "EmptyFile.txt", 1 }, - new object[] { "TestDataArchivesNested.Zip", 14 }, - new object[] { "UdfTestWithMultiSystem.iso", 3 }, -// new object[] { "HfsSampleUDCO.dmg", 2 } + { "100Trees.7z", 101 }, + { "TestData.zip", 5 }, + { "TestData.7z", 3 }, + { "TestData.tar", 6 }, + { "TestData.rar", 3 }, + { "TestData.rar4", 3 }, + { "TestData.tar.bz2", 1 }, + { "TestData.tar.gz", 1 }, + { "TestData.tar.xz", 1 }, + { "sysvbanner_1.0-17fakesync1_amd64.deb", 2 }, + { "TestData.a", 3 }, + { "TestData.bsd.ar", 3 }, + { "TestData.iso", 3 }, + { "TestData.vhdx", 3 }, + { "TestData.wim", 3 }, + { "EmptyFile.txt", 1 }, + { "TestDataArchivesNested.Zip", 14 }, + { "UdfTestWithMultiSystem.iso", 3 }, +// { "HfsSampleUDCO.dmg", 2 } }; } } @@ -85,18 +85,18 @@ private ExtractorOptions GetExtractorOptions(bool parallel = false) return parallel ? defaultExtractorTestOptionsParallel : defaultExtractorTestOptions; } - private ExtractorOptions defaultExtractorTestOptions = new ExtractorOptions() { MaxExtractedBytesRatio = 300 }; - private ExtractorOptions defaultExtractorTestOptionsParallel = new ExtractorOptions() { Parallel = true, MaxExtractedBytesRatio = 300 }; + private readonly ExtractorOptions defaultExtractorTestOptions = new() { MaxExtractedBytesRatio = 300 }; + private readonly ExtractorOptions defaultExtractorTestOptionsParallel = new() { Parallel = true, MaxExtractedBytesRatio = 300 }; - [TestMethod] - [DynamicData(nameof(ArchiveData))] + [Theory] + [MemberData(nameof(ArchiveData))] public void ExtractArchiveToDirectoryParallel(string fileName, int expectedNumFiles) { ExtractArchiveToDirectory(fileName, expectedNumFiles, true); } - [TestMethod] - [DynamicData(nameof(ArchiveData))] + [Theory] + [MemberData(nameof(ArchiveData))] public void ExtractArchiveToDirectorySingleThread(string fileName, int expectedNumFiles) { ExtractArchiveToDirectory(fileName, expectedNumFiles, false); @@ -113,27 +113,27 @@ internal void ExtractArchiveToDirectory(string fileName, int expectedNumFiles, b { files = Directory.EnumerateFiles(directory, "*.*", SearchOption.AllDirectories).ToArray(); } - Assert.AreEqual(expectedNumFiles, files.Length); + Assert.Equal(expectedNumFiles, files.Length); } - [TestMethod] - [DynamicData(nameof(ArchiveData))] + [Theory] + [MemberData(nameof(ArchiveData))] public async Task ExtractArchiveToDirectoryAsync(string fileName, int expectedNumFiles) { var directory = TestPathHelpers.GetFreshTestDirectory(); var path = Path.Combine(Directory.GetCurrentDirectory(), "TestData", "TestDataArchives", fileName); var extractor = new Extractor(); - Assert.AreEqual(ExtractionStatusCode.Ok, await extractor.ExtractToDirectoryAsync(directory, path, GetExtractorOptions()).ConfigureAwait(false)); + Assert.Equal(ExtractionStatusCode.Ok, await extractor.ExtractToDirectoryAsync(directory, path, GetExtractorOptions())); var files = Array.Empty(); if (Directory.Exists(directory)) { files = Directory.EnumerateFiles(directory, "*.*", SearchOption.AllDirectories).ToArray(); } - Assert.AreEqual(expectedNumFiles, files.Length); + Assert.Equal(expectedNumFiles, files.Length); } - [TestMethod] - [DynamicData(nameof(NoRecursionData))] + [Theory] + [MemberData(nameof(NoRecursionData))] public async Task ExtractArchiveAsyncNoRecursion(string fileName, int expectedNumFiles) { var extractor = new Extractor(); @@ -145,11 +145,11 @@ public async Task ExtractArchiveAsyncNoRecursion(string fileName, int expectedNu { numResults++; } - Assert.AreEqual(expectedNumFiles, numResults); + Assert.Equal(expectedNumFiles, numResults); } - [TestMethod] - [DynamicData(nameof(NoRecursionData))] + [Theory] + [MemberData(nameof(NoRecursionData))] public void ExtractArchiveParallelNoRecursion(string fileName, int expectedNumFiles) { var extractor = new Extractor(); @@ -157,11 +157,11 @@ public void ExtractArchiveParallelNoRecursion(string fileName, int expectedNumFi var opts = GetExtractorOptions(true); opts.Recurse = false; var results = extractor.Extract(path, opts); - Assert.AreEqual(expectedNumFiles, results.Count(entry => entry.EntryStatus == FileEntryStatus.Default)); + Assert.Equal(expectedNumFiles, results.Count(entry => entry.EntryStatus == FileEntryStatus.Default)); } - [TestMethod] - [DynamicData(nameof(NoRecursionData))] + [Theory] + [MemberData(nameof(NoRecursionData))] public void ExtractArchiveNoRecursion(string fileName, int expectedNumFiles) { var extractor = new Extractor(); @@ -169,11 +169,11 @@ public void ExtractArchiveNoRecursion(string fileName, int expectedNumFiles) var opts = GetExtractorOptions(); opts.Recurse = false; var results = extractor.Extract(path, opts); - Assert.AreEqual(expectedNumFiles, results.Count(entry => entry.EntryStatus == FileEntryStatus.Default)); + Assert.Equal(expectedNumFiles, results.Count(entry => entry.EntryStatus == FileEntryStatus.Default)); } - [TestMethod] - [DynamicData(nameof(ArchiveData))] + [Theory] + [MemberData(nameof(ArchiveData))] public void ExtractArchive(string fileName, int expectedNumFiles) { var extractor = new Extractor(); @@ -181,13 +181,13 @@ public void ExtractArchive(string fileName, int expectedNumFiles) var results = extractor.Extract(path, GetExtractorOptions()).ToList(); foreach (var result in results) { - Assert.AreNotEqual(FileEntryStatus.FailedArchive, result.EntryStatus); + Assert.NotEqual(FileEntryStatus.FailedArchive, result.EntryStatus); } - Assert.AreEqual(expectedNumFiles, results.Count); + Assert.Equal(expectedNumFiles, results.Count); } - [TestMethod] - [DynamicData(nameof(ArchiveData))] + [Theory] + [MemberData(nameof(ArchiveData))] public void ExtractArchiveParallel(string fileName, int expectedNumFiles) { var extractor = new Extractor(); @@ -195,11 +195,11 @@ public void ExtractArchiveParallel(string fileName, int expectedNumFiles) var results = extractor.Extract(path, GetExtractorOptions(true)).ToList(); var names = results.Select(x => x.FullPath); var stringOfNames = string.Join("\n", names); - Assert.AreEqual(expectedNumFiles, results.Count()); + Assert.Equal(expectedNumFiles, results.Count); } - [TestMethod] - [DynamicData(nameof(ArchiveData))] + [Theory] + [MemberData(nameof(ArchiveData))] public async Task ExtractArchiveAsync(string fileName, int expectedNumFiles) { var extractor = new Extractor(); @@ -215,41 +215,41 @@ public async Task ExtractArchiveAsync(string fileName, int expectedNumFiles) files++; } } - Assert.AreEqual(expectedNumFiles, numFound); - Assert.AreEqual(expectedNumFiles, files); + Assert.Equal(expectedNumFiles, numFound); + Assert.Equal(expectedNumFiles, files); } - [TestMethod] - [DynamicData(nameof(ArchiveData))] + [Theory] + [MemberData(nameof(ArchiveData))] public async Task ExtractArchiveFromStreamAsync(string fileName, int expectedNumFiles) { - var extractor = new Extractor(); + var extractor = new Extractor(); var path = Path.Combine(Directory.GetCurrentDirectory(), "TestData", "TestDataArchives", fileName); - using var stream = new FileStream(path, FileMode.Open); + using var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); var results = extractor.ExtractAsync(path, stream, new ExtractorOptions()); var numFiles = 0; await foreach (var result in results) { numFiles++; } - Assert.AreEqual(expectedNumFiles, numFiles); + Assert.Equal(expectedNumFiles, numFiles); stream.Close(); } - [TestMethod] - [DynamicData(nameof(ArchiveData))] + [Theory] + [MemberData(nameof(ArchiveData))] public void ExtractArchiveFromStream(string fileName, int expectedNumFiles) { var extractor = new Extractor(); var path = Path.Combine(Directory.GetCurrentDirectory(), "TestData", "TestDataArchives", fileName); - using var stream = new FileStream(path, FileMode.Open); + using var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); var results = extractor.Extract(path, stream, GetExtractorOptions()); - Assert.AreEqual(expectedNumFiles, results.Count()); + Assert.Equal(expectedNumFiles, results.Count()); stream.Close(); } - [TestMethod] - [DynamicData(nameof(ArchiveData))] + [Theory] + [MemberData(nameof(ArchiveData))] public void ExtractArchiveSmallBatchSize(string fileName, int expectedNumFiles) { var extractor = new Extractor(); @@ -257,7 +257,7 @@ public void ExtractArchiveSmallBatchSize(string fileName, int expectedNumFiles) var opts = GetExtractorOptions(true); opts.BatchSize = 2; var results = extractor.Extract(path, opts); - Assert.AreEqual(expectedNumFiles, results.Count(entry => entry.EntryStatus == FileEntryStatus.Default)); + Assert.Equal(expectedNumFiles, results.Count(entry => entry.EntryStatus == FileEntryStatus.Default)); } } } \ No newline at end of file diff --git a/RecursiveExtractor.Tests/ExtractorTests/ExtractorTestCollection.cs b/RecursiveExtractor.Tests/ExtractorTests/ExtractorTestCollection.cs new file mode 100644 index 00000000..08796b5c --- /dev/null +++ b/RecursiveExtractor.Tests/ExtractorTests/ExtractorTestCollection.cs @@ -0,0 +1,16 @@ +using Xunit; + +namespace RecursiveExtractor.Tests.ExtractorTests; + +/// +/// Defines a shared test collection so that all extractor test classes share a single +/// fixture instance. The fixture is created once +/// before the first test runs and disposed once after the last test completes, +/// avoiding the race condition where parallel class-level fixtures prematurely +/// delete the shared temp directory while other classes are still running. +/// +[CollectionDefinition(Name)] +public class ExtractorTestCollection : ICollectionFixture +{ + public const string Name = "Extractor Tests"; +} diff --git a/RecursiveExtractor.Tests/ExtractorTests/FilterTests.cs b/RecursiveExtractor.Tests/ExtractorTests/FilterTests.cs index 4fcc28aa..a7442d0a 100644 --- a/RecursiveExtractor.Tests/ExtractorTests/FilterTests.cs +++ b/RecursiveExtractor.Tests/ExtractorTests/FilterTests.cs @@ -1,32 +1,31 @@ -using Microsoft.CST.RecursiveExtractor; -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.CST.RecursiveExtractor; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; +using Xunit; namespace RecursiveExtractor.Tests.ExtractorTests; -[TestClass] -public class FilterTests : BaseExtractorTestClass +public class FilterTests { - [DataTestMethod] - [DataRow("TestData.zip")] - [DataRow("TestData.7z")] - [DataRow("TestData.tar")] - [DataRow("TestData.rar")] - [DataRow("TestData.rar4")] - [DataRow("TestData.tar.bz2")] - [DataRow("TestData.tar.gz")] - [DataRow("TestData.tar.xz")] - [DataRow("sysvbanner_1.0-17fakesync1_amd64.deb", 0)] - [DataRow("TestData.a", 0)] - [DataRow("TestData.bsd.ar", 0)] - [DataRow("TestData.iso")] - [DataRow("TestData.vhdx")] - [DataRow("TestData.wim")] - [DataRow("EmptyFile.txt", 0)] - [DataRow("TestDataArchivesNested.Zip", 9)] + [Theory] + [InlineData("TestData.zip")] + [InlineData("TestData.7z")] + [InlineData("TestData.tar")] + [InlineData("TestData.rar")] + [InlineData("TestData.rar4")] + [InlineData("TestData.tar.bz2")] + [InlineData("TestData.tar.gz")] + [InlineData("TestData.tar.xz")] + [InlineData("sysvbanner_1.0-17fakesync1_amd64.deb", 0)] + [InlineData("TestData.a", 0)] + [InlineData("TestData.bsd.ar", 0)] + [InlineData("TestData.iso")] + [InlineData("TestData.vhdx")] + [InlineData("TestData.wim")] + [InlineData("EmptyFile.txt", 0)] + [InlineData("TestDataArchivesNested.Zip", 9)] public async Task ExtractArchiveAsyncAllowFiltered(string fileName, int expectedNumFiles = 1) { var extractor = new Extractor(); @@ -39,129 +38,129 @@ public async Task ExtractArchiveAsyncAllowFiltered(string fileName, int expected numResults++; } - Assert.AreEqual(expectedNumFiles, numResults); + Assert.Equal(expectedNumFiles, numResults); } - [DataTestMethod] - [DataRow("TestData.zip")] - [DataRow("TestData.7z")] - [DataRow("TestData.tar")] - [DataRow("TestData.rar")] - [DataRow("TestData.rar4")] - [DataRow("TestData.tar.bz2")] - [DataRow("TestData.tar.gz")] - [DataRow("TestData.tar.xz")] - [DataRow("sysvbanner_1.0-17fakesync1_amd64.deb", 0)] - [DataRow("TestData.a", 0)] - [DataRow("TestData.bsd.ar", 0)] - [DataRow("TestData.iso")] - [DataRow("TestData.vhdx")] - [DataRow("TestData.wim")] - [DataRow("EmptyFile.txt", 0)] - [DataRow("TestDataArchivesNested.Zip", 9)] + [Theory] + [InlineData("TestData.zip")] + [InlineData("TestData.7z")] + [InlineData("TestData.tar")] + [InlineData("TestData.rar")] + [InlineData("TestData.rar4")] + [InlineData("TestData.tar.bz2")] + [InlineData("TestData.tar.gz")] + [InlineData("TestData.tar.xz")] + [InlineData("sysvbanner_1.0-17fakesync1_amd64.deb", 0)] + [InlineData("TestData.a", 0)] + [InlineData("TestData.bsd.ar", 0)] + [InlineData("TestData.iso")] + [InlineData("TestData.vhdx")] + [InlineData("TestData.wim")] + [InlineData("EmptyFile.txt", 0)] + [InlineData("TestDataArchivesNested.Zip", 9)] public void ExtractArchiveAllowFiltered(string fileName, int expectedNumFiles = 1) { var extractor = new Extractor(); var path = Path.Combine(Directory.GetCurrentDirectory(), "TestData", "TestDataArchives", fileName); var results = extractor.Extract(path, new ExtractorOptions() { AllowFilters = new string[] { "**/Bar/**", "**/TestData.tar" } }); - Assert.AreEqual(expectedNumFiles, results.Count()); + Assert.Equal(expectedNumFiles, results.Count()); } - [DataTestMethod] - [DataRow("TestData.zip")] - [DataRow("TestData.7z")] - [DataRow("TestData.tar")] - [DataRow("TestData.rar")] - [DataRow("TestData.rar4")] - [DataRow("TestData.tar.bz2")] - [DataRow("TestData.tar.gz")] - [DataRow("TestData.tar.xz")] - [DataRow("sysvbanner_1.0-17fakesync1_amd64.deb", 0)] - [DataRow("TestData.a", 0)] - [DataRow("TestData.bsd.ar", 0)] - [DataRow("TestData.iso")] - [DataRow("TestData.vhdx")] - [DataRow("TestData.wim")] - [DataRow("EmptyFile.txt", 0)] - [DataRow("TestDataArchivesNested.Zip", 9)] + [Theory] + [InlineData("TestData.zip")] + [InlineData("TestData.7z")] + [InlineData("TestData.tar")] + [InlineData("TestData.rar")] + [InlineData("TestData.rar4")] + [InlineData("TestData.tar.bz2")] + [InlineData("TestData.tar.gz")] + [InlineData("TestData.tar.xz")] + [InlineData("sysvbanner_1.0-17fakesync1_amd64.deb", 0)] + [InlineData("TestData.a", 0)] + [InlineData("TestData.bsd.ar", 0)] + [InlineData("TestData.iso")] + [InlineData("TestData.vhdx")] + [InlineData("TestData.wim")] + [InlineData("EmptyFile.txt", 0)] + [InlineData("TestDataArchivesNested.Zip", 9)] public void ExtractArchiveParallelAllowFiltered(string fileName, int expectedNumFiles = 1) { var extractor = new Extractor(); var path = Path.Combine(Directory.GetCurrentDirectory(), "TestData", "TestDataArchives", fileName); var results = extractor.Extract(path, new ExtractorOptions() { Parallel = true, AllowFilters = new string[] { "**/Bar/**", "**/TestData.tar" } }); - Assert.AreEqual(expectedNumFiles, results.Count()); + Assert.Equal(expectedNumFiles, results.Count()); } - [DataTestMethod] - [DataRow("TestData.zip", 4)] - [DataRow("TestData.7z")] - [DataRow("TestData.tar", 5)] - [DataRow("TestData.rar")] - [DataRow("TestData.rar4")] - [DataRow("TestData.tar.bz2", 5)] - [DataRow("TestData.tar.gz", 5)] - [DataRow("TestData.tar.xz")] - [DataRow("sysvbanner_1.0-17fakesync1_amd64.deb", 8)] - [DataRow("TestData.a", 3)] - [DataRow("TestData.bsd.ar", 3)] - [DataRow("TestData.iso")] - [DataRow("TestData.vhdx")] - [DataRow("TestData.wim")] - [DataRow("EmptyFile.txt", 1)] - [DataRow("TestDataArchivesNested.Zip", 45)] + [Theory] + [InlineData("TestData.zip", 4)] + [InlineData("TestData.7z")] + [InlineData("TestData.tar", 5)] + [InlineData("TestData.rar")] + [InlineData("TestData.rar4")] + [InlineData("TestData.tar.bz2", 5)] + [InlineData("TestData.tar.gz", 5)] + [InlineData("TestData.tar.xz")] + [InlineData("sysvbanner_1.0-17fakesync1_amd64.deb", 8)] + [InlineData("TestData.a", 3)] + [InlineData("TestData.bsd.ar", 3)] + [InlineData("TestData.iso")] + [InlineData("TestData.vhdx")] + [InlineData("TestData.wim")] + [InlineData("EmptyFile.txt", 1)] + [InlineData("TestDataArchivesNested.Zip", 45)] public void ExtractArchiveDenyFiltered(string fileName, int expectedNumFiles = 2) { var extractor = new Extractor(); var path = Path.Combine(Directory.GetCurrentDirectory(), "TestData", "TestDataArchives", fileName); var results = extractor.Extract(path, new ExtractorOptions() { DenyFilters = new string[] { "**/Bar/**" } }); - Assert.AreEqual(expectedNumFiles, results.Count()); + Assert.Equal(expectedNumFiles, results.Count()); } - [DataTestMethod] - [DataRow("TestData.zip", 4)] - [DataRow("TestData.7z")] - [DataRow("TestData.tar", 5)] - [DataRow("TestData.rar")] - [DataRow("TestData.rar4")] - [DataRow("TestData.tar.bz2", 5)] - [DataRow("TestData.tar.gz", 5)] - [DataRow("TestData.tar.xz")] - [DataRow("sysvbanner_1.0-17fakesync1_amd64.deb", 8)] - [DataRow("TestData.a", 3)] - [DataRow("TestData.bsd.ar", 3)] - [DataRow("TestData.iso")] - [DataRow("TestData.vhdx")] - [DataRow("TestData.wim")] - [DataRow("EmptyFile.txt", 1)] - [DataRow("TestDataArchivesNested.Zip", 45)] + [Theory] + [InlineData("TestData.zip", 4)] + [InlineData("TestData.7z")] + [InlineData("TestData.tar", 5)] + [InlineData("TestData.rar")] + [InlineData("TestData.rar4")] + [InlineData("TestData.tar.bz2", 5)] + [InlineData("TestData.tar.gz", 5)] + [InlineData("TestData.tar.xz")] + [InlineData("sysvbanner_1.0-17fakesync1_amd64.deb", 8)] + [InlineData("TestData.a", 3)] + [InlineData("TestData.bsd.ar", 3)] + [InlineData("TestData.iso")] + [InlineData("TestData.vhdx")] + [InlineData("TestData.wim")] + [InlineData("EmptyFile.txt", 1)] + [InlineData("TestDataArchivesNested.Zip", 45)] public void ExtractArchiveParallelDenyFiltered(string fileName, int expectedNumFiles = 2) { var extractor = new Extractor(); var path = Path.Combine(Directory.GetCurrentDirectory(), "TestData", "TestDataArchives", fileName); var results = extractor.Extract(path, new ExtractorOptions() { Parallel = true, DenyFilters = new string[] { "**/Bar/**" } }); - Assert.AreEqual(expectedNumFiles, results.Count()); + Assert.Equal(expectedNumFiles, results.Count()); } - [DataTestMethod] - [DataRow("TestData.zip", 4)] - [DataRow("TestData.7z")] - [DataRow("TestData.tar", 5)] - [DataRow("TestData.rar")] - [DataRow("TestData.rar4")] - [DataRow("TestData.tar.bz2", 5)] - [DataRow("TestData.tar.gz", 5)] - [DataRow("TestData.tar.xz")] - [DataRow("sysvbanner_1.0-17fakesync1_amd64.deb", 8)] - [DataRow("TestData.a", 3)] - [DataRow("TestData.bsd.ar", 3)] - [DataRow("TestData.iso")] - [DataRow("TestData.vhdx")] - [DataRow("TestData.wim")] - [DataRow("EmptyFile.txt", 1)] - [DataRow("TestDataArchivesNested.Zip", 45)] + [Theory] + [InlineData("TestData.zip", 4)] + [InlineData("TestData.7z")] + [InlineData("TestData.tar", 5)] + [InlineData("TestData.rar")] + [InlineData("TestData.rar4")] + [InlineData("TestData.tar.bz2", 5)] + [InlineData("TestData.tar.gz", 5)] + [InlineData("TestData.tar.xz")] + [InlineData("sysvbanner_1.0-17fakesync1_amd64.deb", 8)] + [InlineData("TestData.a", 3)] + [InlineData("TestData.bsd.ar", 3)] + [InlineData("TestData.iso")] + [InlineData("TestData.vhdx")] + [InlineData("TestData.wim")] + [InlineData("EmptyFile.txt", 1)] + [InlineData("TestDataArchivesNested.Zip", 45)] public async Task ExtractArchiveAsyncDenyFiltered(string fileName, int expectedNumFiles = 2) { var extractor = new Extractor(); @@ -174,19 +173,19 @@ public async Task ExtractArchiveAsyncDenyFiltered(string fileName, int expectedN numResults++; } - Assert.AreEqual(expectedNumFiles, numResults); + Assert.Equal(expectedNumFiles, numResults); } - [DataTestMethod] - [DataRow(ArchiveFileType.ZIP, new[] { ArchiveFileType.ZIP }, new ArchiveFileType[] { }, false)] - [DataRow(ArchiveFileType.ZIP, new[] { ArchiveFileType.TAR }, new ArchiveFileType[] { }, true)] - [DataRow(ArchiveFileType.ZIP, new ArchiveFileType[] { }, new[] { ArchiveFileType.ZIP }, true)] - [DataRow(ArchiveFileType.TAR, new ArchiveFileType[] { }, new[] { ArchiveFileType.ZIP }, false)] - [DataRow(ArchiveFileType.ZIP, new[] { ArchiveFileType.ZIP }, new[] { ArchiveFileType.ZIP }, false)] + [Theory] + [InlineData(ArchiveFileType.ZIP, new[] { ArchiveFileType.ZIP }, new ArchiveFileType[] { }, false)] + [InlineData(ArchiveFileType.ZIP, new[] { ArchiveFileType.TAR }, new ArchiveFileType[] { }, true)] + [InlineData(ArchiveFileType.ZIP, new ArchiveFileType[] { }, new[] { ArchiveFileType.ZIP }, true)] + [InlineData(ArchiveFileType.TAR, new ArchiveFileType[] { }, new[] { ArchiveFileType.ZIP }, false)] + [InlineData(ArchiveFileType.ZIP, new[] { ArchiveFileType.ZIP }, new[] { ArchiveFileType.ZIP }, false)] public void TestArchiveTypeFilters(ArchiveFileType typeToCheck, IEnumerable denyTypes, IEnumerable allowTypes, bool expected) { ExtractorOptions opts = new() { AllowTypes = allowTypes, DenyTypes = denyTypes }; - Assert.AreEqual(expected, opts.IsAcceptableType(typeToCheck)); + Assert.Equal(expected, opts.IsAcceptableType(typeToCheck)); } -} \ No newline at end of file +} diff --git a/RecursiveExtractor.Tests/ExtractorTests/MiniMagicTests.cs b/RecursiveExtractor.Tests/ExtractorTests/MiniMagicTests.cs index 016ef133..20e2d959 100644 --- a/RecursiveExtractor.Tests/ExtractorTests/MiniMagicTests.cs +++ b/RecursiveExtractor.Tests/ExtractorTests/MiniMagicTests.cs @@ -1,44 +1,43 @@ using Microsoft.CST.RecursiveExtractor; -using Microsoft.VisualStudio.TestTools.UnitTesting; using System.IO; +using Xunit; namespace RecursiveExtractor.Tests.ExtractorTests; -[TestClass] -public class MiniMagicTests : BaseExtractorTestClass +public class MiniMagicTests { - [DataTestMethod] - [DataRow("TestData.zip", ArchiveFileType.ZIP)] - [DataRow("TestData.7z", ArchiveFileType.P7ZIP)] - [DataRow("TestData.Tar", ArchiveFileType.TAR)] - [DataRow("TestData.rar", ArchiveFileType.RAR5)] - [DataRow("TestData.rar4", ArchiveFileType.RAR)] - [DataRow("TestData.tar.bz2", ArchiveFileType.BZIP2)] - [DataRow("TestData.tar.gz", ArchiveFileType.GZIP)] - [DataRow("TestData.tar.xz", ArchiveFileType.XZ)] - [DataRow("sysvbanner_1.0-17fakesync1_amd64.deb", ArchiveFileType.DEB)] - [DataRow("TestData.a", ArchiveFileType.AR)] - [DataRow("TestData.iso", ArchiveFileType.ISO_9660)] - [DataRow("UdfTest.iso", ArchiveFileType.UDF)] - [DataRow("TestData.vhdx", ArchiveFileType.VHDX)] - [DataRow("TestData.wim", ArchiveFileType.WIM)] - [DataRow("Empty.vmdk", ArchiveFileType.VMDK)] - [DataRow("HfsSampleUDCO.dmg", ArchiveFileType.DMG)] - [DataRow("EmptyFile.txt", ArchiveFileType.UNKNOWN)] + [Theory] + [InlineData("TestData.zip", ArchiveFileType.ZIP)] + [InlineData("TestData.7z", ArchiveFileType.P7ZIP)] + [InlineData("TestData.Tar", ArchiveFileType.TAR)] + [InlineData("TestData.rar", ArchiveFileType.RAR5)] + [InlineData("TestData.rar4", ArchiveFileType.RAR)] + [InlineData("TestData.tar.bz2", ArchiveFileType.BZIP2)] + [InlineData("TestData.tar.gz", ArchiveFileType.GZIP)] + [InlineData("TestData.tar.xz", ArchiveFileType.XZ)] + [InlineData("sysvbanner_1.0-17fakesync1_amd64.deb", ArchiveFileType.DEB)] + [InlineData("TestData.a", ArchiveFileType.AR)] + [InlineData("TestData.iso", ArchiveFileType.ISO_9660)] + [InlineData("UdfTest.iso", ArchiveFileType.UDF)] + [InlineData("TestData.vhdx", ArchiveFileType.VHDX)] + [InlineData("TestData.wim", ArchiveFileType.WIM)] + [InlineData("Empty.vmdk", ArchiveFileType.VMDK)] + [InlineData("HfsSampleUDCO.dmg", ArchiveFileType.DMG)] + [InlineData("EmptyFile.txt", ArchiveFileType.UNKNOWN)] public void TestMiniMagic(string fileName, ArchiveFileType expectedArchiveFileType) { var path = Path.Combine(Directory.GetCurrentDirectory(), "TestData", "TestDataArchives", fileName); - using var fs = new FileStream(path, FileMode.Open); + using var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); // Test just based on the content var fileEntry = new FileEntry("NoName", fs); // We make sure the expected type matches and we have reset the stream - Assert.AreEqual(expectedArchiveFileType, fileEntry.ArchiveType); - Assert.AreEqual(0, fileEntry.Content.Position); + Assert.Equal(expectedArchiveFileType, fileEntry.ArchiveType); + Assert.Equal(0, fileEntry.Content.Position); // Should also work if the stream doesn't start at 0 fileEntry.Content.Position = 10; - Assert.AreEqual(expectedArchiveFileType, fileEntry.ArchiveType); - Assert.AreEqual(10, fileEntry.Content.Position); + Assert.Equal(expectedArchiveFileType, fileEntry.ArchiveType); + Assert.Equal(10, fileEntry.Content.Position); } } \ No newline at end of file diff --git a/RecursiveExtractor.Tests/ExtractorTests/MiscTests.cs b/RecursiveExtractor.Tests/ExtractorTests/MiscTests.cs index e375904a..789f9bf0 100644 --- a/RecursiveExtractor.Tests/ExtractorTests/MiscTests.cs +++ b/RecursiveExtractor.Tests/ExtractorTests/MiscTests.cs @@ -1,20 +1,19 @@ using Microsoft.CST.RecursiveExtractor; -using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; +using Xunit; namespace RecursiveExtractor.Tests.ExtractorTests; -[TestClass] public class MiscTests { - [DataTestMethod] - [DataRow("TestDataCorrupt.tar", false, 0, 1)] - [DataRow("TestDataCorrupt.tar", true, 1, 1)] - [DataRow("TestDataCorrupt.tar.zip", false, 0, 2)] - [DataRow("TestDataCorrupt.tar.zip", true, 0, 2)] + [Theory] + [InlineData("TestDataCorrupt.tar", false, 0, 1)] + [InlineData("TestDataCorrupt.tar", true, 1, 1)] + [InlineData("TestDataCorrupt.tar.zip", false, 0, 2)] + [InlineData("TestDataCorrupt.tar.zip", true, 0, 2)] public async Task ExtractCorruptArchiveAsync(string fileName, bool requireTopLevelToBeArchive, int expectedNumFailures, int expectedNumFiles) { var extractor = new Extractor(); @@ -23,61 +22,61 @@ public async Task ExtractCorruptArchiveAsync(string fileName, bool requireTopLev new ExtractorOptions() { RequireTopLevelToBeArchive = requireTopLevelToBeArchive }).ToListAsync(); - Assert.AreEqual(expectedNumFiles, results.Count); + Assert.Equal(expectedNumFiles, results.Count); var actualNumberOfFailedArchives = results.Count(x => x.EntryStatus == FileEntryStatus.FailedArchive); - Assert.AreEqual(expectedNumFailures, actualNumberOfFailedArchives); + Assert.Equal(expectedNumFailures, actualNumberOfFailedArchives); } - [DataTestMethod] - [DataRow("Lorem.txt", true, 1)] - [DataRow("Lorem.txt", false, 0)] + [Theory] + [InlineData("Lorem.txt", true, 1)] + [InlineData("Lorem.txt", false, 0)] public async Task ExtractFlatFileAsync(string fileName, bool requireTopLevelToBeArchive, int expectedNumFailures) { var extractor = new Extractor(); var path = Path.Combine(Directory.GetCurrentDirectory(), "TestData", "TestData", fileName); var results = await extractor.ExtractAsync(path, new ExtractorOptions(){ RequireTopLevelToBeArchive = requireTopLevelToBeArchive }).ToListAsync(); - Assert.AreEqual(1, results.Count); + Assert.Single(results); var actualNumberOfFailedArchives = results.Count(x => x.EntryStatus == FileEntryStatus.FailedArchive); - Assert.AreEqual(expectedNumFailures, actualNumberOfFailedArchives); + Assert.Equal(expectedNumFailures, actualNumberOfFailedArchives); } - [DataTestMethod] - [DataRow("TestDataCorrupt.tar", false, 0, 1)] - [DataRow("TestDataCorrupt.tar", true, 1, 1)] - [DataRow("TestDataCorrupt.tar.zip", false, 0, 2)] - [DataRow("TestDataCorrupt.tar.zip", true, 0, 2)] - [DataRow("TestDataCorruptWim.zip", true, 0, 0)] + [Theory] + [InlineData("TestDataCorrupt.tar", false, 0, 1)] + [InlineData("TestDataCorrupt.tar", true, 1, 1)] + [InlineData("TestDataCorrupt.tar.zip", false, 0, 2)] + [InlineData("TestDataCorrupt.tar.zip", true, 0, 2)] + [InlineData("TestDataCorruptWim.zip", true, 0, 0)] public void ExtractCorruptArchive(string fileName, bool requireTopLevelToBeArchive, int expectedNumFailures, int expectedNumFiles) { var extractor = new Extractor(); var path = Path.Combine(Directory.GetCurrentDirectory(), "TestData", "TestDataArchives", fileName); var results = extractor.Extract(path, new ExtractorOptions(){ RequireTopLevelToBeArchive = requireTopLevelToBeArchive }).ToList(); - Assert.AreEqual(expectedNumFiles, results.Count); + Assert.Equal(expectedNumFiles, results.Count); var actualNumberOfFailedArchives = results.Count(x => x.EntryStatus == FileEntryStatus.FailedArchive); - Assert.AreEqual(expectedNumFailures, actualNumberOfFailedArchives); + Assert.Equal(expectedNumFailures, actualNumberOfFailedArchives); } - [DataTestMethod] - [DataRow("Lorem.txt", true, 1)] - [DataRow("Lorem.txt", false, 0)] + [Theory] + [InlineData("Lorem.txt", true, 1)] + [InlineData("Lorem.txt", false, 0)] public void ExtractFlatFile(string fileName, bool requireTopLevelToBeArchive, int expectedNumFailures) { var extractor = new Extractor(); var path = Path.Combine(Directory.GetCurrentDirectory(), "TestData", "TestData", fileName); var results = extractor.Extract(path, new ExtractorOptions(){ RequireTopLevelToBeArchive = requireTopLevelToBeArchive }).ToList(); - Assert.AreEqual(1, results.Count); + Assert.Single(results); var actualNumberOfFailedArchives = results.Count(x => x.EntryStatus == FileEntryStatus.FailedArchive); - Assert.AreEqual(expectedNumFailures, actualNumberOfFailedArchives); + Assert.Equal(expectedNumFailures, actualNumberOfFailedArchives); } - [DataTestMethod] - [DataRow("EmptyFile.txt")] - [DataRow("TestData.zip", ".zip")] + [Theory] + [InlineData("EmptyFile.txt")] + [InlineData("TestData.zip", ".zip")] public void ExtractAsRaw(string fileName, string? RawExtension = null) { var extractor = new Extractor(); @@ -88,6 +87,6 @@ public void ExtractAsRaw(string fileName, string? RawExtension = null) var path = Path.Combine(Directory.GetCurrentDirectory(), "TestData", "TestDataArchives", fileName); var results = extractor.Extract(path, options); - Assert.AreEqual(1, results.Count()); + Assert.Single(results); } } \ No newline at end of file diff --git a/RecursiveExtractor.Tests/ExtractorTests/TestQuinesAndSlip.cs b/RecursiveExtractor.Tests/ExtractorTests/TestQuinesAndSlip.cs index e2da32d4..71823764 100644 --- a/RecursiveExtractor.Tests/ExtractorTests/TestQuinesAndSlip.cs +++ b/RecursiveExtractor.Tests/ExtractorTests/TestQuinesAndSlip.cs @@ -1,85 +1,86 @@ using Microsoft.CST.RecursiveExtractor; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using SharpCompress.Archives.Tar; using System; -using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; +using Xunit; namespace RecursiveExtractor.Tests.ExtractorTests; -[TestClass] -public class TestQuinesAndSlip : BaseExtractorTestClass +public class TestQuinesAndSlip { - public static IEnumerable ZipSlipNames + public static TheoryData ZipSlipNames { get { - return new[] + return new TheoryData { - new object [] { "zip-slip-win.zip" }, - new object [] { "zip-slip-win.tar" }, - new object [] { "zip-slip.zip" }, - new object [] { "zip-slip.tar" } + { "zip-slip-win.zip" }, + { "zip-slip-win.tar" }, + { "zip-slip.zip" }, + { "zip-slip.tar" } }; } } - [TestMethod] - [DynamicData(nameof(ZipSlipNames))] + [Theory] + [MemberData(nameof(ZipSlipNames))] public void TestZipSlip(string fileName) { var extractor = new Extractor(); var path = Path.Combine(Directory.GetCurrentDirectory(), "TestData", "Bombs", fileName); var results = extractor.Extract(path, new ExtractorOptions()).ToList(); - Assert.IsTrue(results.All(x => !x.FullPath.Contains(".."))); + Assert.True(results.All(x => !x.FullPath.Contains(".."))); } - [TestMethod] - [DynamicData(nameof(ZipSlipNames))] + [Theory] + [MemberData(nameof(ZipSlipNames))] public async Task TestZipSlipAsync(string fileName) { var extractor = new Extractor(); var path = Path.Combine(Directory.GetCurrentDirectory(), "TestData", "Bombs", fileName); var results = await extractor.ExtractAsync(path, new ExtractorOptions()).ToListAsync(); - Assert.IsTrue(results.All(x => !x.FullPath.Contains(".."))); + Assert.True(results.All(x => !x.FullPath.Contains(".."))); } - public static IEnumerable QuineBombNames + public static TheoryData QuineBombNames { get { - return new[] + return new TheoryData { - new object [] { "10GB.7z.bz2" }, - new object [] { "10GB.gz.bz2" }, - new object [] { "10GB.rar.bz2" }, - new object [] { "10GB.xz.bz2" }, - new object [] { "10GB.zip.bz2" }, - new object [] { "zblg.zip" }, - new object [] { "zbsm.zip" } + { "10GB.7z.bz2" }, + { "10GB.gz.bz2" }, + { "10GB.rar.bz2" }, + { "10GB.xz.bz2" }, + { "10GB.zip.bz2" }, + { "zblg.zip" }, + { "zbsm.zip" } }; } } - [TestMethod] - [DynamicData(nameof(QuineBombNames))] - [ExpectedException(typeof(OverflowException))] + [Theory] + [MemberData(nameof(QuineBombNames))] public void TestQuineBombs(string fileName) { var extractor = new Extractor(); var path = Path.Combine(Directory.GetCurrentDirectory(), "TestData", "Bombs", fileName); - _ = extractor.Extract(path, new ExtractorOptions() { MemoryStreamCutoff = 1024 * 1024 * 1024 }).ToList(); + Assert.Throws(() => + { + _ = extractor.Extract(path, new ExtractorOptions() { MemoryStreamCutoff = 1024 * 1024 * 1024 }).ToList(); + }); } - [TestMethod] - [DynamicData(nameof(QuineBombNames))] - [ExpectedException(typeof(OverflowException))] + [Theory] + [MemberData(nameof(QuineBombNames))] public async Task TestQuineBombsAsync(string fileName) { var extractor = new Extractor(); var path = Path.Combine(Directory.GetCurrentDirectory(), "TestData", "Bombs", fileName); - _ = await extractor.ExtractAsync(path, new ExtractorOptions() { MemoryStreamCutoff = 1024 * 1024 * 1024 }).ToListAsync(); + await Assert.ThrowsAsync(async () => + { + _ = await extractor.ExtractAsync(path, new ExtractorOptions() { MemoryStreamCutoff = 1024 * 1024 * 1024 }).ToListAsync(); + }); } } \ No newline at end of file diff --git a/RecursiveExtractor.Tests/ExtractorTests/TimeOutTests.cs b/RecursiveExtractor.Tests/ExtractorTests/TimeOutTests.cs index 2f2c8455..980922c5 100644 --- a/RecursiveExtractor.Tests/ExtractorTests/TimeOutTests.cs +++ b/RecursiveExtractor.Tests/ExtractorTests/TimeOutTests.cs @@ -1,51 +1,50 @@ -using Microsoft.CST.RecursiveExtractor; -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.CST.RecursiveExtractor; using System; using System.IO; using System.Threading.Tasks; +using Xunit; namespace RecursiveExtractor.Tests.ExtractorTests; -[TestClass] -public class TimeOutTests : BaseExtractorTestClass +public class TimeOutTests { - [DataTestMethod] - [DataRow("TestData.7z", 3, false)] - [DataRow("TestData.tar", 6, false)] - [DataRow("TestData.rar", 3, false)] - [DataRow("TestData.rar4", 3, false)] - [DataRow("TestData.tar.bz2", 6, false)] - [DataRow("TestData.tar.gz", 6, false)] - [DataRow("TestData.tar.xz", 3, false)] - [DataRow("sysvbanner_1.0-17fakesync1_amd64.deb", 8, false)] - [DataRow("TestData.a", 3, false)] - [DataRow("TestData.bsd.ar", 3, false)] - [DataRow("TestData.iso", 3, false)] - [DataRow("TestData.vhdx", 3, false)] - [DataRow("TestData.wim", 3, false)] - [DataRow("EmptyFile.txt", 1, false)] - [DataRow("TestData.zip", 5, true)] - [DataRow("TestData.7z", 3, true)] - [DataRow("TestData.tar", 6, true)] - [DataRow("TestData.rar", 3, true)] - [DataRow("TestData.rar4", 3, true)] - [DataRow("TestData.tar.bz2", 6, true)] - [DataRow("TestData.tar.gz", 6, true)] - [DataRow("TestData.tar.xz", 3, true)] - [DataRow("sysvbanner_1.0-17fakesync1_amd64.deb", 8, true)] - [DataRow("TestData.a", 3, true)] - [DataRow("TestData.bsd.ar", 3, true)] - [DataRow("TestData.iso", 3, true)] - [DataRow("TestData.vhdx", 3, true)] - [DataRow("TestData.wim", 3, true)] - [DataRow("EmptyFile.txt", 1, true)] - [DataRow("TestDataArchivesNested.Zip", 54, true)] - [DataRow("TestDataArchivesNested.Zip", 54, false)] - public void TimeoutTest(string fileName, int expectedNumFiles = 3, bool parallel = false) + [Theory] + [InlineData("TestData.7z", false)] + [InlineData("TestData.tar", false)] + [InlineData("TestData.rar", false)] + [InlineData("TestData.rar4", false)] + [InlineData("TestData.tar.bz2", false)] + [InlineData("TestData.tar.gz", false)] + [InlineData("TestData.tar.xz", false)] + [InlineData("sysvbanner_1.0-17fakesync1_amd64.deb", false)] + [InlineData("TestData.a", false)] + [InlineData("TestData.bsd.ar", false)] + [InlineData("TestData.iso", false)] + [InlineData("TestData.vhdx", false)] + [InlineData("TestData.wim", false)] + [InlineData("EmptyFile.txt", false)] + [InlineData("TestData.zip", true)] + [InlineData("TestData.7z", true)] + [InlineData("TestData.tar", true)] + [InlineData("TestData.rar", true)] + [InlineData("TestData.rar4", true)] + [InlineData("TestData.tar.bz2", true)] + [InlineData("TestData.tar.gz", true)] + [InlineData("TestData.tar.xz", true)] + [InlineData("sysvbanner_1.0-17fakesync1_amd64.deb", true)] + [InlineData("TestData.a", true)] + [InlineData("TestData.bsd.ar", true)] + [InlineData("TestData.iso", true)] + [InlineData("TestData.vhdx", true)] + [InlineData("TestData.wim", true)] + [InlineData("EmptyFile.txt", true)] + [InlineData("TestDataArchivesNested.Zip", true)] + [InlineData("TestDataArchivesNested.Zip", false)] + public void TimeoutTest(string fileName, bool parallel = false) { var extractor = new Extractor(); var path = Path.Combine(Directory.GetCurrentDirectory(), "TestData", "TestDataArchives", fileName); - Assert.ThrowsException(() => + Assert.Throws(() => { var results = extractor.Extract(path, new ExtractorOptions() @@ -59,49 +58,47 @@ public void TimeoutTest(string fileName, int expectedNumFiles = 3, bool parallel } // We should not be able to get to all the files - Assert.Fail(); + Assert.Fail("Should have thrown TimeoutException"); }); } - [DataTestMethod] - [DataRow("TestData.7z", 3, false)] - [DataRow("TestData.tar", 6, false)] - [DataRow("TestData.rar", 3, false)] - [DataRow("TestData.rar4", 3, false)] - [DataRow("TestData.tar.bz2", 6, false)] - [DataRow("TestData.tar.gz", 6, false)] - [DataRow("TestData.tar.xz", 3, false)] - [DataRow("sysvbanner_1.0-17fakesync1_amd64.deb", 8, false)] - [DataRow("TestData.a", 3, false)] - [DataRow("TestData.bsd.ar", 3, false)] - [DataRow("TestData.iso", 3, false)] - [DataRow("TestData.vhdx", 3, false)] - [DataRow("TestData.wim", 3, false)] - [DataRow("EmptyFile.txt", 1, false)] - [DataRow("TestData.zip", 5, true)] - [DataRow("TestData.7z", 3, true)] - [DataRow("TestData.tar", 6, true)] - [DataRow("TestData.rar", 3, true)] - [DataRow("TestData.rar4", 3, true)] - [DataRow("TestData.tar.bz2", 6, true)] - [DataRow("TestData.tar.gz", 6, true)] - [DataRow("TestData.tar.xz", 3, true)] - [DataRow("sysvbanner_1.0-17fakesync1_amd64.deb", 8, true)] - [DataRow("TestData.a", 3, true)] - [DataRow("TestData.bsd.ar", 3, true)] - [DataRow("TestData.iso", 3, true)] - [DataRow("TestData.vhdx", 3, true)] - [DataRow("TestData.wim", 3, true)] - [DataRow("EmptyFile.txt", 1, true)] - [DataRow("TestDataArchivesNested.Zip", 54, true)] - [DataRow("TestDataArchivesNested.Zip", 54, false)] - [DataRow("TestDataArchivesNested.Zip", 54, true)] - [DataRow("TestDataArchivesNested.Zip", 54, false)] - public async Task TimeoutTestAsync(string fileName, int expectedNumFiles = 3, bool parallel = false) + [Theory] + [InlineData("TestData.7z", false)] + [InlineData("TestData.tar", false)] + [InlineData("TestData.rar", false)] + [InlineData("TestData.rar4", false)] + [InlineData("TestData.tar.bz2", false)] + [InlineData("TestData.tar.gz", false)] + [InlineData("TestData.tar.xz", false)] + [InlineData("sysvbanner_1.0-17fakesync1_amd64.deb", false)] + [InlineData("TestData.a", false)] + [InlineData("TestData.bsd.ar", false)] + [InlineData("TestData.iso", false)] + [InlineData("TestData.vhdx", false)] + [InlineData("TestData.wim", false)] + [InlineData("EmptyFile.txt", false)] + [InlineData("TestData.zip", true)] + [InlineData("TestData.7z", true)] + [InlineData("TestData.tar", true)] + [InlineData("TestData.rar", true)] + [InlineData("TestData.rar4", true)] + [InlineData("TestData.tar.bz2", true)] + [InlineData("TestData.tar.gz", true)] + [InlineData("TestData.tar.xz", true)] + [InlineData("sysvbanner_1.0-17fakesync1_amd64.deb", true)] + [InlineData("TestData.a", true)] + [InlineData("TestData.bsd.ar", true)] + [InlineData("TestData.iso", true)] + [InlineData("TestData.vhdx", true)] + [InlineData("TestData.wim", true)] + [InlineData("EmptyFile.txt", true)] + [InlineData("TestDataArchivesNested.Zip", true)] + [InlineData("TestDataArchivesNested.Zip", false)] + public async Task TimeoutTestAsync(string fileName, bool parallel = false) { var extractor = new Extractor(); var path = Path.Combine(Directory.GetCurrentDirectory(), "TestData", "TestDataArchives", fileName); - await Assert.ThrowsExceptionAsync(async () => + await Assert.ThrowsAsync(async () => { var results = extractor.ExtractAsync(path, new ExtractorOptions() @@ -115,7 +112,7 @@ await Assert.ThrowsExceptionAsync(async () => } // We should not be able to get to all the files - Assert.Fail(); + Assert.Fail("Should have thrown TimeoutException"); }); } -} \ No newline at end of file +} diff --git a/RecursiveExtractor.Tests/RecursiveExtractor.Tests.csproj b/RecursiveExtractor.Tests/RecursiveExtractor.Tests.csproj index fe84518b..5b57be59 100644 --- a/RecursiveExtractor.Tests/RecursiveExtractor.Tests.csproj +++ b/RecursiveExtractor.Tests/RecursiveExtractor.Tests.csproj @@ -5,18 +5,16 @@ false enable 10.0 - Exe + + $(NoWarn);CS1685 - - - - - - - + + + + diff --git a/RecursiveExtractor.Tests/SanitizePathTests.cs b/RecursiveExtractor.Tests/SanitizePathTests.cs index f8d56a29..a0fb2f2a 100644 --- a/RecursiveExtractor.Tests/SanitizePathTests.cs +++ b/RecursiveExtractor.Tests/SanitizePathTests.cs @@ -1,38 +1,37 @@ // Copyright (c) Microsoft Corporation. Licensed under the MIT License. using Microsoft.CST.RecursiveExtractor; -using Microsoft.VisualStudio.TestTools.UnitTesting; using System.IO; using System.Runtime.InteropServices; +using Xunit; namespace RecursiveExtractor.Tests { - [TestClass] public class SanitizePathTests { - [DataTestMethod] - [DataRow("a\\file\\with:colon.name", "a\\file\\with_colon.name")] - [DataRow("a\\folder:with\\colon.name", "a\\folder_with\\colon.name")] + [Theory] + [InlineData("a\\file\\with:colon.name", "a\\file\\with_colon.name")] + [InlineData("a\\folder:with\\colon.name", "a\\folder_with\\colon.name")] public void TestSanitizePathWindows(string windowsInputPath, string expectedWindowsPath) { var entry = new FileEntry(windowsInputPath, Stream.Null); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - Assert.AreEqual(expectedWindowsPath, entry.GetSanitizedPath()); + Assert.Equal(expectedWindowsPath, entry.GetSanitizedPath()); } } - [DataTestMethod] - [DataRow("a/file/with:colon.name", "a/file/with_colon.name")] - [DataRow("a/folder:with/colon.name", "a/folder_with/colon.name")] + [Theory] + [InlineData("a/file/with:colon.name", "a/file/with_colon.name")] + [InlineData("a/folder:with/colon.name", "a/folder_with/colon.name")] public void TestSanitizePathLinux(string linuxInputPath, string expectedLinuxPath) { var entry = new FileEntry(linuxInputPath, Stream.Null); if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { - Assert.AreEqual(expectedLinuxPath, entry.GetSanitizedPath()); + Assert.Equal(expectedLinuxPath, entry.GetSanitizedPath()); } } diff --git a/RecursiveExtractor.Tests/TestPathHelpers.cs b/RecursiveExtractor.Tests/TestPathHelpers.cs index 97461b2b..f854934a 100644 --- a/RecursiveExtractor.Tests/TestPathHelpers.cs +++ b/RecursiveExtractor.Tests/TestPathHelpers.cs @@ -9,7 +9,10 @@ public static class TestPathHelpers { public const string TestTempFolderName = "RE_Tests"; - public static string TestDirectoryPath => Path.Combine(Path.GetTempPath(), TestTempFolderName); + // Use a process-unique subdirectory to avoid cross-TFM/cross-process interference + private static readonly string ProcessId = System.Diagnostics.Process.GetCurrentProcess().Id.ToString(); + + public static string TestDirectoryPath => Path.Combine(Path.GetTempPath(), TestTempFolderName, ProcessId); public static string GetFreshTestDirectory() { @@ -20,13 +23,13 @@ public static void DeleteTestDirectory() { try { - Directory.Delete(Path.Combine(TestDirectoryPath), true); + Directory.Delete(TestDirectoryPath, true); } catch (DirectoryNotFoundException) { // Not an error. Not every test makes the folder. } - catch (Exception e) + catch (Exception) { // Throwing the exception up may cause tests to fail due to file system oddness so just log Logger.Warn("Failed to delete Test Working Directory at {directory}", TestDirectoryPath); diff --git a/RecursiveExtractor/ArFile.cs b/RecursiveExtractor/ArFile.cs index 01845d93..66728061 100644 --- a/RecursiveExtractor/ArFile.cs +++ b/RecursiveExtractor/ArFile.cs @@ -32,7 +32,7 @@ public static IEnumerable GetFileEntries(FileEntry fileEntry, Extract var headerBuffer = new byte[60]; while (fileEntry.Content.Length - fileEntry.Content.Position >= 60) { - fileEntry.Content.Read(headerBuffer, 0, 60); + fileEntry.Content.ReadExactly(headerBuffer, 0, 60); var headerString = Encoding.ASCII.GetString(headerBuffer); if (long.TryParse(Encoding.ASCII.GetString(headerBuffer[48..58]), out var size))// header size in bytes { @@ -46,7 +46,7 @@ public static IEnumerable GetFileEntries(FileEntry fileEntry, Extract // This should just be a list of names, size should be safe to load in memory and cast // to int var fileNamesBytes = new byte[size]; - fileEntry.Content.Read(fileNamesBytes, 0, (int)size); + fileEntry.Content.ReadExactly(fileNamesBytes, 0, (int)size); var name = new StringBuilder(); var index = 0; @@ -76,7 +76,7 @@ public static IEnumerable GetFileEntries(FileEntry fileEntry, Extract var nameSpan = new byte[nameLength]; // This should move us right to the file - fileEntry.Content.Read(nameSpan, 0, nameLength); + fileEntry.Content.ReadExactly(nameSpan, 0, nameLength); var entryStream = StreamFactory.GenerateAppropriateBackingStream(options, size - nameLength); @@ -93,7 +93,7 @@ public static IEnumerable GetFileEntries(FileEntry fileEntry, Extract // terminated strings "symbol name" (possibly filename) var tableContents = new byte[size]; - fileEntry.Content.Read(tableContents, 0, (int)size); + fileEntry.Content.ReadExactly(tableContents, 0, (int)size); var numEntries = IntFromBigEndianBytes(tableContents[0..4]); var filePositions = new int[numEntries]; @@ -124,7 +124,7 @@ public static IEnumerable GetFileEntries(FileEntry fileEntry, Extract foreach (var entry in fileEntries) { fileEntry.Content.Position = entry.Item1; - fileEntry.Content.Read(headerBuffer, 0, 60); + fileEntry.Content.ReadExactly(headerBuffer, 0, 60); if (long.TryParse(Encoding.ASCII.GetString(headerBuffer[48..58]), out var innerSize))// header size in bytes { @@ -162,14 +162,14 @@ public static IEnumerable GetFileEntries(FileEntry fileEntry, Extract // strings "symbol name" (possibly filename) var buffer = new byte[8]; - fileEntry.Content.Read(buffer, 0, 8); + fileEntry.Content.ReadExactly(buffer, 0, 8); var numEntries = Int64FromBigEndianBytes(buffer); var filePositions = new long[numEntries]; for (var i = 0; i < numEntries; i++) { - fileEntry.Content.Read(buffer, 0, 8); + fileEntry.Content.ReadExactly(buffer, 0, 8); filePositions[i] = Int64FromBigEndianBytes(buffer); } @@ -179,7 +179,7 @@ public static IEnumerable GetFileEntries(FileEntry fileEntry, Extract while (fileEntry.Content.Position < size) { - fileEntry.Content.Read(buffer, 0, 1); + fileEntry.Content.ReadExactly(buffer, 0, 1); if (buffer[0] == '\0') { fileEntries.Add((filePositions[index++], sb.ToString())); @@ -195,7 +195,7 @@ public static IEnumerable GetFileEntries(FileEntry fileEntry, Extract { fileEntry.Content.Position = innerEntry.Item1; - fileEntry.Content.Read(headerBuffer, 0, 60); + fileEntry.Content.ReadExactly(headerBuffer, 0, 60); if (long.TryParse(Encoding.ASCII.GetString(headerBuffer[48..58]), out var innerSize))// header size in bytes { @@ -217,9 +217,9 @@ public static IEnumerable GetFileEntries(FileEntry fileEntry, Extract { filename = innerEntry.Item2; } + var entryStream = StreamFactory.GenerateAppropriateBackingStream(options, innerSize); CopyStreamBytes(fileEntry.Content, entryStream, innerSize); - yield return new FileEntry(filename.TrimEnd('/'), entryStream, fileEntry, true); } } @@ -281,7 +281,7 @@ public static async IAsyncEnumerable GetFileEntriesAsync(FileEntry fi var headerBuffer = new byte[60]; while (fileEntry.Content.Length - fileEntry.Content.Position >= 60) { - fileEntry.Content.Read(headerBuffer, 0, 60); + fileEntry.Content.ReadExactly(headerBuffer, 0, 60); if (long.TryParse(Encoding.ASCII.GetString(headerBuffer[48..58]), out var size))// header size in bytes { governor.CheckResourceGovernor(size); @@ -339,7 +339,7 @@ public static async IAsyncEnumerable GetFileEntriesAsync(FileEntry fi // terminated strings "symbol name" (possibly filename) var tableContents = new byte[size]; - fileEntry.Content.Read(tableContents, 0, (int)size); + fileEntry.Content.ReadExactly(tableContents, 0, (int)size); var numEntries = IntFromBigEndianBytes(tableContents[0..4]); var filePositions = new int[numEntries]; @@ -407,14 +407,14 @@ public static async IAsyncEnumerable GetFileEntriesAsync(FileEntry fi // strings "symbol name" (possibly filename) var buffer = new byte[8]; - fileEntry.Content.Read(buffer, 0, 8); + fileEntry.Content.ReadExactly(buffer, 0, 8); var numEntries = Int64FromBigEndianBytes(buffer); var filePositions = new long[numEntries]; for (var i = 0; i < numEntries; i++) { - fileEntry.Content.Read(buffer, 0, 8); + fileEntry.Content.ReadExactly(buffer, 0, 8); filePositions[i] = Int64FromBigEndianBytes(buffer); } @@ -424,7 +424,7 @@ public static async IAsyncEnumerable GetFileEntriesAsync(FileEntry fi while (fileEntry.Content.Position < size) { - fileEntry.Content.Read(buffer, 0, 1); + fileEntry.Content.ReadExactly(buffer, 0, 1); if (buffer[0] == '\0') { fileEntries.Add((filePositions[index++], sb.ToString())); @@ -440,7 +440,7 @@ public static async IAsyncEnumerable GetFileEntriesAsync(FileEntry fi { fileEntry.Content.Position = innerEntry.Item1; - fileEntry.Content.Read(headerBuffer, 0, 60); + fileEntry.Content.ReadExactly(headerBuffer, 0, 60); if (long.TryParse(Encoding.ASCII.GetString(headerBuffer[48..58]), out var innerSize))// header size in bytes { diff --git a/RecursiveExtractor/DebArchiveFile.cs b/RecursiveExtractor/DebArchiveFile.cs index a7d42602..25139648 100644 --- a/RecursiveExtractor/DebArchiveFile.cs +++ b/RecursiveExtractor/DebArchiveFile.cs @@ -30,7 +30,7 @@ public static IEnumerable GetFileEntries(FileEntry fileEntry, Extract while (fileEntry.Content.Length - fileEntry.Content.Position >= 60) { - fileEntry.Content.Read(headerBytes, 0, 60); + fileEntry.Content.ReadExactly(headerBytes, 0, 60); var filename = Encoding.ASCII.GetString(headerBytes[0..16]).Trim(); // filename is 16 bytes var fileSizeBytes = headerBytes[48..58]; // File size is decimal-encoded, 10 bytes long if (int.TryParse(Encoding.ASCII.GetString(fileSizeBytes).Trim(), out var fileSize)) @@ -39,7 +39,7 @@ public static IEnumerable GetFileEntries(FileEntry fileEntry, Extract governor.AdjustRemainingBytes(-fileSize); var entryContent = new byte[fileSize]; - fileEntry.Content.Read(entryContent, 0, fileSize); + fileEntry.Content.ReadExactly(entryContent, 0, fileSize); var stream = new MemoryStream(entryContent); yield return new FileEntry(filename, stream, fileEntry, true); } @@ -70,7 +70,7 @@ public static async IAsyncEnumerable GetFileEntriesAsync(FileEntry fi while (fileEntry.Content.Length - fileEntry.Content.Position >= 60) { - fileEntry.Content.Read(headerBytes, 0, 60); + fileEntry.Content.ReadExactly(headerBytes, 0, 60); var filename = Encoding.ASCII.GetString(headerBytes[0..16]).Trim(); // filename is 16 bytes var fileSizeBytes = headerBytes[48..58]; // File size is decimal-encoded, 10 bytes long if (int.TryParse(Encoding.ASCII.GetString(fileSizeBytes).Trim(), out var fileSize)) diff --git a/RecursiveExtractor/Extractor.cs b/RecursiveExtractor/Extractor.cs index 15dafc57..8b14fe2c 100644 --- a/RecursiveExtractor/Extractor.cs +++ b/RecursiveExtractor/Extractor.cs @@ -148,13 +148,17 @@ public static bool AreIdentical(FileEntry fileEntry1, FileEntry fileEntry2) var bytesRemaining = stream2.Length; while (bytesRemaining > 0) { - stream1.Read(buffer1, 0, bufferSize); - stream2.Read(buffer2, 0, bufferSize); - if (!buffer1.SequenceEqual(buffer2)) + var bytesToRead = (int)Math.Min(bufferSize, bytesRemaining); + stream1.ReadExactly(buffer1, 0, bytesToRead); + stream2.ReadExactly(buffer2, 0, bytesToRead); + for (int i = 0; i < bytesToRead; i++) { - stream1.Position = position1; - stream2.Position = position2; - return false; + if (buffer1[i] != buffer2[i]) + { + stream1.Position = position1; + stream2.Position = position2; + return false; + } } bytesRemaining = stream2.Length - stream2.Position; } diff --git a/RecursiveExtractor/Extractors/RarExtractor.cs b/RecursiveExtractor/Extractors/RarExtractor.cs index e8b31406..e1096583 100644 --- a/RecursiveExtractor/Extractors/RarExtractor.cs +++ b/RecursiveExtractor/Extractors/RarExtractor.cs @@ -104,7 +104,7 @@ public async IAsyncEnumerable ExtractAsync(FileEntry fileEntry, Extra foreach (var entry in rarArchive.Entries.Where(x => x.IsComplete && !x.IsDirectory)) { governor.CheckResourceGovernor(entry.Size); - var name = entry.Key.Replace('/', Path.DirectorySeparatorChar); + var name = (entry.Key ?? string.Empty).Replace('/', Path.DirectorySeparatorChar); var newFileEntry = await FileEntry.FromStreamAsync(name, entry.OpenEntryStream(), fileEntry, entry.CreatedTime, entry.LastModifiedTime, entry.LastAccessedTime, memoryStreamCutoff: options.MemoryStreamCutoff).ConfigureAwait(false); if (newFileEntry != null) { @@ -149,7 +149,7 @@ public IEnumerable Extract(FileEntry fileEntry, ExtractorOptions opti try { var stream = entry.OpenEntryStream(); - var name = entry.Key.Replace('/', Path.DirectorySeparatorChar); + var name = (entry.Key ?? string.Empty).Replace('/', Path.DirectorySeparatorChar); newFileEntry = new FileEntry(name, stream, fileEntry, false, entry.CreatedTime, entry.LastModifiedTime, entry.LastAccessedTime, memoryStreamCutoff: options.MemoryStreamCutoff); } catch (Exception e) diff --git a/RecursiveExtractor/Extractors/SevenZipExtractor.cs b/RecursiveExtractor/Extractors/SevenZipExtractor.cs index 50228a55..2f1e3a53 100644 --- a/RecursiveExtractor/Extractors/SevenZipExtractor.cs +++ b/RecursiveExtractor/Extractors/SevenZipExtractor.cs @@ -41,7 +41,7 @@ public async IAsyncEnumerable ExtractAsync(FileEntry fileEntry, Extra foreach (var entry in sevenZipArchive.Entries.Where(x => !x.IsDirectory && x.IsComplete).ToList()) { governor.CheckResourceGovernor(entry.Size); - var name = entry.Key.Replace('/', Path.DirectorySeparatorChar); + var name = (entry.Key ?? string.Empty).Replace('/', Path.DirectorySeparatorChar); var newFileEntry = await FileEntry.FromStreamAsync(name, entry.OpenEntryStream(), fileEntry, entry.CreatedTime, entry.LastModifiedTime, entry.LastAccessedTime, memoryStreamCutoff: options.MemoryStreamCutoff).ConfigureAwait(false); if (newFileEntry != null) @@ -154,7 +154,7 @@ public IEnumerable Extract(FileEntry fileEntry, ExtractorOptions opti foreach (var entry in entries) { governor.CheckResourceGovernor(entry.Size); - var name = entry.Key.Replace('/', Path.DirectorySeparatorChar); + var name = (entry.Key ?? string.Empty).Replace('/', Path.DirectorySeparatorChar); var newFileEntry = new FileEntry(name, entry.OpenEntryStream(), fileEntry, createTime: entry.CreatedTime, modifyTime: entry.LastModifiedTime, accessTime: entry.LastAccessedTime, memoryStreamCutoff: options.MemoryStreamCutoff); if (options.Recurse || topLevel) diff --git a/RecursiveExtractor/Extractors/TarExtractor.cs b/RecursiveExtractor/Extractors/TarExtractor.cs index 8553da07..9de51798 100644 --- a/RecursiveExtractor/Extractors/TarExtractor.cs +++ b/RecursiveExtractor/Extractors/TarExtractor.cs @@ -70,7 +70,7 @@ public async IAsyncEnumerable ExtractAsync(FileEntry fileEntry, Extra continue; } // Remove leading ./ - while (name.StartsWith($".{Path.DirectorySeparatorChar}")) + while (name!.StartsWith($".{Path.DirectorySeparatorChar}")) { name = name[2..]; } @@ -140,7 +140,7 @@ public IEnumerable Extract(FileEntry fileEntry, ExtractorOptions opti continue; } // Remove leading ./ - while (name.StartsWith($".{Path.DirectorySeparatorChar}")) + while (name!.StartsWith($".{Path.DirectorySeparatorChar}")) { name = name[2..]; } diff --git a/RecursiveExtractor/Extractors/ZipExtractor.cs b/RecursiveExtractor/Extractors/ZipExtractor.cs index 0c786230..5b706b44 100644 --- a/RecursiveExtractor/Extractors/ZipExtractor.cs +++ b/RecursiveExtractor/Extractors/ZipExtractor.cs @@ -50,7 +50,7 @@ public ZipExtractor(Extractor context) using var testStream = testEntry.OpenEntryStream(); // If we can read without exception, password is correct var buffer = new byte[1]; - testStream.Read(buffer, 0, 1); + testStream.ReadExactly(buffer, 0, 1); return password; } } diff --git a/RecursiveExtractor/MiniMagic.cs b/RecursiveExtractor/MiniMagic.cs index 932ac5db..64c914d7 100644 --- a/RecursiveExtractor/MiniMagic.cs +++ b/RecursiveExtractor/MiniMagic.cs @@ -81,7 +81,7 @@ public enum ArchiveFileType /// VMDK, /// - /// A DMG disc image. + /// A DMG disc image. /// DMG, /// @@ -128,7 +128,7 @@ public static ArchiveFileType DetectFileType(Stream fileStream) { var dmgFooterMagic = new byte[] { 0x6b, 0x6f, 0x6c, 0x79 }; fileStream.Position = fileStream.Length - 0x200; // Footer position - fileStream.Read(buffer, 0, 4); + fileStream.ReadExactly(buffer, 0, 4); fileStream.Position = initialPosition; if (dmgFooterMagic.SequenceEqual(buffer[0..4])) @@ -140,7 +140,7 @@ public static ArchiveFileType DetectFileType(Stream fileStream) if (fileStream.Length >= 9) { fileStream.Position = 0; - fileStream.Read(buffer, 0, 9); + fileStream.ReadExactly(buffer, 0, 9); fileStream.Position = initialPosition; if (buffer[0] == 0x50 && buffer[1] == 0x4B && buffer[2] == 0x03 && buffer[3] == 0x04) @@ -181,7 +181,7 @@ public static ArchiveFileType DetectFileType(Stream fileStream) { fileStream.Position = 512; var secondToken = new byte[21]; - fileStream.Read(secondToken, 0, 21); + fileStream.ReadExactly(secondToken, 0, 21); fileStream.Position = initialPosition; if (Encoding.ASCII.GetString(secondToken) == "# Disk DescriptorFile") @@ -194,7 +194,7 @@ public static ArchiveFileType DetectFileType(Stream fileStream) { // .deb https://manpages.debian.org/unstable/dpkg-dev/deb.5.en.html fileStream.Position = 68; - fileStream.Read(buffer, 0, 4); + fileStream.ReadExactly(buffer, 0, 4); fileStream.Position = initialPosition; var encoding = new ASCIIEncoding(); @@ -208,7 +208,7 @@ public static ArchiveFileType DetectFileType(Stream fileStream) // Created by GNU ar https://en.wikipedia.org/wiki/Ar_(Unix)#System_V_(or_GNU)_variant fileStream.Position = 8; - fileStream.Read(headerBuffer, 0, 60); + fileStream.ReadExactly(headerBuffer, 0, 60); fileStream.Position = initialPosition; // header size in bytes @@ -232,7 +232,7 @@ public static ArchiveFileType DetectFileType(Stream fileStream) if (fileStream.Length >= 262) { fileStream.Position = 257; - fileStream.Read(buffer, 0, 5); + fileStream.ReadExactly(buffer, 0, 5); fileStream.Position = initialPosition; if (buffer[0] == 0x75 && buffer[1] == 0x73 && buffer[2] == 0x74 && buffer[3] == 0x61 && buffer[4] == 0x72) @@ -245,7 +245,7 @@ public static ArchiveFileType DetectFileType(Stream fileStream) if (fileStream.Length > 32768 + 2048) { fileStream.Position = 32769; - fileStream.Read(buffer, 0, 5); + fileStream.ReadExactly(buffer, 0, 5); fileStream.Position = initialPosition; if (buffer[0] == 'C' && buffer[1] == 'D' && buffer[2] == '0' && buffer[3] == '0' && buffer[4] == '1') @@ -266,7 +266,7 @@ public static ArchiveFileType DetectFileType(Stream fileStream) var vhdFooterCookie = new byte[] { 0x63, 0x6F, 0x6E, 0x65, 0x63, 0x74, 0x69, 0x78 }; fileStream.Position = fileStream.Length - 0x200; // Footer position - fileStream.Read(buffer, 0, 8); + fileStream.ReadExactly(buffer, 0, 8); fileStream.Position = initialPosition; if (vhdFooterCookie.SequenceEqual(buffer[0..8])) @@ -275,7 +275,7 @@ public static ArchiveFileType DetectFileType(Stream fileStream) } fileStream.Position = fileStream.Length - 0x1FF; //If created on legacy platform footer is 511 bytes instead - fileStream.Read(buffer, 0, 8); + fileStream.ReadExactly(buffer, 0, 8); fileStream.Position = initialPosition; if (vhdFooterCookie.SequenceEqual(buffer[0..8])) diff --git a/RecursiveExtractor/Range.cs b/RecursiveExtractor/Range.cs index ac5b2147..18638669 100644 --- a/RecursiveExtractor/Range.cs +++ b/RecursiveExtractor/Range.cs @@ -1,238 +1,7 @@ -// https://github.com/dotnet/corefx/blob/1597b894a2e9cac668ce6e484506eca778a85197/src/Common/src/CoreLib/System/Index.cs -// https://github.com/dotnet/corefx/blob/1597b894a2e9cac668ce6e484506eca778a85197/src/Common/src/CoreLib/System/Range.cs +// Polyfill for RuntimeHelpers.GetSubArray needed by the C# compiler for array range syntax on netstandard2.0. +// Index and Range types are now provided by Microsoft.Bcl.Memory (transitive dependency). #if NETSTANDARD2_0 -using System.Runtime.CompilerServices; - -namespace System -{ - /// Represent a type can be used to index a collection either from the start or the end. - /// - /// Index is used by the C# compiler to support the new index syntax - /// - /// int[] someArray = new int[5] { 1, 2, 3, 4, 5 } ; - /// int lastElement = someArray[^1]; // lastElement = 5 - /// - /// - internal readonly struct Index : IEquatable - { - private readonly int _value; - - /// Construct an Index using a value and indicating if the index is from the start or from the end. - /// The index value. it has to be zero or positive number. - /// Indicating if the index is from the start or from the end. - /// - /// If the Index constructed from the end, index value 1 means pointing at the last element and index value 0 means pointing at beyond last element. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Index(int value, bool fromEnd = false) - { - if (value < 0) - { - throw new ArgumentOutOfRangeException(nameof(value), "value must be non-negative"); - } - - if (fromEnd) - _value = ~value; - else - _value = value; - } - - // The following private constructors mainly created for perf reason to avoid the checks - private Index(int value) - { - _value = value; - } - - /// Create an Index pointing at first element. - public static Index Start => new Index(0); - - /// Create an Index pointing at beyond last element. - public static Index End => new Index(~0); - - /// Create an Index from the start at the position indicated by the value. - /// The index value from the start. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Index FromStart(int value) - { - if (value < 0) - { - throw new ArgumentOutOfRangeException(nameof(value), "value must be non-negative"); - } - - return new Index(value); - } - - /// Create an Index from the end at the position indicated by the value. - /// The index value from the end. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Index FromEnd(int value) - { - if (value < 0) - { - throw new ArgumentOutOfRangeException(nameof(value), "value must be non-negative"); - } - - return new Index(~value); - } - - /// Returns the index value. - public int Value - { - get - { - if (_value < 0) - { - return ~_value; - } - else - { - return _value; - } - } - } - - /// Indicates whether the index is from the start or the end. - public bool IsFromEnd => _value < 0; - - /// Calculate the offset from the start using the giving collection length. - /// The length of the collection that the Index will be used with. length has to be a positive value - /// - /// For performance reason, we don't validate the input length parameter and the returned offset value against negative values. - /// we don't validate either the returned offset is greater than the input length. - /// It is expected Index will be used with collections which always have non negative length/count. If the returned offset is negative and - /// then used to index a collection will get out of range exception which will be same affect as the validation. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int GetOffset(int length) - { - var offset = _value; - if (IsFromEnd) - { - // offset = length - (~value) - // offset = length + (~(~value) + 1) - // offset = length + value + 1 - - offset += length + 1; - } - return offset; - } - - /// Indicates whether the current Index object is equal to another object of the same type. - /// An object to compare with this object - public override bool Equals(object? value) => value is Index && _value == ((Index)value)._value; - - /// Indicates whether the current Index object is equal to another Index object. - /// An object to compare with this object - public bool Equals(Index other) => _value == other._value; - - /// Returns the hash code for this instance. - public override int GetHashCode() => _value; - - /// Converts integer number to an Index. - public static implicit operator Index(int value) => FromStart(value); - - /// Converts the value of the current Index object to its equivalent string representation. - public override string ToString() - { - if (IsFromEnd) - return "^" + ((uint)Value).ToString(); - - return ((uint)Value).ToString(); - } - } - - /// Represent a range has start and end indexes. - /// - /// Range is used by the C# compiler to support the range syntax. - /// - /// int[] someArray = new int[5] { 1, 2, 3, 4, 5 }; - /// int[] subArray1 = someArray[0..2]; // { 1, 2 } - /// int[] subArray2 = someArray[1..^0]; // { 2, 3, 4, 5 } - /// - /// - internal readonly struct Range : IEquatable - { - /// Represent the inclusive start index of the Range. - public Index Start { get; } - - /// Represent the exclusive end index of the Range. - public Index End { get; } - - /// Construct a Range object using the start and end indexes. - /// Represent the inclusive start index of the range. - /// Represent the exclusive end index of the range. - public Range(Index start, Index end) - { - Start = start; - End = end; - } - - /// Indicates whether the current Range object is equal to another object of the same type. - /// An object to compare with this object - public override bool Equals(object? value) => - value is Range r && - r.Start.Equals(Start) && - r.End.Equals(End); - - /// Indicates whether the current Range object is equal to another Range object. - /// An object to compare with this object - public bool Equals(Range other) => other.Start.Equals(Start) && other.End.Equals(End); - - /// Returns the hash code for this instance. - public override int GetHashCode() - { - return Start.GetHashCode() * 31 + End.GetHashCode(); - } - - /// Converts the value of the current Range object to its equivalent string representation. - public override string ToString() - { - return Start + ".." + End; - } - - /// Create a Range object starting from start index to the end of the collection. - public static Range StartAt(Index start) => new Range(start, Index.End); - - /// Create a Range object starting from first element in the collection to the end Index. - public static Range EndAt(Index end) => new Range(Index.Start, end); - - /// Create a Range object starting from first element to the end. - public static Range All => new Range(Index.Start, Index.End); - - /// Calculate the start offset and length of range object using a collection length. - /// The length of the collection that the range will be used with. length has to be a positive value. - /// - /// For performance reason, we don't validate the input length parameter against negative values. - /// It is expected Range will be used with collections which always have non negative length/count. - /// We validate the range is inside the length scope though. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public (int Offset, int Length) GetOffsetAndLength(int length) - { - int start; - var startIndex = Start; - if (startIndex.IsFromEnd) - start = length - startIndex.Value; - else - start = startIndex.Value; - - int end; - var endIndex = End; - if (endIndex.IsFromEnd) - end = length - endIndex.Value; - else - end = endIndex.Value; - - if ((uint)end > (uint)length || (uint)start > (uint)end) - { - throw new ArgumentOutOfRangeException(nameof(length)); - } - - return (start, end - start); - } - } -} namespace System.Runtime.CompilerServices { diff --git a/RecursiveExtractor/StreamReadExactlyPolyfill.cs b/RecursiveExtractor/StreamReadExactlyPolyfill.cs new file mode 100644 index 00000000..1a2a0f55 --- /dev/null +++ b/RecursiveExtractor/StreamReadExactlyPolyfill.cs @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. Licensed under the MIT License. + +#if !NET7_0_OR_GREATER +using System.IO; + +namespace Microsoft.CST.RecursiveExtractor +{ + internal static class StreamReadExactlyPolyfill + { + internal static void ReadExactly(this Stream stream, byte[] buffer, int offset, int count) + { + int totalRead = 0; + while (totalRead < count) + { + int read = stream.Read(buffer, offset + totalRead, count - totalRead); + if (read == 0) + { + throw new EndOfStreamException(); + } + totalRead += read; + } + } + } +} +#endif diff --git a/coverlet.runsettings b/coverlet.runsettings new file mode 100644 index 00000000..d7c98c23 --- /dev/null +++ b/coverlet.runsettings @@ -0,0 +1,17 @@ + + + + + + + cobertura,opencover + **/obj/**,**/bin/** + false + true + false + true + + + + +