Skip to content

Mock System.IO.Compression.ZipFile #396

Closed

Description

@samds

Hello guys,

For one of my project I started to write a basic wrapper around the methods of System.IO.Compression.ZipFile. To achieve that I'm of course using System.IO.Abstractions.

I started to introduce 4 new types IZipFileFactory, ZipFileFactory, MockZipFileFactory, MockZipFileData. Now I would like to have a property ZipFile of type IZipFileFactory in IFileSystem so I can write something like

var fileSystem = new FileSystem();
fileSystem.ZipFile.CreateFromDirectory(sourceDirectoryName, destinationArchiveFileName);
...
fileSystem.ZipFile.ExtractToDirectory(destinationArchiveFileName, destinationDirectoryName);

Would you like to see a pull request for that feature?

Thanks

Activity

  1. herebebeasties commented on Nov 5, 2018

    @herebebeasties
    Contributor

    We did something like this at the company I work for (will hopefully open source some of this stuff soon).

    It's actually quite nice to be able to take a ZipArchive on one IFileSystem and extract it to another (e.g. take a real ZIP on disk and populate a MockFileSystem with it), so we implemented this as extension methods on FileBase and ZipArchive:

    public static ZipArchive CreateZip(this FileBase file, string path);
    public static void ExtractToDirectory(this ZipArchive zipArchive, IFileSystem destFs, string destPath);

    etc.
    Note that ZipArchive already works against the Stream APIs instead of files directly, so this is all pretty easy.

    The advantage of that is that it works against any IFileSystem implementation (and across them), and that you don't need to implement this stuff in the IFileSystem implementations themselves.

  2. fgreinacher commented on Jan 5, 2019

    @fgreinacher
    Contributor

    I like the simple, extension-method based approach.

    @samds Is there any major advantage in your proposal?

  3. samds commented on Jan 12, 2019

    @samds
    Author

    @fgreinacher Well, at the time I started to write this it looked like the more natural way to do it for me. I have to give @herebebeasties' solution a try.

  4. ericnewton76 commented on Jan 16, 2019

    @ericnewton76
    Contributor

    Might want to consider not locking on Zip files only, and consider a more generic ICompressorStream and IVirtualFileSystem, a virtual filesystem supplied by a zip file, or vhd or similar.

    I've had thoughts of doing VHD manipulation somehow using System.IO.Abstractions since VHD and ZIP files basically represent virtual filesystems with varying degrees of functionality.

    Your extension methods might make an excellent candidate for a SysIOAbstractions Contrib project or SysIOVirtualFileSystems

    The only reason I think it'll be easier to be in a separate library is due to the widely varying platform support of various codecs and compression algos at that point.

  5. added
    area: coreIssues that address the core abstractions & the wrappers
    on Mar 24, 2019
  6. fgreinacher commented on Apr 19, 2019

    @fgreinacher
    Contributor

    Closing this. If you want to get a separate project in the SIOA org feel free to ping me here!

  7. EricEzaM commented on Sep 13, 2022

    @EricEzaM

    @herebebeasties Any updates on open-sourcing that implementation? I am looking to make my usages of ZipArchive testable but am not sure how to start on the implementation you hinted at here. Would really appreciate if you could point me in the right direction to get me going.

    I can see that FileBase has access to the underlying IFileSystem which can help you write these extension methods, but what about ExtractToDirectory(this ZipArchive zipArchive, string path); on ZipArchive? How do you get the IFileSystem in there?

  8. herebebeasties commented on Sep 13, 2022

    @herebebeasties
    Contributor

    I've since left the company where I did this, but I can perhaps find some time to get you going if you can wait until the weekend.

  9. EricEzaM commented on Sep 13, 2022

    @EricEzaM

    That would be good, thanks. Most basic question from would be how do you get IFileSystem in the ZipArchive extension method?

  10. herebebeasties commented on Sep 13, 2022

    @herebebeasties
    Contributor

    Well it's an extension method so just something like:

    public static class ZipExtensions
    {
        public static void Unzip(this IFileSystem source, string pathToZip, IFileSystem dest, string pathToUnzipTo)
        {
            //...
        } 
    } 
    
  11. EricEzaM commented on Sep 13, 2022

    @EricEzaM

    Ah, right, thanks. I suppose I was taking your sample code below a little to literally then, with it being an ext method on ZipArchive?

    public static void ExtractToDirectory(this ZipArchive zipArchive, string path);
  12. herebebeasties commented on Sep 13, 2022

    @herebebeasties
    Contributor

    Or that, with a destination IFileSystem also passed in. Sorry, that was missing in my original, have updated. Anyway, lots of ways to skin this particular cat!

  13. greg0rym commented on Oct 21, 2022

    @greg0rym

    Forgive me but I am trying to understand the reason for not implementing coverage of ZipFile in System.IO.Abstractions. If we are to use an extension method, doesn't that mean everyone who needs an interface on ZipFile will need to write an extension, where as if it was in the project in could be reused?

    (If you use an extension method, how do you mock it?)

  14. vbreuss commented on Oct 27, 2022

    @vbreuss
    Member

    @greg0rym :
    I created a library that abstracts the ZipFile methods, so that they can be abstracted via the underlying IFileSystem interface:

    Testably.Abstractions.Compression
    Nuget

    In the corresponding unit tests ZipFileTests.cs you can see, how such tests could look...

    Even if the interfaces are not 100% compatible, you could either give my project a try or use the extension methods as a basis for implementing something similar for System.IO.Abstractions...

  15. sakopov commented on Feb 3, 2023

    @sakopov

    The easiest way to get around this issue is not to use ZipFile at all. It's a wrapper which wasn't designed to be unit tested at all. Instead, use what it wraps internally:

    var _fileSystem = new FileSystem(); // Usually injected in your class
    
    using var archiveStream = _fileSystem.FileStream.New("/path/to/file.zip", FileMode.CreateNew, FileAccess.Write, FileShare.None, bufferSize: 0x1000, useAsync: false);
    using var archive = new ZipArchive(archiveStream, ZipArchiveMode.Create, leaveOpen: false);
    
    var archiveEntry = archive.CreateEntry("file.txt", CompressionLevel.SmallestSize);
    
    using var archiveEntryStream = archiveEntry.Open();
    using var archiveFileStream = _fileSystem.FileStream.New("/path/to/file.txt", FileMode.Open);
    
    archiveFileStream.CopyTo(archiveEntryStream);

    Then just use MockFileSystem to make your assertions.

    var fileSystemMock = new MockFileSystem();
    
    fileSystemMock.FileExists("path/to/file.zip").Should().BeTrue();
  16. cdMOE commented on Jan 24, 2025

    @cdMOE

    public static void ExtractToDirectory(this ZipArchive zipArchive, IFileSystem destFs, string destPath);

    I thought I'd comment this sample implementation in case it is helpful:

    public static void ExtractToDirectory(this ZipArchive zipArchive, IFileSystem destFs, string destPath)
        {
            ArgumentNullException.ThrowIfNull(zipArchive);
            ArgumentNullException.ThrowIfNull(destFs);
            if (string.IsNullOrEmpty(destPath)) throw new ArgumentNullException(nameof(destPath));
    
            foreach (var entry in zipArchive.Entries)
            {
                var entryPath = destFs.Path.Combine(destPath, entry.FullName);
    
                if (string.IsNullOrEmpty(entry.Name))
                {
                    destFs.Directory.CreateDirectory(entryPath);
                    continue;
                }
    
                var directoryPath = destFs.Path.GetDirectoryName(entryPath);
                destFs.Directory.CreateDirectory(directoryPath);
    
                using var entryStream = entry.Open();
                using var outputStream = destFs.File.Create(entryPath);
                entryStream.CopyTo(outputStream);
            }
        }
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: coreIssues that address the core abstractions & the wrappersstate: needs discussionIssues that need further discussiontype: enhancementIssues that propose new functionality

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions