mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-02-04 05:25:00 +00:00
Add support for empty directory entries in archives
Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com>
This commit is contained in:
@@ -96,6 +96,9 @@ public abstract class AbstractWritableArchive<TEntry, TVolume>
|
||||
DateTime? modified
|
||||
) => AddEntry(key, source, closeStream, size, modified);
|
||||
|
||||
IArchiveEntry IWritableArchive.AddDirectoryEntry(string key, DateTime? modified) =>
|
||||
AddDirectoryEntry(key, modified);
|
||||
|
||||
public TEntry AddEntry(
|
||||
string key,
|
||||
Stream source,
|
||||
@@ -136,6 +139,22 @@ public abstract class AbstractWritableArchive<TEntry, TVolume>
|
||||
return false;
|
||||
}
|
||||
|
||||
public TEntry AddDirectoryEntry(string key, DateTime? modified = null)
|
||||
{
|
||||
if (key.Length > 0 && key[0] is '/' or '\\')
|
||||
{
|
||||
key = key.Substring(1);
|
||||
}
|
||||
if (DoesKeyMatchExisting(key))
|
||||
{
|
||||
throw new ArchiveException("Cannot add entry with duplicate key: " + key);
|
||||
}
|
||||
var entry = CreateDirectoryEntry(key, modified);
|
||||
newEntries.Add(entry);
|
||||
RebuildModifiedCollection();
|
||||
return entry;
|
||||
}
|
||||
|
||||
public void SaveTo(Stream stream, WriterOptions options)
|
||||
{
|
||||
//reset streams of new entries
|
||||
@@ -180,6 +199,8 @@ public abstract class AbstractWritableArchive<TEntry, TVolume>
|
||||
bool closeStream
|
||||
);
|
||||
|
||||
protected abstract TEntry CreateDirectoryEntry(string key, DateTime? modified);
|
||||
|
||||
protected abstract void SaveTo(
|
||||
Stream stream,
|
||||
WriterOptions options,
|
||||
|
||||
@@ -185,6 +185,9 @@ public class GZipArchive : AbstractWritableArchive<GZipArchiveEntry, GZipVolume>
|
||||
return new GZipWritableArchiveEntry(this, source, filePath, size, modified, closeStream);
|
||||
}
|
||||
|
||||
protected override GZipArchiveEntry CreateDirectoryEntry(string directoryPath, DateTime? modified) =>
|
||||
throw new NotSupportedException("GZip archives do not support directory entries.");
|
||||
|
||||
protected override void SaveTo(
|
||||
Stream stream,
|
||||
WriterOptions options,
|
||||
|
||||
@@ -18,6 +18,8 @@ public interface IWritableArchive : IArchive
|
||||
DateTime? modified = null
|
||||
);
|
||||
|
||||
IArchiveEntry AddDirectoryEntry(string key, DateTime? modified = null);
|
||||
|
||||
void SaveTo(Stream stream, WriterOptions options);
|
||||
|
||||
Task SaveToAsync(
|
||||
|
||||
@@ -224,6 +224,9 @@ public class TarArchive : AbstractWritableArchive<TarArchiveEntry, TarVolume>
|
||||
closeStream
|
||||
);
|
||||
|
||||
protected override TarArchiveEntry CreateDirectoryEntry(string directoryPath, DateTime? modified) =>
|
||||
new TarWritableArchiveEntry(this, directoryPath, modified);
|
||||
|
||||
protected override void SaveTo(
|
||||
Stream stream,
|
||||
WriterOptions options,
|
||||
@@ -232,15 +235,25 @@ public class TarArchive : AbstractWritableArchive<TarArchiveEntry, TarVolume>
|
||||
)
|
||||
{
|
||||
using var writer = new TarWriter(stream, new TarWriterOptions(options));
|
||||
foreach (var entry in oldEntries.Concat(newEntries).Where(x => !x.IsDirectory))
|
||||
foreach (var entry in oldEntries.Concat(newEntries))
|
||||
{
|
||||
using var entryStream = entry.OpenEntryStream();
|
||||
writer.Write(
|
||||
entry.Key.NotNull("Entry Key is null"),
|
||||
entryStream,
|
||||
entry.LastModifiedTime,
|
||||
entry.Size
|
||||
);
|
||||
if (entry.IsDirectory)
|
||||
{
|
||||
writer.WriteDirectory(
|
||||
entry.Key.NotNull("Entry Key is null"),
|
||||
entry.LastModifiedTime
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
using var entryStream = entry.OpenEntryStream();
|
||||
writer.Write(
|
||||
entry.Key.NotNull("Entry Key is null"),
|
||||
entryStream,
|
||||
entry.LastModifiedTime,
|
||||
entry.Size
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,18 +266,31 @@ public class TarArchive : AbstractWritableArchive<TarArchiveEntry, TarVolume>
|
||||
)
|
||||
{
|
||||
using var writer = new TarWriter(stream, new TarWriterOptions(options));
|
||||
foreach (var entry in oldEntries.Concat(newEntries).Where(x => !x.IsDirectory))
|
||||
foreach (var entry in oldEntries.Concat(newEntries))
|
||||
{
|
||||
using var entryStream = entry.OpenEntryStream();
|
||||
await writer
|
||||
.WriteAsync(
|
||||
entry.Key.NotNull("Entry Key is null"),
|
||||
entryStream,
|
||||
entry.LastModifiedTime,
|
||||
entry.Size,
|
||||
cancellationToken
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
if (entry.IsDirectory)
|
||||
{
|
||||
await writer
|
||||
.WriteDirectoryAsync(
|
||||
entry.Key.NotNull("Entry Key is null"),
|
||||
entry.LastModifiedTime,
|
||||
cancellationToken
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
using var entryStream = entry.OpenEntryStream();
|
||||
await writer
|
||||
.WriteAsync(
|
||||
entry.Key.NotNull("Entry Key is null"),
|
||||
entryStream,
|
||||
entry.LastModifiedTime,
|
||||
entry.Size,
|
||||
cancellationToken
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,8 @@ namespace SharpCompress.Archives.Tar;
|
||||
internal sealed class TarWritableArchiveEntry : TarArchiveEntry, IWritableArchiveEntry
|
||||
{
|
||||
private readonly bool closeStream;
|
||||
private readonly Stream stream;
|
||||
private readonly Stream? stream;
|
||||
private readonly bool isDirectory;
|
||||
|
||||
internal TarWritableArchiveEntry(
|
||||
TarArchive archive,
|
||||
@@ -27,6 +28,22 @@ internal sealed class TarWritableArchiveEntry : TarArchiveEntry, IWritableArchiv
|
||||
Size = size;
|
||||
LastModifiedTime = lastModified;
|
||||
this.closeStream = closeStream;
|
||||
isDirectory = false;
|
||||
}
|
||||
|
||||
internal TarWritableArchiveEntry(
|
||||
TarArchive archive,
|
||||
string directoryPath,
|
||||
DateTime? lastModified
|
||||
)
|
||||
: base(archive, null, CompressionType.None)
|
||||
{
|
||||
stream = null;
|
||||
Key = directoryPath;
|
||||
Size = 0;
|
||||
LastModifiedTime = lastModified;
|
||||
closeStream = false;
|
||||
isDirectory = true;
|
||||
}
|
||||
|
||||
public override long Crc => 0;
|
||||
@@ -47,15 +64,19 @@ internal sealed class TarWritableArchiveEntry : TarArchiveEntry, IWritableArchiv
|
||||
|
||||
public override bool IsEncrypted => false;
|
||||
|
||||
public override bool IsDirectory => false;
|
||||
public override bool IsDirectory => isDirectory;
|
||||
|
||||
public override bool IsSplitAfter => false;
|
||||
|
||||
internal override IEnumerable<FilePart> Parts => throw new NotImplementedException();
|
||||
Stream IWritableArchiveEntry.Stream => stream;
|
||||
Stream IWritableArchiveEntry.Stream => stream ?? Stream.Null;
|
||||
|
||||
public override Stream OpenEntryStream()
|
||||
{
|
||||
if (stream is null)
|
||||
{
|
||||
return Stream.Null;
|
||||
}
|
||||
//ensure new stream is at the start, this could be reset
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
return SharpCompressStream.Create(stream, leaveOpen: true);
|
||||
@@ -63,7 +84,7 @@ internal sealed class TarWritableArchiveEntry : TarArchiveEntry, IWritableArchiv
|
||||
|
||||
internal override void Close()
|
||||
{
|
||||
if (closeStream)
|
||||
if (closeStream && stream is not null)
|
||||
{
|
||||
stream.Dispose();
|
||||
}
|
||||
|
||||
@@ -308,14 +308,24 @@ public class ZipArchive : AbstractWritableArchive<ZipArchiveEntry, ZipVolume>
|
||||
)
|
||||
{
|
||||
using var writer = new ZipWriter(stream, new ZipWriterOptions(options));
|
||||
foreach (var entry in oldEntries.Concat(newEntries).Where(x => !x.IsDirectory))
|
||||
foreach (var entry in oldEntries.Concat(newEntries))
|
||||
{
|
||||
using var entryStream = entry.OpenEntryStream();
|
||||
writer.Write(
|
||||
entry.Key.NotNull("Entry Key is null"),
|
||||
entryStream,
|
||||
entry.LastModifiedTime
|
||||
);
|
||||
if (entry.IsDirectory)
|
||||
{
|
||||
writer.WriteDirectory(
|
||||
entry.Key.NotNull("Entry Key is null"),
|
||||
entry.LastModifiedTime
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
using var entryStream = entry.OpenEntryStream();
|
||||
writer.Write(
|
||||
entry.Key.NotNull("Entry Key is null"),
|
||||
entryStream,
|
||||
entry.LastModifiedTime
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -328,12 +338,29 @@ public class ZipArchive : AbstractWritableArchive<ZipArchiveEntry, ZipVolume>
|
||||
)
|
||||
{
|
||||
using var writer = new ZipWriter(stream, new ZipWriterOptions(options));
|
||||
foreach (var entry in oldEntries.Concat(newEntries).Where(x => !x.IsDirectory))
|
||||
foreach (var entry in oldEntries.Concat(newEntries))
|
||||
{
|
||||
using var entryStream = entry.OpenEntryStream();
|
||||
await writer
|
||||
.WriteAsync(entry.Key.NotNull("Entry Key is null"), entryStream, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
if (entry.IsDirectory)
|
||||
{
|
||||
await writer
|
||||
.WriteDirectoryAsync(
|
||||
entry.Key.NotNull("Entry Key is null"),
|
||||
entry.LastModifiedTime,
|
||||
cancellationToken
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
using var entryStream = entry.OpenEntryStream();
|
||||
await writer
|
||||
.WriteAsync(
|
||||
entry.Key.NotNull("Entry Key is null"),
|
||||
entryStream,
|
||||
cancellationToken
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -345,6 +372,9 @@ public class ZipArchive : AbstractWritableArchive<ZipArchiveEntry, ZipVolume>
|
||||
bool closeStream
|
||||
) => new ZipWritableArchiveEntry(this, source, filePath, size, modified, closeStream);
|
||||
|
||||
protected override ZipArchiveEntry CreateDirectoryEntry(string directoryPath, DateTime? modified) =>
|
||||
new ZipWritableArchiveEntry(this, directoryPath, modified);
|
||||
|
||||
public static ZipArchive Create() => new();
|
||||
|
||||
protected override IReader CreateReaderForSolidExtraction()
|
||||
|
||||
@@ -9,7 +9,8 @@ namespace SharpCompress.Archives.Zip;
|
||||
internal class ZipWritableArchiveEntry : ZipArchiveEntry, IWritableArchiveEntry
|
||||
{
|
||||
private readonly bool closeStream;
|
||||
private readonly Stream stream;
|
||||
private readonly Stream? stream;
|
||||
private readonly bool isDirectory;
|
||||
private bool isDisposed;
|
||||
|
||||
internal ZipWritableArchiveEntry(
|
||||
@@ -27,6 +28,22 @@ internal class ZipWritableArchiveEntry : ZipArchiveEntry, IWritableArchiveEntry
|
||||
Size = size;
|
||||
LastModifiedTime = lastModified;
|
||||
this.closeStream = closeStream;
|
||||
isDirectory = false;
|
||||
}
|
||||
|
||||
internal ZipWritableArchiveEntry(
|
||||
ZipArchive archive,
|
||||
string directoryPath,
|
||||
DateTime? lastModified
|
||||
)
|
||||
: base(archive, null)
|
||||
{
|
||||
stream = null;
|
||||
Key = directoryPath;
|
||||
Size = 0;
|
||||
LastModifiedTime = lastModified;
|
||||
closeStream = false;
|
||||
isDirectory = true;
|
||||
}
|
||||
|
||||
public override long Crc => 0;
|
||||
@@ -47,16 +64,20 @@ internal class ZipWritableArchiveEntry : ZipArchiveEntry, IWritableArchiveEntry
|
||||
|
||||
public override bool IsEncrypted => false;
|
||||
|
||||
public override bool IsDirectory => false;
|
||||
public override bool IsDirectory => isDirectory;
|
||||
|
||||
public override bool IsSplitAfter => false;
|
||||
|
||||
internal override IEnumerable<FilePart> Parts => throw new NotImplementedException();
|
||||
|
||||
Stream IWritableArchiveEntry.Stream => stream;
|
||||
Stream IWritableArchiveEntry.Stream => stream ?? Stream.Null;
|
||||
|
||||
public override Stream OpenEntryStream()
|
||||
{
|
||||
if (stream is null)
|
||||
{
|
||||
return Stream.Null;
|
||||
}
|
||||
//ensure new stream is at the start, this could be reset
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
return SharpCompressStream.Create(stream, leaveOpen: true);
|
||||
@@ -64,7 +85,7 @@ internal class ZipWritableArchiveEntry : ZipArchiveEntry, IWritableArchiveEntry
|
||||
|
||||
internal override void Close()
|
||||
{
|
||||
if (closeStream && !isDisposed)
|
||||
if (closeStream && !isDisposed && stream is not null)
|
||||
{
|
||||
stream.Dispose();
|
||||
isDisposed = true;
|
||||
|
||||
@@ -37,6 +37,20 @@ public abstract class AbstractWriter(ArchiveType type, WriterOptions writerOptio
|
||||
await Task.CompletedTask.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public abstract void WriteDirectory(string directoryName, DateTime? modificationTime);
|
||||
|
||||
public virtual async Task WriteDirectoryAsync(
|
||||
string directoryName,
|
||||
DateTime? modificationTime,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
// Default implementation calls synchronous version
|
||||
// Derived classes should override for true async behavior
|
||||
WriteDirectory(directoryName, modificationTime);
|
||||
await Task.CompletedTask.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool isDisposing)
|
||||
{
|
||||
if (isDisposing)
|
||||
|
||||
@@ -50,4 +50,7 @@ public sealed class GZipWriter : AbstractWriter
|
||||
source.CopyTo(stream);
|
||||
_wroteToStream = true;
|
||||
}
|
||||
|
||||
public override void WriteDirectory(string directoryName, DateTime? modificationTime) =>
|
||||
throw new NotSupportedException("GZip archives do not support directory entries.");
|
||||
}
|
||||
|
||||
@@ -16,4 +16,10 @@ public interface IWriter : IDisposable
|
||||
DateTime? modificationTime,
|
||||
CancellationToken cancellationToken = default
|
||||
);
|
||||
void WriteDirectory(string directoryName, DateTime? modificationTime);
|
||||
Task WriteDirectoryAsync(
|
||||
string directoryName,
|
||||
DateTime? modificationTime,
|
||||
CancellationToken cancellationToken = default
|
||||
);
|
||||
}
|
||||
|
||||
@@ -55,6 +55,9 @@ public static class IWriterExtensions
|
||||
}
|
||||
}
|
||||
|
||||
public static void WriteDirectory(this IWriter writer, string directoryName) =>
|
||||
writer.WriteDirectory(directoryName, null);
|
||||
|
||||
// Async extensions
|
||||
public static Task WriteAsync(
|
||||
this IWriter writer,
|
||||
@@ -121,4 +124,10 @@ public static class IWriterExtensions
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public static Task WriteDirectoryAsync(
|
||||
this IWriter writer,
|
||||
string directoryName,
|
||||
CancellationToken cancellationToken = default
|
||||
) => writer.WriteDirectoryAsync(directoryName, null, cancellationToken);
|
||||
}
|
||||
|
||||
@@ -74,6 +74,44 @@ public class TarWriter : AbstractWriter
|
||||
return filename.Trim('/');
|
||||
}
|
||||
|
||||
private string NormalizeDirectoryName(string directoryName)
|
||||
{
|
||||
directoryName = NormalizeFilename(directoryName);
|
||||
// Ensure directory name ends with '/' for tar format
|
||||
if (!string.IsNullOrEmpty(directoryName) && !directoryName.EndsWith('/'))
|
||||
{
|
||||
directoryName += '/';
|
||||
}
|
||||
return directoryName;
|
||||
}
|
||||
|
||||
public override void WriteDirectory(string directoryName, DateTime? modificationTime)
|
||||
{
|
||||
var normalizedName = NormalizeDirectoryName(directoryName);
|
||||
if (string.IsNullOrEmpty(normalizedName))
|
||||
{
|
||||
return; // Skip empty or root directory
|
||||
}
|
||||
|
||||
var header = new TarHeader(WriterOptions.ArchiveEncoding);
|
||||
header.LastModifiedTime = modificationTime ?? TarHeader.EPOCH;
|
||||
header.Name = normalizedName;
|
||||
header.Size = 0;
|
||||
header.EntryType = EntryType.Directory;
|
||||
header.Write(OutputStream);
|
||||
}
|
||||
|
||||
public override async Task WriteDirectoryAsync(
|
||||
string directoryName,
|
||||
DateTime? modificationTime,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
// Synchronous implementation is sufficient for header-only write
|
||||
WriteDirectory(directoryName, modificationTime);
|
||||
await Task.CompletedTask.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public void Write(string filename, Stream source, DateTime? modificationTime, long? size)
|
||||
{
|
||||
if (!source.CanSeek && size is null)
|
||||
|
||||
@@ -3,6 +3,8 @@ using System.Buffers.Binary;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.Zip;
|
||||
using SharpCompress.Common.Zip.Headers;
|
||||
@@ -135,6 +137,73 @@ public class ZipWriter : AbstractWriter
|
||||
return filename.Trim('/');
|
||||
}
|
||||
|
||||
private string NormalizeDirectoryName(string directoryName)
|
||||
{
|
||||
directoryName = NormalizeFilename(directoryName);
|
||||
// Ensure directory name ends with '/' for zip format
|
||||
if (!string.IsNullOrEmpty(directoryName) && !directoryName.EndsWith('/'))
|
||||
{
|
||||
directoryName += '/';
|
||||
}
|
||||
return directoryName;
|
||||
}
|
||||
|
||||
public override void WriteDirectory(string directoryName, DateTime? modificationTime)
|
||||
{
|
||||
var normalizedName = NormalizeDirectoryName(directoryName);
|
||||
if (string.IsNullOrEmpty(normalizedName))
|
||||
{
|
||||
return; // Skip empty or root directory
|
||||
}
|
||||
|
||||
var options = new ZipWriterEntryOptions { ModificationDateTime = modificationTime };
|
||||
WriteDirectoryEntry(normalizedName, options);
|
||||
}
|
||||
|
||||
public override async Task WriteDirectoryAsync(
|
||||
string directoryName,
|
||||
DateTime? modificationTime,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
// Synchronous implementation is sufficient for directory entries
|
||||
WriteDirectory(directoryName, modificationTime);
|
||||
await Task.CompletedTask.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private void WriteDirectoryEntry(string directoryPath, ZipWriterEntryOptions options)
|
||||
{
|
||||
var compression = ZipCompressionMethod.None;
|
||||
|
||||
options.ModificationDateTime ??= DateTime.Now;
|
||||
options.EntryComment ??= string.Empty;
|
||||
|
||||
var entry = new ZipCentralDirectoryEntry(
|
||||
compression,
|
||||
directoryPath,
|
||||
(ulong)streamPosition,
|
||||
WriterOptions.ArchiveEncoding
|
||||
)
|
||||
{
|
||||
Comment = options.EntryComment,
|
||||
ModificationTime = options.ModificationDateTime,
|
||||
Crc = 0,
|
||||
Compressed = 0,
|
||||
Decompressed = 0,
|
||||
};
|
||||
|
||||
// Use the archive default setting for zip64 and allow overrides
|
||||
var useZip64 = isZip64;
|
||||
if (options.EnableZip64.HasValue)
|
||||
{
|
||||
useZip64 = options.EnableZip64.Value;
|
||||
}
|
||||
|
||||
var headersize = (uint)WriteHeader(directoryPath, options, entry, useZip64);
|
||||
streamPosition += headersize;
|
||||
entries.Add(entry);
|
||||
}
|
||||
|
||||
private int WriteHeader(
|
||||
string filename,
|
||||
ZipWriterEntryOptions zipWriterEntryOptions,
|
||||
|
||||
18
tests/SharpCompress.Test/GZip/GZipArchiveDirectoryTests.cs
Normal file
18
tests/SharpCompress.Test/GZip/GZipArchiveDirectoryTests.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SharpCompress.Archives.GZip;
|
||||
using Xunit;
|
||||
|
||||
namespace SharpCompress.Test.GZip;
|
||||
|
||||
public class GZipArchiveDirectoryTests : TestBase
|
||||
{
|
||||
[Fact]
|
||||
public void GZipArchive_AddDirectoryEntry_ThrowsNotSupportedException()
|
||||
{
|
||||
using var archive = GZipArchive.Create();
|
||||
|
||||
Assert.Throws<NotSupportedException>(() =>
|
||||
archive.AddDirectoryEntry("test-dir", DateTime.Now));
|
||||
}
|
||||
}
|
||||
20
tests/SharpCompress.Test/GZip/GZipWriterDirectoryTests.cs
Normal file
20
tests/SharpCompress.Test/GZip/GZipWriterDirectoryTests.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Writers.GZip;
|
||||
using Xunit;
|
||||
|
||||
namespace SharpCompress.Test.GZip;
|
||||
|
||||
public class GZipWriterDirectoryTests : TestBase
|
||||
{
|
||||
[Fact]
|
||||
public void GZipWriter_WriteDirectory_ThrowsNotSupportedException()
|
||||
{
|
||||
using var memoryStream = new MemoryStream();
|
||||
using var writer = new GZipWriter(memoryStream, new GZipWriterOptions());
|
||||
|
||||
Assert.Throws<NotSupportedException>(() =>
|
||||
writer.WriteDirectory("test-dir", DateTime.Now));
|
||||
}
|
||||
}
|
||||
103
tests/SharpCompress.Test/Tar/TarArchiveDirectoryTests.cs
Normal file
103
tests/SharpCompress.Test/Tar/TarArchiveDirectoryTests.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SharpCompress.Archives.Tar;
|
||||
using SharpCompress.Common;
|
||||
using Xunit;
|
||||
|
||||
namespace SharpCompress.Test.Tar;
|
||||
|
||||
public class TarArchiveDirectoryTests : TestBase
|
||||
{
|
||||
[Fact]
|
||||
public void TarArchive_AddDirectoryEntry_CreatesDirectoryEntry()
|
||||
{
|
||||
using var archive = TarArchive.Create();
|
||||
|
||||
archive.AddDirectoryEntry("test-dir", DateTime.Now);
|
||||
|
||||
var entries = archive.Entries.ToList();
|
||||
Assert.Single(entries);
|
||||
Assert.Equal("test-dir", entries[0].Key);
|
||||
Assert.True(entries[0].IsDirectory);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TarArchive_AddDirectoryEntry_MultipleDirectories()
|
||||
{
|
||||
using var archive = TarArchive.Create();
|
||||
|
||||
archive.AddDirectoryEntry("dir1", DateTime.Now);
|
||||
archive.AddDirectoryEntry("dir2", DateTime.Now);
|
||||
archive.AddDirectoryEntry("dir1/subdir", DateTime.Now);
|
||||
|
||||
var entries = archive.Entries.OrderBy(e => e.Key).ToList();
|
||||
Assert.Equal(3, entries.Count);
|
||||
Assert.True(entries.All(e => e.IsDirectory));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TarArchive_AddDirectoryEntry_MixedWithFiles()
|
||||
{
|
||||
using var archive = TarArchive.Create();
|
||||
|
||||
archive.AddDirectoryEntry("dir1", DateTime.Now);
|
||||
|
||||
using var contentStream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes("test content"));
|
||||
archive.AddEntry("dir1/file.txt", contentStream, false, contentStream.Length, DateTime.Now);
|
||||
|
||||
archive.AddDirectoryEntry("dir2", DateTime.Now);
|
||||
|
||||
var entries = archive.Entries.OrderBy(e => e.Key).ToList();
|
||||
Assert.Equal(3, entries.Count);
|
||||
Assert.True(entries[0].IsDirectory);
|
||||
Assert.False(entries[1].IsDirectory);
|
||||
Assert.True(entries[2].IsDirectory);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TarArchive_AddDirectoryEntry_SaveAndReload()
|
||||
{
|
||||
var scratchPath = Path.Combine(SCRATCH_FILES_PATH, "tar-directory-test.tar");
|
||||
|
||||
using (var archive = TarArchive.Create())
|
||||
{
|
||||
archive.AddDirectoryEntry("dir1", DateTime.Now);
|
||||
archive.AddDirectoryEntry("dir2", DateTime.Now);
|
||||
|
||||
using var contentStream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes("test content"));
|
||||
archive.AddEntry("dir1/file.txt", contentStream, false, contentStream.Length, DateTime.Now);
|
||||
|
||||
using (var fileStream = File.Create(scratchPath))
|
||||
{
|
||||
archive.SaveTo(fileStream, CompressionType.None);
|
||||
}
|
||||
}
|
||||
|
||||
using (var archive = TarArchive.Open(scratchPath))
|
||||
{
|
||||
var entries = archive.Entries.OrderBy(e => e.Key).ToList();
|
||||
Assert.Equal(3, entries.Count);
|
||||
|
||||
Assert.Equal("dir1/", entries[0].Key);
|
||||
Assert.True(entries[0].IsDirectory);
|
||||
|
||||
Assert.Equal("dir1/file.txt", entries[1].Key);
|
||||
Assert.False(entries[1].IsDirectory);
|
||||
|
||||
Assert.Equal("dir2/", entries[2].Key);
|
||||
Assert.True(entries[2].IsDirectory);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TarArchive_AddDirectoryEntry_DuplicateKey_ThrowsException()
|
||||
{
|
||||
using var archive = TarArchive.Create();
|
||||
|
||||
archive.AddDirectoryEntry("test-dir", DateTime.Now);
|
||||
|
||||
Assert.Throws<ArchiveException>(() =>
|
||||
archive.AddDirectoryEntry("test-dir", DateTime.Now));
|
||||
}
|
||||
}
|
||||
132
tests/SharpCompress.Test/Tar/TarWriterDirectoryTests.cs
Normal file
132
tests/SharpCompress.Test/Tar/TarWriterDirectoryTests.cs
Normal file
@@ -0,0 +1,132 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SharpCompress.Archives.Tar;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Writers.Tar;
|
||||
using Xunit;
|
||||
|
||||
namespace SharpCompress.Test.Tar;
|
||||
|
||||
public class TarWriterDirectoryTests : TestBase
|
||||
{
|
||||
[Fact]
|
||||
public void TarWriter_WriteDirectory_CreatesDirectoryEntry()
|
||||
{
|
||||
using var memoryStream = new MemoryStream();
|
||||
using (var writer = new TarWriter(memoryStream, new TarWriterOptions(CompressionType.None, true)))
|
||||
{
|
||||
writer.WriteDirectory("test-dir", DateTime.Now);
|
||||
}
|
||||
|
||||
memoryStream.Position = 0;
|
||||
using var archive = TarArchive.Open(memoryStream);
|
||||
var entries = archive.Entries.ToList();
|
||||
|
||||
Assert.Single(entries);
|
||||
Assert.Equal("test-dir/", entries[0].Key);
|
||||
Assert.True(entries[0].IsDirectory);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TarWriter_WriteDirectory_WithTrailingSlash()
|
||||
{
|
||||
using var memoryStream = new MemoryStream();
|
||||
using (var writer = new TarWriter(memoryStream, new TarWriterOptions(CompressionType.None, true)))
|
||||
{
|
||||
writer.WriteDirectory("test-dir/", DateTime.Now);
|
||||
}
|
||||
|
||||
memoryStream.Position = 0;
|
||||
using var archive = TarArchive.Open(memoryStream);
|
||||
var entries = archive.Entries.ToList();
|
||||
|
||||
Assert.Single(entries);
|
||||
Assert.Equal("test-dir/", entries[0].Key);
|
||||
Assert.True(entries[0].IsDirectory);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TarWriter_WriteDirectory_WithBackslash()
|
||||
{
|
||||
using var memoryStream = new MemoryStream();
|
||||
using (var writer = new TarWriter(memoryStream, new TarWriterOptions(CompressionType.None, true)))
|
||||
{
|
||||
writer.WriteDirectory("test-dir\\subdir", DateTime.Now);
|
||||
}
|
||||
|
||||
memoryStream.Position = 0;
|
||||
using var archive = TarArchive.Open(memoryStream);
|
||||
var entries = archive.Entries.ToList();
|
||||
|
||||
Assert.Single(entries);
|
||||
Assert.Equal("test-dir/subdir/", entries[0].Key);
|
||||
Assert.True(entries[0].IsDirectory);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TarWriter_WriteDirectory_EmptyString_IsSkipped()
|
||||
{
|
||||
using var memoryStream = new MemoryStream();
|
||||
using (var writer = new TarWriter(memoryStream, new TarWriterOptions(CompressionType.None, true)))
|
||||
{
|
||||
writer.WriteDirectory("", DateTime.Now);
|
||||
}
|
||||
|
||||
memoryStream.Position = 0;
|
||||
using var archive = TarArchive.Open(memoryStream);
|
||||
|
||||
Assert.Empty(archive.Entries);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TarWriter_WriteDirectory_MultipleDirectories()
|
||||
{
|
||||
using var memoryStream = new MemoryStream();
|
||||
using (var writer = new TarWriter(memoryStream, new TarWriterOptions(CompressionType.None, true)))
|
||||
{
|
||||
writer.WriteDirectory("dir1", DateTime.Now);
|
||||
writer.WriteDirectory("dir2", DateTime.Now);
|
||||
writer.WriteDirectory("dir1/subdir", DateTime.Now);
|
||||
}
|
||||
|
||||
memoryStream.Position = 0;
|
||||
using var archive = TarArchive.Open(memoryStream);
|
||||
var entries = archive.Entries.OrderBy(e => e.Key).ToList();
|
||||
|
||||
Assert.Equal(3, entries.Count);
|
||||
Assert.Equal("dir1/", entries[0].Key);
|
||||
Assert.True(entries[0].IsDirectory);
|
||||
Assert.Equal("dir1/subdir/", entries[1].Key);
|
||||
Assert.True(entries[1].IsDirectory);
|
||||
Assert.Equal("dir2/", entries[2].Key);
|
||||
Assert.True(entries[2].IsDirectory);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TarWriter_WriteDirectory_MixedWithFiles()
|
||||
{
|
||||
using var memoryStream = new MemoryStream();
|
||||
using (var writer = new TarWriter(memoryStream, new TarWriterOptions(CompressionType.None, true)))
|
||||
{
|
||||
writer.WriteDirectory("dir1", DateTime.Now);
|
||||
|
||||
using var contentStream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes("test content"));
|
||||
writer.Write("dir1/file.txt", contentStream, DateTime.Now);
|
||||
|
||||
writer.WriteDirectory("dir2", DateTime.Now);
|
||||
}
|
||||
|
||||
memoryStream.Position = 0;
|
||||
using var archive = TarArchive.Open(memoryStream);
|
||||
var entries = archive.Entries.OrderBy(e => e.Key).ToList();
|
||||
|
||||
Assert.Equal(3, entries.Count);
|
||||
Assert.Equal("dir1/", entries[0].Key);
|
||||
Assert.True(entries[0].IsDirectory);
|
||||
Assert.Equal("dir1/file.txt", entries[1].Key);
|
||||
Assert.False(entries[1].IsDirectory);
|
||||
Assert.Equal("dir2/", entries[2].Key);
|
||||
Assert.True(entries[2].IsDirectory);
|
||||
}
|
||||
}
|
||||
103
tests/SharpCompress.Test/Zip/ZipArchiveDirectoryTests.cs
Normal file
103
tests/SharpCompress.Test/Zip/ZipArchiveDirectoryTests.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SharpCompress.Archives.Zip;
|
||||
using SharpCompress.Common;
|
||||
using Xunit;
|
||||
|
||||
namespace SharpCompress.Test.Zip;
|
||||
|
||||
public class ZipArchiveDirectoryTests : TestBase
|
||||
{
|
||||
[Fact]
|
||||
public void ZipArchive_AddDirectoryEntry_CreatesDirectoryEntry()
|
||||
{
|
||||
using var archive = ZipArchive.Create();
|
||||
|
||||
archive.AddDirectoryEntry("test-dir", DateTime.Now);
|
||||
|
||||
var entries = archive.Entries.ToList();
|
||||
Assert.Single(entries);
|
||||
Assert.Equal("test-dir", entries[0].Key);
|
||||
Assert.True(entries[0].IsDirectory);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ZipArchive_AddDirectoryEntry_MultipleDirectories()
|
||||
{
|
||||
using var archive = ZipArchive.Create();
|
||||
|
||||
archive.AddDirectoryEntry("dir1", DateTime.Now);
|
||||
archive.AddDirectoryEntry("dir2", DateTime.Now);
|
||||
archive.AddDirectoryEntry("dir1/subdir", DateTime.Now);
|
||||
|
||||
var entries = archive.Entries.OrderBy(e => e.Key).ToList();
|
||||
Assert.Equal(3, entries.Count);
|
||||
Assert.True(entries.All(e => e.IsDirectory));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ZipArchive_AddDirectoryEntry_MixedWithFiles()
|
||||
{
|
||||
using var archive = ZipArchive.Create();
|
||||
|
||||
archive.AddDirectoryEntry("dir1", DateTime.Now);
|
||||
|
||||
using var contentStream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes("test content"));
|
||||
archive.AddEntry("dir1/file.txt", contentStream, false, contentStream.Length, DateTime.Now);
|
||||
|
||||
archive.AddDirectoryEntry("dir2", DateTime.Now);
|
||||
|
||||
var entries = archive.Entries.OrderBy(e => e.Key).ToList();
|
||||
Assert.Equal(3, entries.Count);
|
||||
Assert.True(entries[0].IsDirectory);
|
||||
Assert.False(entries[1].IsDirectory);
|
||||
Assert.True(entries[2].IsDirectory);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ZipArchive_AddDirectoryEntry_SaveAndReload()
|
||||
{
|
||||
var scratchPath = Path.Combine(SCRATCH_FILES_PATH, "zip-directory-test.zip");
|
||||
|
||||
using (var archive = ZipArchive.Create())
|
||||
{
|
||||
archive.AddDirectoryEntry("dir1", DateTime.Now);
|
||||
archive.AddDirectoryEntry("dir2", DateTime.Now);
|
||||
|
||||
using var contentStream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes("test content"));
|
||||
archive.AddEntry("dir1/file.txt", contentStream, false, contentStream.Length, DateTime.Now);
|
||||
|
||||
using (var fileStream = File.Create(scratchPath))
|
||||
{
|
||||
archive.SaveTo(fileStream, CompressionType.Deflate);
|
||||
}
|
||||
}
|
||||
|
||||
using (var archive = ZipArchive.Open(scratchPath))
|
||||
{
|
||||
var entries = archive.Entries.OrderBy(e => e.Key).ToList();
|
||||
Assert.Equal(3, entries.Count);
|
||||
|
||||
Assert.Equal("dir1/", entries[0].Key);
|
||||
Assert.True(entries[0].IsDirectory);
|
||||
|
||||
Assert.Equal("dir1/file.txt", entries[1].Key);
|
||||
Assert.False(entries[1].IsDirectory);
|
||||
|
||||
Assert.Equal("dir2/", entries[2].Key);
|
||||
Assert.True(entries[2].IsDirectory);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ZipArchive_AddDirectoryEntry_DuplicateKey_ThrowsException()
|
||||
{
|
||||
using var archive = ZipArchive.Create();
|
||||
|
||||
archive.AddDirectoryEntry("test-dir", DateTime.Now);
|
||||
|
||||
Assert.Throws<ArchiveException>(() =>
|
||||
archive.AddDirectoryEntry("test-dir", DateTime.Now));
|
||||
}
|
||||
}
|
||||
132
tests/SharpCompress.Test/Zip/ZipWriterDirectoryTests.cs
Normal file
132
tests/SharpCompress.Test/Zip/ZipWriterDirectoryTests.cs
Normal file
@@ -0,0 +1,132 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SharpCompress.Archives.Zip;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Writers.Zip;
|
||||
using Xunit;
|
||||
|
||||
namespace SharpCompress.Test.Zip;
|
||||
|
||||
public class ZipWriterDirectoryTests : TestBase
|
||||
{
|
||||
[Fact]
|
||||
public void ZipWriter_WriteDirectory_CreatesDirectoryEntry()
|
||||
{
|
||||
using var memoryStream = new MemoryStream();
|
||||
using (var writer = new ZipWriter(memoryStream, new ZipWriterOptions(CompressionType.Deflate)))
|
||||
{
|
||||
writer.WriteDirectory("test-dir", DateTime.Now);
|
||||
}
|
||||
|
||||
memoryStream.Position = 0;
|
||||
using var archive = ZipArchive.Open(memoryStream);
|
||||
var entries = archive.Entries.ToList();
|
||||
|
||||
Assert.Single(entries);
|
||||
Assert.Equal("test-dir/", entries[0].Key);
|
||||
Assert.True(entries[0].IsDirectory);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ZipWriter_WriteDirectory_WithTrailingSlash()
|
||||
{
|
||||
using var memoryStream = new MemoryStream();
|
||||
using (var writer = new ZipWriter(memoryStream, new ZipWriterOptions(CompressionType.Deflate)))
|
||||
{
|
||||
writer.WriteDirectory("test-dir/", DateTime.Now);
|
||||
}
|
||||
|
||||
memoryStream.Position = 0;
|
||||
using var archive = ZipArchive.Open(memoryStream);
|
||||
var entries = archive.Entries.ToList();
|
||||
|
||||
Assert.Single(entries);
|
||||
Assert.Equal("test-dir/", entries[0].Key);
|
||||
Assert.True(entries[0].IsDirectory);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ZipWriter_WriteDirectory_WithBackslash()
|
||||
{
|
||||
using var memoryStream = new MemoryStream();
|
||||
using (var writer = new ZipWriter(memoryStream, new ZipWriterOptions(CompressionType.Deflate)))
|
||||
{
|
||||
writer.WriteDirectory("test-dir\\subdir", DateTime.Now);
|
||||
}
|
||||
|
||||
memoryStream.Position = 0;
|
||||
using var archive = ZipArchive.Open(memoryStream);
|
||||
var entries = archive.Entries.ToList();
|
||||
|
||||
Assert.Single(entries);
|
||||
Assert.Equal("test-dir/subdir/", entries[0].Key);
|
||||
Assert.True(entries[0].IsDirectory);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ZipWriter_WriteDirectory_EmptyString_IsSkipped()
|
||||
{
|
||||
using var memoryStream = new MemoryStream();
|
||||
using (var writer = new ZipWriter(memoryStream, new ZipWriterOptions(CompressionType.Deflate)))
|
||||
{
|
||||
writer.WriteDirectory("", DateTime.Now);
|
||||
}
|
||||
|
||||
memoryStream.Position = 0;
|
||||
using var archive = ZipArchive.Open(memoryStream);
|
||||
|
||||
Assert.Empty(archive.Entries);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ZipWriter_WriteDirectory_MultipleDirectories()
|
||||
{
|
||||
using var memoryStream = new MemoryStream();
|
||||
using (var writer = new ZipWriter(memoryStream, new ZipWriterOptions(CompressionType.Deflate)))
|
||||
{
|
||||
writer.WriteDirectory("dir1", DateTime.Now);
|
||||
writer.WriteDirectory("dir2", DateTime.Now);
|
||||
writer.WriteDirectory("dir1/subdir", DateTime.Now);
|
||||
}
|
||||
|
||||
memoryStream.Position = 0;
|
||||
using var archive = ZipArchive.Open(memoryStream);
|
||||
var entries = archive.Entries.OrderBy(e => e.Key).ToList();
|
||||
|
||||
Assert.Equal(3, entries.Count);
|
||||
Assert.Equal("dir1/", entries[0].Key);
|
||||
Assert.True(entries[0].IsDirectory);
|
||||
Assert.Equal("dir1/subdir/", entries[1].Key);
|
||||
Assert.True(entries[1].IsDirectory);
|
||||
Assert.Equal("dir2/", entries[2].Key);
|
||||
Assert.True(entries[2].IsDirectory);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ZipWriter_WriteDirectory_MixedWithFiles()
|
||||
{
|
||||
using var memoryStream = new MemoryStream();
|
||||
using (var writer = new ZipWriter(memoryStream, new ZipWriterOptions(CompressionType.Deflate)))
|
||||
{
|
||||
writer.WriteDirectory("dir1", DateTime.Now);
|
||||
|
||||
using var contentStream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes("test content"));
|
||||
writer.Write("dir1/file.txt", contentStream, DateTime.Now);
|
||||
|
||||
writer.WriteDirectory("dir2", DateTime.Now);
|
||||
}
|
||||
|
||||
memoryStream.Position = 0;
|
||||
using var archive = ZipArchive.Open(memoryStream);
|
||||
var entries = archive.Entries.OrderBy(e => e.Key).ToList();
|
||||
|
||||
Assert.Equal(3, entries.Count);
|
||||
Assert.Equal("dir1/", entries[0].Key);
|
||||
Assert.True(entries[0].IsDirectory);
|
||||
Assert.Equal("dir1/file.txt", entries[1].Key);
|
||||
Assert.False(entries[1].IsDirectory);
|
||||
Assert.Equal("dir2/", entries[2].Key);
|
||||
Assert.True(entries[2].IsDirectory);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user