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
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
+
+
+
+
+