Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
40b7c69
Initial plan
Copilot Feb 4, 2026
479cca3
Initial analysis
Copilot Feb 4, 2026
e0f18f2
Changes before error encountered
Copilot Feb 4, 2026
cca4e14
Fix all MSTest warnings: replace DataTestMethod, ThrowsException, Exp…
Copilot Feb 4, 2026
bdfa42c
Revert pipeline dotnetTestArgs to original format to fix argument par…
Copilot Feb 5, 2026
1e3c7b2
Fix test result reporting: use --logger trx with relative results dir…
Copilot Feb 5, 2026
71bdc4f
Update FileStream usage in tests to specify access/share
gfs Feb 6, 2026
31ca7fa
Fix indentation in ExtractArchiveFromStream method
Copilot Feb 6, 2026
8990a1e
Migrate tests from MSTest to xUnit
gfs Feb 6, 2026
c93472b
Refactor TimeOutTests to remove expectedNumFiles param
gfs Feb 6, 2026
d72326c
Merge branch 'copilot/resolve-mstest-warnings' of https://github.com/…
gfs Feb 6, 2026
4543935
Fix pipeline: remove duplicate --results-directory argument
Copilot Feb 6, 2026
d91b6ce
Add coverlet.runsettings for code coverage HTML report compatibility
Copilot Feb 6, 2026
d85415e
Wrapped the Directory.Delete cleanup calls in both EnsureDisposedWith…
gfs Feb 6, 2026
438c8e4
Merge branch 'copilot/resolve-mstest-warnings' of https://github.com/…
gfs Feb 6, 2026
d1365c6
Refactor test cleanup to use process-unique temp dirs
gfs Feb 6, 2026
2999ca8
Refactor test data to use TheoryData in xUnit tests
gfs Feb 6, 2026
5a3f43f
Refactor CliTests: static methods, C# 12 collections
gfs Feb 6, 2026
fcdff49
Add PublishCodeCoverageResults@2 task via postTest parameter
Copilot Feb 6, 2026
0372d99
Ensure reliable stream reads with ReadExactly polyfill
gfs Feb 6, 2026
814aab5
Merge branch 'copilot/resolve-mstest-warnings' of https://github.com/…
gfs Feb 6, 2026
cb99764
Refactor test base class and remove cleanup fixture
gfs Feb 6, 2026
3a348b6
Refactor tests to use XUnit fixture pattern
gfs Feb 6, 2026
5ce022d
Use shared test collection fixture for extractor tests
gfs Feb 6, 2026
2eb47a3
Update RecursiveExtractor.Tests/ExtractorTests/BaseExtractorTestClass.cs
gfs Feb 6, 2026
04abdc0
Update RecursiveExtractor.Tests/ExtractorTests/BaseExtractorTestClass.cs
gfs Feb 6, 2026
89b5e72
Refactor test data to use TheoryData<string> in xUnit
gfs Feb 6, 2026
ce19536
Merge branch 'copilot/resolve-mstest-warnings' of https://github.com/…
gfs Feb 6, 2026
129bb9e
Suppress CS1685 warning and remove DiscUtils packages
gfs Feb 6, 2026
d43b4c0
Remove shared test collection from several test classes
gfs Feb 6, 2026
01357d3
Remove Index and Range polyfills; use Bcl.Memory
gfs Feb 6, 2026
2dd6e2d
Add null-forgiving operator to name in TarExtractor
gfs Feb 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,5 @@
</PackageReference>
</ItemGroup>

<!-- MSTest config -->
<PropertyGroup>
<EnableMSTestRunner>true</EnableMSTestRunner>
<TestingPlatformDotnetTestSupport>true</TestingPlatformDotnetTestSupport>
<MSTestAnalysisMode>Recommended</MSTestAnalysisMode>
</PropertyGroup>

</Project>
12 changes: 10 additions & 2 deletions Pipelines/recursive-extractor-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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:
Expand Down
12 changes: 10 additions & 2 deletions Pipelines/recursive-extractor-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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:
Expand Down
127 changes: 62 additions & 65 deletions RecursiveExtractor.Cli.Tests/CliTests/CliTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<BaseExtractorTestClass>
{
[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);
Expand All @@ -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();
Expand All @@ -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<string>();
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();
Expand All @@ -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<string>();
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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
<OutputType>Exe</OutputType>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Testing.Extensions.CodeCoverage" Version="17.14.2" />
<PackageReference Include="Microsoft.Testing.Extensions.TrxReport" Version="1.8.4" />
<PackageReference Include="MSTest" Version="3.10.4" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.2" />
<PackageReference Include="coverlet.collector" Version="6.0.4" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
</ItemGroup>

<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion RecursiveExtractor.Cli.Tests/Usings.cs
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
global using Microsoft.VisualStudio.TestTools.UnitTesting;
global using Xunit;

3 changes: 3 additions & 0 deletions RecursiveExtractor.Tests/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
using Xunit;

[assembly: CollectionBehavior(DisableTestParallelization = false)]
24 changes: 14 additions & 10 deletions RecursiveExtractor.Tests/ExtractorTests/BaseExtractorTestClass.cs
Original file line number Diff line number Diff line change
@@ -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
/// <summary>
/// XUnit test fixture class for extractor tests. Sets up logging and test directories. Tests should use this class as a fixture via IClassFixture&lt;BaseExtractorTestClass&gt; to get the benefits of the setup and teardown.
/// </summary>
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
Expand All @@ -27,5 +25,11 @@ public static void ClassInitialize(TestContext context)

LogManager.Configuration = config;
}
protected static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();

/// <inheritdoc />
public void Dispose()
{
TestPathHelpers.DeleteTestDirectory();
GC.SuppressFinalize(this);
}
}
Loading