diff --git a/docs/API.md b/docs/API.md index 28d40169..d2a944ad 100644 --- a/docs/API.md +++ b/docs/API.md @@ -20,14 +20,18 @@ using (var archive = RarArchive.OpenArchive("file.rar")) using (var archive = SevenZipArchive.OpenArchive("file.7z")) using (var archive = GZipArchive.OpenArchive("file.gz")) -// With options -var options = new ReaderOptions +// With fluent options (preferred) +var options = ReaderOptions.ForEncryptedArchive("password") + .WithArchiveEncoding(new ArchiveEncoding { Default = Encoding.GetEncoding(932) }); +using (var archive = ZipArchive.OpenArchive("encrypted.zip", options)) + +// Alternative: object initializer +var options2 = new ReaderOptions { Password = "password", LeaveStreamOpen = true, ArchiveEncoding = new ArchiveEncoding { Default = Encoding.GetEncoding(932) } }; -using (var archive = ZipArchive.OpenArchive("encrypted.zip", options)) ``` ### Creating Archives @@ -44,16 +48,21 @@ using (var archive = ZipArchive.CreateArchive()) using (var archive = TarArchive.CreateArchive()) using (var archive = GZipArchive.CreateArchive()) -// With options -var options = new WriterOptions(CompressionType.Deflate) -{ - CompressionLevel = 9, - LeaveStreamOpen = false -}; +// With fluent options (preferred) +var options = WriterOptions.ForZip() + .WithCompressionLevel(9) + .WithLeaveStreamOpen(false); using (var archive = ZipArchive.CreateArchive()) { archive.SaveTo("output.zip", options); } + +// Alternative: constructor with object initializer +var options2 = new WriterOptions(CompressionType.Deflate) +{ + CompressionLevel = 9, + LeaveStreamOpen = false +}; ``` --- @@ -222,33 +231,65 @@ using (var writer = WriterFactory.OpenWriter(stream, ArchiveType.Zip, Compressio ### ReaderOptions +Use factory presets and fluent helpers for common configurations: + ```csharp -var options = new ReaderOptions -{ - Password = "password", // For encrypted archives - LeaveStreamOpen = true, // Don't close wrapped stream - ArchiveEncoding = new ArchiveEncoding // Custom character encoding - { - Default = Encoding.GetEncoding(932) - } -}; +// External stream with password and custom encoding +var options = ReaderOptions.ForExternalStream() + .WithPassword("password") + .WithArchiveEncoding(new ArchiveEncoding { Default = Encoding.GetEncoding(932) }); + using (var archive = ZipArchive.OpenArchive("file.zip", options)) { // ... } +// Common presets +var safeOptions = ReaderOptions.SafeExtract; // No overwrite +var flatOptions = ReaderOptions.FlatExtract; // No directory structure + // Factory defaults: // - file path / FileInfo overloads use LeaveStreamOpen = false // - stream overloads use LeaveStreamOpen = true ``` +Alternative: traditional object initializer: + +```csharp +var options = new ReaderOptions +{ + Password = "password", + LeaveStreamOpen = true, + ArchiveEncoding = new ArchiveEncoding { Default = Encoding.GetEncoding(932) } +}; +``` + ### WriterOptions +Factory methods provide a clean, discoverable way to create writer options: + +```csharp +// Factory methods for common archive types +var zipOptions = WriterOptions.ForZip() // ZIP with Deflate + .WithCompressionLevel(9) // 0-9 for Deflate + .WithLeaveStreamOpen(false); // Close stream when done + +var tarOptions = WriterOptions.ForTar(CompressionType.GZip) // TAR with GZip + .WithLeaveStreamOpen(false); + +var gzipOptions = WriterOptions.ForGZip() // GZip file + .WithCompressionLevel(6); + +archive.SaveTo("output.zip", zipOptions); +``` + +Alternative: traditional constructor with object initializer: + ```csharp var options = new WriterOptions(CompressionType.Deflate) { - CompressionLevel = 9, // 0-9 for Deflate - LeaveStreamOpen = true, // Don't close stream + CompressionLevel = 9, + LeaveStreamOpen = true, }; archive.SaveTo("output.zip", options); ``` @@ -326,7 +367,7 @@ ArchiveType.ZStandard try { using (var archive = ZipArchive.Open("archive.zip", - new ReaderOptions { Password = "password" })) + ReaderOptions.ForEncryptedArchive("password"))) { archive.WriteToDirectory(@"C:\output"); } @@ -353,7 +394,7 @@ var progress = new Progress(report => Console.WriteLine($"Extracting {report.EntryPath}: {report.PercentComplete}%"); }); -var options = new ReaderOptions { Progress = progress }; +var options = ReaderOptions.ForOwnedFile().WithProgress(progress); using (var archive = ZipArchive.OpenArchive("archive.zip", options)) { archive.WriteToDirectory(@"C:\output"); diff --git a/docs/ENCODING.md b/docs/ENCODING.md index eb850679..5ae0de09 100644 --- a/docs/ENCODING.md +++ b/docs/ENCODING.md @@ -18,14 +18,9 @@ Most archive formats store filenames and metadata as bytes. SharpCompress must c using SharpCompress.Common; using SharpCompress.Readers; -// Configure encoding before opening archive -var options = new ReaderOptions -{ - ArchiveEncoding = new ArchiveEncoding - { - Default = Encoding.GetEncoding(932) // cp932 for Japanese - } -}; +// Configure encoding using fluent factory method (preferred) +var options = ReaderOptions.ForEncoding( + new ArchiveEncoding { Default = Encoding.GetEncoding(932) }); // cp932 for Japanese using (var archive = ZipArchive.OpenArchive("japanese.zip", options)) { @@ -34,6 +29,12 @@ using (var archive = ZipArchive.OpenArchive("japanese.zip", options)) Console.WriteLine(entry.Key); // Now shows correct characters } } + +// Alternative: object initializer +var options2 = new ReaderOptions +{ + ArchiveEncoding = new ArchiveEncoding { Default = Encoding.GetEncoding(932) } +}; ``` ### ArchiveEncoding Properties @@ -47,10 +48,8 @@ using (var archive = ZipArchive.OpenArchive("japanese.zip", options)) **Archive API:** ```csharp -var options = new ReaderOptions -{ - ArchiveEncoding = new ArchiveEncoding { Default = Encoding.GetEncoding(932) } -}; +var options = ReaderOptions.ForEncoding( + new ArchiveEncoding { Default = Encoding.GetEncoding(932) }); using (var archive = ZipArchive.OpenArchive("file.zip", options)) { // Use archive with correct encoding @@ -59,10 +58,8 @@ using (var archive = ZipArchive.OpenArchive("file.zip", options)) **Reader API:** ```csharp -var options = new ReaderOptions -{ - ArchiveEncoding = new ArchiveEncoding { Default = Encoding.GetEncoding(932) } -}; +var options = ReaderOptions.ForEncoding( + new ArchiveEncoding { Default = Encoding.GetEncoding(932) }); using (var stream = File.OpenRead("file.zip")) using (var reader = ReaderFactory.OpenReader(stream, options)) { diff --git a/docs/USAGE.md b/docs/USAGE.md index 2f292e8e..df863d32 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -94,11 +94,11 @@ Note: Extracting a solid rar or 7z file needs to be done in sequential order to `ExtractAllEntries` is primarily intended for solid archives (like solid Rar) or 7Zip archives, where sequential extraction provides the best performance. For general/simple extraction with any supported archive type, use `archive.WriteToDirectory()` instead. ```C# -using (var archive = RarArchive.OpenArchive("Test.rar", new ReaderOptions -{ - ExtractFullPath = true, - Overwrite = true -})) +// Using fluent factory method for extraction options +using (var archive = RarArchive.OpenArchive("Test.rar", + ReaderOptions.ForOwnedFile() + .WithExtractFullPath(true) + .WithOverwrite(true))) { // Simple extraction with RarArchive; this WriteToDirectory pattern works for all archive types archive.WriteToDirectory(@"D:\temp"); @@ -130,12 +130,11 @@ var progress = new Progress(report => Console.WriteLine($"Extracting {report.EntryPath}: {report.PercentComplete}%"); }); -using (var archive = RarArchive.OpenArchive("archive.rar", new ReaderOptions -{ - Progress = progress, - ExtractFullPath = true, - Overwrite = true -})) // Must be solid Rar or 7Zip +using (var archive = RarArchive.OpenArchive("archive.rar", + ReaderOptions.ForOwnedFile() + .WithProgress(progress) + .WithExtractFullPath(true) + .WithOverwrite(true))) // Must be solid Rar or 7Zip { archive.WriteToDirectory(@"D:\output"); } @@ -181,10 +180,10 @@ using (var reader = ReaderFactory.OpenReader(stream)) ```C# using (Stream stream = File.OpenWrite("C:\\temp.tgz")) -using (var writer = WriterFactory.OpenWriter(stream, ArchiveType.Tar, new WriterOptions(CompressionType.GZip) - { - LeaveStreamOpen = true - })) +using (var writer = WriterFactory.OpenWriter( + stream, + ArchiveType.Tar, + WriterOptions.ForTar(CompressionType.GZip).WithLeaveStreamOpen(true))) { writer.WriteAll("D:\\temp", "*", SearchOption.AllDirectories); } @@ -193,15 +192,15 @@ using (var writer = WriterFactory.OpenWriter(stream, ArchiveType.Tar, new Writer ### Extract zip which has non-utf8 encoded filename(cp932) ```C# -var opts = new SharpCompress.Readers.ReaderOptions(); var encoding = Encoding.GetEncoding(932); -opts.ArchiveEncoding = new SharpCompress.Common.ArchiveEncoding(); -opts.ArchiveEncoding.CustomDecoder = (data, x, y) => -{ - return encoding.GetString(data); -}; -var tr = SharpCompress.Archives.Zip.ZipArchive.OpenArchive("test.zip", opts); -foreach(var entry in tr.Entries) +var opts = new ReaderOptions() + .WithArchiveEncoding(new ArchiveEncoding + { + CustomDecoder = (data, x, y) => encoding.GetString(data) + }); + +using var archive = ZipArchive.OpenArchive("test.zip", opts); +foreach(var entry in archive.Entries) { Console.WriteLine($"{entry.Key}"); } diff --git a/src/SharpCompress/Readers/ReaderOptions.cs b/src/SharpCompress/Readers/ReaderOptions.cs index 692d9ef5..0caac68d 100644 --- a/src/SharpCompress/Readers/ReaderOptions.cs +++ b/src/SharpCompress/Readers/ReaderOptions.cs @@ -8,10 +8,15 @@ namespace SharpCompress.Readers; /// Options for configuring reader behavior when opening archives. /// /// -/// This class is immutable. Use the with expression to create modified copies: +/// This class is immutable. Use factory presets and fluent helpers for common configurations: /// -/// var options = new ReaderOptions { Password = "secret" }; -/// options = options with { LeaveStreamOpen = false }; +/// var options = ReaderOptions.ForExternalStream() +/// .WithPassword("secret") +/// .WithLookForHeader(true); +/// +/// Or use object initializers for simple cases: +/// +/// var options = new ReaderOptions { Password = "secret", LeaveStreamOpen = false }; /// /// public sealed record ReaderOptions : IReaderOptions @@ -169,6 +174,29 @@ public sealed record ReaderOptions : IReaderOptions /// public static ReaderOptions FlatExtract => new() { ExtractFullPath = false, Overwrite = true }; + /// + /// Creates ReaderOptions for reading encrypted archives. + /// + /// The password for encrypted archives. + public static ReaderOptions ForEncryptedArchive(string? password = null) => + new ReaderOptions().WithPassword(password); + + /// + /// Creates ReaderOptions for archives with custom character encoding. + /// + /// The encoding for archive entry names. + public static ReaderOptions ForEncoding(IArchiveEncoding encoding) => + new ReaderOptions().WithArchiveEncoding(encoding); + + /// + /// Creates ReaderOptions for self-extracting archives that require header search. + /// + public static ReaderOptions ForSelfExtractingArchive(string? password = null) => + new ReaderOptions() + .WithLookForHeader(true) + .WithPassword(password) + .WithRewindableBufferSize(1_048_576); // 1MB for SFX archives + /// /// Default symbolic link handler that logs a warning message. /// @@ -179,66 +207,9 @@ public sealed record ReaderOptions : IReaderOptions ); } - /// - /// Creates a new ReaderOptions instance with the specified password. - /// - /// The password for encrypted archives. - public ReaderOptions(string? password) => Password = password; - - /// - /// Creates a new ReaderOptions instance with the specified password and header search option. - /// - /// The password for encrypted archives. - /// Whether to search for the archive header. - public ReaderOptions(string? password, bool lookForHeader) - { - Password = password; - LookForHeader = lookForHeader; - } - - /// - /// Creates a new ReaderOptions instance with the specified encoding. - /// - /// The encoding for archive entry names. - public ReaderOptions(IArchiveEncoding encoding) => ArchiveEncoding = encoding; - - /// - /// Creates a new ReaderOptions instance with the specified password and encoding. - /// - /// The password for encrypted archives. - /// The encoding for archive entry names. - public ReaderOptions(string? password, IArchiveEncoding encoding) - { - Password = password; - ArchiveEncoding = encoding; - } - - /// - /// Creates a new ReaderOptions instance with the specified stream open behavior. - /// - /// Whether to leave the stream open after reading. - public ReaderOptions(bool leaveStreamOpen) - { - LeaveStreamOpen = leaveStreamOpen; - } - - /// - /// Creates a new ReaderOptions instance with the specified stream open behavior and password. - /// - /// Whether to leave the stream open after reading. - /// The password for encrypted archives. - public ReaderOptions(bool leaveStreamOpen, string? password) - { - LeaveStreamOpen = leaveStreamOpen; - Password = password; - } - - /// - /// Creates a new ReaderOptions instance with the specified buffer size. - /// - /// The buffer size for stream operations. - public ReaderOptions(int bufferSize) - { - BufferSize = bufferSize; - } + // Note: Parameterized constructors have been removed. + // Use fluent With*() helpers or object initializers instead: + // new ReaderOptions().WithPassword("secret").WithLookForHeader(true) + // or + // new ReaderOptions { Password = "secret", LookForHeader = true } } diff --git a/src/SharpCompress/Readers/ReaderOptionsExtensions.cs b/src/SharpCompress/Readers/ReaderOptionsExtensions.cs new file mode 100644 index 00000000..26a8dfbe --- /dev/null +++ b/src/SharpCompress/Readers/ReaderOptionsExtensions.cs @@ -0,0 +1,127 @@ +using System; +using SharpCompress.Common; +using SharpCompress.Common.Options; + +namespace SharpCompress.Readers; + +/// +/// Extension methods for fluent configuration of reader options. +/// +public static class ReaderOptionsExtensions +{ + /// + /// Creates a copy with the specified LeaveStreamOpen value. + /// + public static ReaderOptions WithLeaveStreamOpen( + this ReaderOptions options, + bool leaveStreamOpen + ) => options with { LeaveStreamOpen = leaveStreamOpen }; + + /// + /// Creates a copy with the specified password. + /// + public static ReaderOptions WithPassword(this ReaderOptions options, string? password) => + options with + { + Password = password, + }; + + /// + /// Creates a copy with the specified archive encoding. + /// + public static ReaderOptions WithArchiveEncoding( + this ReaderOptions options, + IArchiveEncoding encoding + ) => options with { ArchiveEncoding = encoding }; + + /// + /// Creates a copy with the specified LookForHeader value. + /// + public static ReaderOptions WithLookForHeader(this ReaderOptions options, bool lookForHeader) => + options with + { + LookForHeader = lookForHeader, + }; + + /// + /// Creates a copy with the specified DisableCheckIncomplete value. + /// + public static ReaderOptions WithDisableCheckIncomplete( + this ReaderOptions options, + bool disableCheckIncomplete + ) => options with { DisableCheckIncomplete = disableCheckIncomplete }; + + /// + /// Creates a copy with the specified buffer size. + /// + public static ReaderOptions WithBufferSize(this ReaderOptions options, int bufferSize) => + options with + { + BufferSize = bufferSize, + }; + + /// + /// Creates a copy with the specified extension hint. + /// + public static ReaderOptions WithExtensionHint( + this ReaderOptions options, + string? extensionHint + ) => options with { ExtensionHint = extensionHint }; + + /// + /// Creates a copy with the specified progress reporter. + /// + public static ReaderOptions WithProgress( + this ReaderOptions options, + IProgress? progress + ) => options with { Progress = progress }; + + /// + /// Creates a copy with the specified rewindable buffer size. + /// + public static ReaderOptions WithRewindableBufferSize( + this ReaderOptions options, + int? rewindableBufferSize + ) => options with { RewindableBufferSize = rewindableBufferSize }; + + /// + /// Creates a copy with the specified overwrite setting. + /// + public static ReaderOptions WithOverwrite(this ReaderOptions options, bool overwrite) => + options with + { + Overwrite = overwrite, + }; + + /// + /// Creates a copy with the specified extract full path setting. + /// + public static ReaderOptions WithExtractFullPath( + this ReaderOptions options, + bool extractFullPath + ) => options with { ExtractFullPath = extractFullPath }; + + /// + /// Creates a copy with the specified preserve file time setting. + /// + public static ReaderOptions WithPreserveFileTime( + this ReaderOptions options, + bool preserveFileTime + ) => options with { PreserveFileTime = preserveFileTime }; + + /// + /// Creates a copy with the specified preserve attributes setting. + /// + public static ReaderOptions WithPreserveAttributes( + this ReaderOptions options, + bool preserveAttributes + ) => options with { PreserveAttributes = preserveAttributes }; + + /// + /// Creates a copy with the specified symbolic link handler. + /// + public static ReaderOptions WithSymbolicLinkHandler( + this ReaderOptions options, + Action? handler + ) => options with { SymbolicLinkHandler = handler }; +} diff --git a/src/SharpCompress/Writers/GZip/GZipWriterOptions.cs b/src/SharpCompress/Writers/GZip/GZipWriterOptions.cs index e660ce09..3477cb3e 100644 --- a/src/SharpCompress/Writers/GZip/GZipWriterOptions.cs +++ b/src/SharpCompress/Writers/GZip/GZipWriterOptions.cs @@ -10,10 +10,9 @@ namespace SharpCompress.Writers.GZip; /// Options for configuring GZip writer behavior. /// /// -/// This class is immutable. Use the with expression to create modified copies: +/// This class is immutable. Use factory methods for creation: /// -/// var options = new GZipWriterOptions { CompressionLevel = 9 }; -/// options = options with { LeaveStreamOpen = false }; +/// var options = WriterOptions.ForGZip().WithLeaveStreamOpen(false).WithCompressionLevel(9); /// /// public sealed record GZipWriterOptions : IWriterOptions @@ -90,14 +89,11 @@ public sealed record GZipWriterOptions : IWriterOptions CompressionLevel = (int)compressionLevel; } - /// - /// Creates a new GZipWriterOptions instance with the specified stream open behavior. - /// - /// Whether to leave the stream open after writing. - public GZipWriterOptions(bool leaveStreamOpen) - { - LeaveStreamOpen = leaveStreamOpen; - } + // Note: Constructor with boolean leaveStreamOpen parameter removed. + // Use the fluent WithLeaveStreamOpen() helper or object initializer instead: + // new GZipWriterOptions() { LeaveStreamOpen = false } + // or + // WriterOptions.ForGZip().WithLeaveStreamOpen(false) /// /// Creates a new GZipWriterOptions instance from an existing WriterOptions instance. diff --git a/src/SharpCompress/Writers/WriterOptions.cs b/src/SharpCompress/Writers/WriterOptions.cs index 87e5683b..37037976 100644 --- a/src/SharpCompress/Writers/WriterOptions.cs +++ b/src/SharpCompress/Writers/WriterOptions.cs @@ -9,10 +9,9 @@ namespace SharpCompress.Writers; /// Options for configuring writer behavior when creating archives. /// /// -/// This class is immutable. Use the with expression to create modified copies: +/// This class is immutable. Use factory methods for creation: /// -/// var options = new WriterOptions(CompressionType.Zip); -/// options = options with { LeaveStreamOpen = false }; +/// var options = WriterOptions.ForZip().WithLeaveStreamOpen(false).WithCompressionLevel(9); /// /// public sealed record WriterOptions : IWriterOptions @@ -92,32 +91,11 @@ public sealed record WriterOptions : IWriterOptions CompressionLevel = compressionLevel; } - /// - /// Creates a new WriterOptions instance with the specified compression type and stream open behavior. - /// - /// The compression type for the archive. - /// Whether to leave the stream open after writing. - public WriterOptions(CompressionType compressionType, bool leaveStreamOpen) - : this(compressionType) - { - LeaveStreamOpen = leaveStreamOpen; - } - - /// - /// Creates a new WriterOptions instance with the specified compression type, level, and stream open behavior. - /// - /// The compression type for the archive. - /// The compression level (algorithm-specific). - /// Whether to leave the stream open after writing. - public WriterOptions( - CompressionType compressionType, - int compressionLevel, - bool leaveStreamOpen - ) - : this(compressionType, compressionLevel) - { - LeaveStreamOpen = leaveStreamOpen; - } + // Note: Constructors with boolean leaveStreamOpen parameter removed. + // Use the fluent WithLeaveStreamOpen() helper or object initializer instead: + // new WriterOptions(type) { LeaveStreamOpen = false } + // or + // WriterOptions.ForZip().WithLeaveStreamOpen(false) /// /// Implicit conversion from CompressionType to WriterOptions. @@ -125,4 +103,23 @@ public sealed record WriterOptions : IWriterOptions /// The compression type. public static implicit operator WriterOptions(CompressionType compressionType) => new(compressionType); + + /// + /// Creates a new ZipWriterOptions for writing ZIP archives. + /// + /// The compression type for the archive. Defaults to Deflate. + public static WriterOptions ForZip(CompressionType compressionType = CompressionType.Deflate) => + new(compressionType); + + /// + /// Creates a new WriterOptions for writing TAR archives. + /// + /// The compression type for the archive. Defaults to None. + public static WriterOptions ForTar(CompressionType compressionType = CompressionType.None) => + new(compressionType); + + /// + /// Creates a new WriterOptions for writing GZip compressed files. + /// + public static WriterOptions ForGZip() => new(CompressionType.GZip); } diff --git a/src/SharpCompress/Writers/WriterOptionsExtensions.cs b/src/SharpCompress/Writers/WriterOptionsExtensions.cs new file mode 100644 index 00000000..d86b5406 --- /dev/null +++ b/src/SharpCompress/Writers/WriterOptionsExtensions.cs @@ -0,0 +1,55 @@ +using System; +using SharpCompress.Common; +using SharpCompress.Common.Options; + +namespace SharpCompress.Writers; + +/// +/// Extension methods for fluent configuration of writer options. +/// +public static class WriterOptionsExtensions +{ + /// + /// Creates a copy with the specified LeaveStreamOpen value. + /// + /// The source options. + /// Whether to leave the stream open. + /// A new options instance with the specified LeaveStreamOpen value. + public static WriterOptions WithLeaveStreamOpen( + this WriterOptions options, + bool leaveStreamOpen + ) => options with { LeaveStreamOpen = leaveStreamOpen }; + + /// + /// Creates a copy with the specified compression level. + /// + /// The source options. + /// The compression level (algorithm-specific). + /// A new options instance with the specified compression level. + public static WriterOptions WithCompressionLevel( + this WriterOptions options, + int compressionLevel + ) => options with { CompressionLevel = compressionLevel }; + + /// + /// Creates a copy with the specified archive encoding. + /// + /// The source options. + /// The archive encoding to use. + /// A new options instance with the specified archive encoding. + public static WriterOptions WithArchiveEncoding( + this WriterOptions options, + IArchiveEncoding archiveEncoding + ) => options with { ArchiveEncoding = archiveEncoding }; + + /// + /// Creates a copy with the specified progress reporter. + /// + /// The source options. + /// The progress reporter. + /// A new options instance with the specified progress reporter. + public static WriterOptions WithProgress( + this WriterOptions options, + IProgress progress + ) => options with { Progress = progress }; +} diff --git a/tests/SharpCompress.Test/ArchiveTests.cs b/tests/SharpCompress.Test/ArchiveTests.cs index 428b96ba..95e10214 100644 --- a/tests/SharpCompress.Test/ArchiveTests.cs +++ b/tests/SharpCompress.Test/ArchiveTests.cs @@ -390,7 +390,7 @@ public class ArchiveTests : ReaderTests ) { var writerOptions = compressionLevel.HasValue - ? new WriterOptions(compressionType, compressionLevel.Value, leaveStreamOpen: true) + ? new WriterOptions(compressionType, compressionLevel.Value) { LeaveStreamOpen = true } : new WriterOptions(compressionType) { LeaveStreamOpen = true }; return WriterFactory.OpenAsyncWriter( new AsyncOnlyStream(stream), diff --git a/tests/SharpCompress.Test/OptionsUsabilityTests.cs b/tests/SharpCompress.Test/OptionsUsabilityTests.cs index c82667c6..603007c9 100644 --- a/tests/SharpCompress.Test/OptionsUsabilityTests.cs +++ b/tests/SharpCompress.Test/OptionsUsabilityTests.cs @@ -108,4 +108,141 @@ public class OptionsUsabilityTests : TestBase writer.Write("entry.bin", source, options) ); } + + [Fact] + public void WriterOptions_Factory_Methods_Create_Valid_Options() + { + // ForZip + var zipOptions = WriterOptions.ForZip(); + Assert.Equal(CompressionType.Deflate, zipOptions.CompressionType); + Assert.True(zipOptions.LeaveStreamOpen); + + // ForTar + var tarOptions = WriterOptions.ForTar(); + Assert.Equal(CompressionType.None, tarOptions.CompressionType); + + // ForGZip + var gzipOptions = WriterOptions.ForGZip(); + Assert.Equal(CompressionType.GZip, gzipOptions.CompressionType); + } + + [Fact] + public void WriterOptions_Fluent_Methods_Modify_Correctly() + { + var options = WriterOptions.ForZip().WithLeaveStreamOpen(false).WithCompressionLevel(9); + + Assert.Equal(CompressionType.Deflate, options.CompressionType); + Assert.Equal(9, options.CompressionLevel); + Assert.False(options.LeaveStreamOpen); + } + + [Fact] + public void WriterOptions_Factory_And_Fluent_Equivalent_To_Constructor() + { + // Factory + fluent approach + var factoryApproach = WriterOptions + .ForZip() + .WithLeaveStreamOpen(false) + .WithCompressionLevel(9); + + // Traditional constructor approach + var constructorApproach = new WriterOptions(CompressionType.Deflate) + { + CompressionLevel = 9, + LeaveStreamOpen = false, + }; + + Assert.Equal(factoryApproach.CompressionType, constructorApproach.CompressionType); + Assert.Equal(factoryApproach.CompressionLevel, constructorApproach.CompressionLevel); + Assert.Equal(factoryApproach.LeaveStreamOpen, constructorApproach.LeaveStreamOpen); + } + + [Fact] + public void ReaderOptions_Fluent_Methods_Modify_Correctly() + { + var options = new ReaderOptions() + .WithLeaveStreamOpen(false) + .WithPassword("secret") + .WithLookForHeader(true) + .WithBufferSize(65536); + + Assert.False(options.LeaveStreamOpen); + Assert.Equal("secret", options.Password); + Assert.True(options.LookForHeader); + Assert.Equal(65536, options.BufferSize); + } + + [Fact] + public void ReaderOptions_Fluent_And_Initializer_Equivalent() + { + // Fluent approach + var fluentApproach = new ReaderOptions() + .WithLeaveStreamOpen(false) + .WithPassword("secret") + .WithLookForHeader(true) + .WithOverwrite(false); + + // Object initializer approach + var initializerApproach = new ReaderOptions + { + LeaveStreamOpen = false, + Password = "secret", + LookForHeader = true, + Overwrite = false, + }; + + Assert.Equal(fluentApproach.LeaveStreamOpen, initializerApproach.LeaveStreamOpen); + Assert.Equal(fluentApproach.Password, initializerApproach.Password); + Assert.Equal(fluentApproach.LookForHeader, initializerApproach.LookForHeader); + Assert.Equal(fluentApproach.Overwrite, initializerApproach.Overwrite); + } + + [Fact] + public void ReaderOptions_Presets_Have_Correct_Defaults() + { + var external = ReaderOptions.ForExternalStream; + Assert.True(external.LeaveStreamOpen); + + var owned = ReaderOptions.ForOwnedFile; + Assert.False(owned.LeaveStreamOpen); + + var safe = ReaderOptions.SafeExtract; + Assert.False(safe.Overwrite); + + var flat = ReaderOptions.FlatExtract; + Assert.False(flat.ExtractFullPath); + Assert.True(flat.Overwrite); + } + + [Fact] + public void ReaderOptions_Factory_ForEncryptedArchive_Sets_Password() + { + var options = ReaderOptions.ForEncryptedArchive("myPassword"); + Assert.Equal("myPassword", options.Password); + + var noPassword = ReaderOptions.ForEncryptedArchive(); + Assert.Null(noPassword.Password); + } + + [Fact] + public void ReaderOptions_Factory_ForEncoding_Sets_Encoding() + { + var encoding = new ArchiveEncoding { Default = System.Text.Encoding.UTF8 }; + var options = ReaderOptions.ForEncoding(encoding); + Assert.Equal(encoding, options.ArchiveEncoding); + } + + [Fact] + public void ReaderOptions_Factory_ForSelfExtractingArchive_Configures_Correctly() + { + var options = ReaderOptions.ForSelfExtractingArchive("password"); + Assert.True(options.LookForHeader); + Assert.Equal("password", options.Password); + Assert.Equal(1_048_576, options.RewindableBufferSize); + + var noPassword = ReaderOptions.ForSelfExtractingArchive(); + Assert.True(noPassword.LookForHeader); + Assert.Null(noPassword.Password); + Assert.Equal(1_048_576, noPassword.RewindableBufferSize); + } } diff --git a/tests/SharpCompress.Test/Zip/ZipReaderTests.cs b/tests/SharpCompress.Test/Zip/ZipReaderTests.cs index dea7a506..e4e6ee7a 100644 --- a/tests/SharpCompress.Test/Zip/ZipReaderTests.cs +++ b/tests/SharpCompress.Test/Zip/ZipReaderTests.cs @@ -149,7 +149,12 @@ public class ZipReaderTests : ReaderTests using var stream = new TestStream( File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.dd.zip")) ); - using (var reader = ReaderFactory.OpenReader(stream)) + using ( + var reader = ReaderFactory.OpenReader( + stream, + new ReaderOptions().WithLeaveStreamOpen(false) + ) + ) { while (reader.MoveToNextEntry()) {