From ccb3aafb30ac123a4af0c16ae0bfb4fd1e25480d Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Wed, 13 May 2026 13:16:35 +0100 Subject: [PATCH] AI clean up for tests --- tests/SharpCompress.Test/TempDirectory.cs | 83 +++++++++++++++++++++++ tests/SharpCompress.Test/TestBase.cs | 67 +++++------------- 2 files changed, 101 insertions(+), 49 deletions(-) create mode 100644 tests/SharpCompress.Test/TempDirectory.cs diff --git a/tests/SharpCompress.Test/TempDirectory.cs b/tests/SharpCompress.Test/TempDirectory.cs new file mode 100644 index 00000000..e155210f --- /dev/null +++ b/tests/SharpCompress.Test/TempDirectory.cs @@ -0,0 +1,83 @@ +using System; +using System.IO; +using System.Threading.Tasks; + +namespace SharpCompress.Test; + +internal sealed class TempDirectory : IAsyncDisposable +{ + private const int MaxDeleteAttempts = 5; + private static readonly TimeSpan DeleteRetryDelay = TimeSpan.FromMilliseconds(100); + + public TempDirectory(string prefix) + { + Path = System.IO.Path.Combine(System.IO.Path.GetTempPath(), $"{prefix}.{Guid.NewGuid():N}"); + Directory.CreateDirectory(Path); + } + + public string Path { get; } + + public string GetDirectory(string name) + { + var path = System.IO.Path.Combine(Path, name); + Directory.CreateDirectory(path); + return path; + } + + public string CreateDirectory(string name) + { + var path = System.IO.Path.Combine(Path, name, System.IO.Path.GetRandomFileName()); + Directory.CreateDirectory(path); + return path; + } + + public void ResetDirectory(string name) + { + DeleteDirectory(System.IO.Path.Combine(Path, name)); + Directory.CreateDirectory(System.IO.Path.Combine(Path, name)); + } + + public async ValueTask DisposeAsync() + { + for (var attempt = 1; attempt <= MaxDeleteAttempts; attempt++) + { + try + { + DeleteDirectory(Path); + if (Directory.Exists(Path)) + { + throw new IOException( + $"Temp test directory '{Path}' still exists after deletion." + ); + } + + return; + } + catch (Exception ex) + when (IsRetryableDeleteException(ex) && attempt < MaxDeleteAttempts) + { + await Task.Delay(DeleteRetryDelay).ConfigureAwait(false); + } + catch (Exception ex) when (IsRetryableDeleteException(ex)) + { + throw new InvalidOperationException( + $"Failed to clean up temp test directory '{Path}'.", + ex + ); + } + } + + throw new InvalidOperationException($"Temp test directory '{Path}' was not cleaned up."); + } + + private static void DeleteDirectory(string path) + { + if (Directory.Exists(path)) + { + Directory.Delete(path, true); + } + } + + private static bool IsRetryableDeleteException(Exception ex) => + ex is IOException or UnauthorizedAccessException; +} diff --git a/tests/SharpCompress.Test/TestBase.cs b/tests/SharpCompress.Test/TestBase.cs index 0cdd6a07..1c244ac8 100644 --- a/tests/SharpCompress.Test/TestBase.cs +++ b/tests/SharpCompress.Test/TestBase.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; -using System.Threading; using System.Threading.Tasks; using SharpCompress.Readers; using Xunit; @@ -31,70 +30,40 @@ public class TestBase : IAsyncDisposable MISC_TEST_FILES_PATH = Path.Combine(SOLUTION_BASE_PATH, "TestArchives", "MiscTest"); } - private readonly Guid _testGuid = Guid.NewGuid(); - private readonly string _testTempDirectory; + private readonly TempDirectory _tempDirectory; protected readonly string SCRATCH_FILES_PATH; protected readonly string SCRATCH2_FILES_PATH; protected TestBase() { - _testTempDirectory = Path.Combine(Path.GetTempPath(), $"SharpCompress.Test.{_testGuid:N}"); - SCRATCH_FILES_PATH = Path.Combine(_testTempDirectory, "Scratch"); - SCRATCH2_FILES_PATH = Path.Combine(_testTempDirectory, "Scratch2"); - - Directory.CreateDirectory(SCRATCH_FILES_PATH); - Directory.CreateDirectory(SCRATCH2_FILES_PATH); + _tempDirectory = new TempDirectory("SharpCompress.Test"); + SCRATCH_FILES_PATH = _tempDirectory.GetDirectory("Scratch"); + SCRATCH2_FILES_PATH = _tempDirectory.GetDirectory("Scratch2"); } - //always use async dispose since we have I/O and sync Dispose doesn't wait when using xunit - public async ValueTask DisposeAsync() - { - await Task.CompletedTask; - DeleteScratchDirectory(_testTempDirectory); - } + // Always use async dispose since we have I/O and sync Dispose doesn't wait when using xunit. + public ValueTask DisposeAsync() => _tempDirectory.DisposeAsync(); public void CleanScratch() { - ResetScratchDirectory(SCRATCH_FILES_PATH); - ResetScratchDirectory(SCRATCH2_FILES_PATH); + _tempDirectory.ResetDirectory("Scratch"); + _tempDirectory.ResetDirectory("Scratch2"); } - private static void ResetScratchDirectory(string path) - { - if (Directory.Exists(path)) - { - Directory.Delete(path, true); - } + protected string CreateScratchDirectory(string name) => + _tempDirectory.CreateDirectory(Path.Combine("Scratch", name)); - Directory.CreateDirectory(path); - } + protected string CreateScratch2Directory(string name) => + _tempDirectory.CreateDirectory(Path.Combine("Scratch2", name)); - private static void DeleteScratchDirectory(string path) - { - if (!Directory.Exists(path)) - { - return; - } + protected string GetScratchPath(params string[] parts) => + CombinePath(SCRATCH_FILES_PATH, parts); - try - { - Directory.Delete(path, true); - } - catch (Exception ex) when (ex is IOException or UnauthorizedAccessException) - { - throw new InvalidOperationException( - $"Failed to clean up temp test directory '{path}'.", - ex - ); - } + protected string GetScratch2Path(params string[] parts) => + CombinePath(SCRATCH2_FILES_PATH, parts); - if (Directory.Exists(path)) - { - throw new InvalidOperationException( - $"Temp test directory '{path}' was not cleaned up." - ); - } - } + private static string CombinePath(string root, string[] parts) => + parts.Length == 0 ? root : Path.Combine(root, Path.Combine(parts)); public void VerifyFiles() {