5

Currently what I am doing:

  1. Testing function by letting it zip a file/directory. Assert it exists.
  2. Using :zip.t and :zip.tt let it list down the contents of the zip folder to see if it's what I am expecting.

Somehow I think I am missing something. Is it better to test with :zip.table ? The function looks confusing. Can someone provide an example of how to use it ? Below is an example of the output I got to, but I can't figure out how to make this into a test ? Is md5sum a better test for zip archives ?

iex(4)> :zip.table('testing.zip')
{:ok,
 [{:zip_comment, []},
  {:zip_file, 'mix.exs',
   {:file_info, 930, :regular, :read_write, {{2015, 7, 15}, {2, 11, 9}},
    {{2015, 7, 15}, {2, 11, 9}}, {{2015, 7, 15}, {2, 11, 9}}, 54, 1, 0, 0, 0, 0,
    0}, [], 0, 444},
  {:zip_file, 'mix.lock',
   {:file_info, 332, :regular, :read_write, {{2015, 7, 15}, {2, 9, 6}},
    {{2015, 7, 15}, {2, 9, 6}}, {{2015, 7, 15}, {2, 9, 6}}, 54, 1, 0, 0, 0, 0,
    0}, [], 481, 152}]}
Patrick Oscity
  • 53,604
  • 17
  • 144
  • 168
Muhammad Lukman Low
  • 8,177
  • 11
  • 44
  • 54
  • 1
    The first Google result for "erlang zip" reveals [the zip module from Erlang](http://www.erlang.org/doc/man/zip.html). You should show at least _some_ effort when asking a question here. Try to get started on your own, then come back with specific questions when you run into a problem. – Patrick Oscity Jul 14 '15 at 18:55
  • Sorry for not being clear or getting to what I really want. I have clarified the question. – Muhammad Lukman Low Jul 14 '15 at 19:19
  • 1
    So you want to write a test that asserts the contents of a zip file? Do you want to assert only the file names or the contents of the zipped files as well? – Patrick Oscity Jul 14 '15 at 19:45
  • Preferably everything, from the files / folders in the zip to their integrity. – Muhammad Lukman Low Jul 14 '15 at 19:50
  • I've added an answer below but TL;DR I think building unit tests around zipping and unzipping files is basically a waste of time. You're basically testing the Erlang zip library. – Onorio Catenacci Jul 16 '15 at 13:30

2 Answers2

8

The :zip module from Erlang is not really easy to work with, I'll try to break it down for you.

First, we will need an appropriate representation of the zip_file record in order from Erlang to be able to properly work with it. Otherwise, we would have to do matches on tuples with a lot of elements which would just clutter our code unnecessarily. The following module is heavily based on the File.Stat implementation from Elixir and will allow us to access the values in those unwieldy tuples with simple dot notation.

require Record

defmodule Zip.File do
  record = Record.extract(:zip_file, from_lib: "stdlib/include/zip.hrl")
  keys   = :lists.map(&elem(&1, 0), record)
  vals   = :lists.map(&{&1, [], nil}, keys)
  pairs  = :lists.zip(keys, vals)

  defstruct keys

  def to_record(%Zip.File{unquote_splicing(pairs)}) do
    {:zip_file, unquote_splicing(vals)}
  end

  def from_record(zip_file)
  def from_record({:zip_file, unquote_splicing(vals)}) do
    %Zip.File{unquote_splicing(pairs)}
    |> Map.update!(:info, fn(info) -> File.Stat.from_record(info) end)
  end
end

We can now build a small wrapper class around the Erlang zip module. It does not wrap all methods, just the ones we'll use here. I also added a list_files/1 function that only returns files, excluding directories and comments from the listing.

defmodule Zip do
  def open(archive) do
    {:ok, zip_handle} = :zip.zip_open(archive)
    zip_handle
  end

  def close(zip_handle) do
    :zip.zip_close(zip_handle)
  end

  def list_dir(zip_handle) do
    {:ok, result} = :zip.zip_list_dir(zip_handle)
    result
  end

  def list_files(zip_handle) do
    list_dir(zip_handle)
    |> Enum.drop(1)
    |> Enum.map(&Zip.File.from_record/1)
    |> Enum.filter(&(&1.info.type == :regular))
  end
end

Suppose we have the following zip archive for testing:

cd /tmp
touch foo bar baz
zip archive.zip foo bar baz

Now you can assert the file names inside the zip archive:

test "files are in zip" do
  zip = Zip.open('/tmp/archive.zip')
  files = Zip.list_files(zip) |> Enum.map(&(&1.name))
  Zip.close(zip)
  assert files == ['foo', 'bar', 'baz']
end

I'll leave further operations and assertions on the zip archive for you to implement, and hope this gets you started.

Onorio Catenacci
  • 14,928
  • 14
  • 81
  • 132
Patrick Oscity
  • 53,604
  • 17
  • 144
  • 168
0

You indicate you want to validate the zip file and its contents. While this is partially an Elixir/Erlang question it also goes somewhat beyond that simple context. This would be my suggestion:

  1. Create a list of all of the files and create a checksum for each file. Save this somewhere.

  2. Zip the whole thing.

  3. Extract the files from the zip to a different directory.

  4. Count the files in the extracted directory--if the number of files isn't the same, test fails.

  5. Check the list of files extracted against the list created in step 1. If the list of files doesn't match, test fails.

  6. Calculate the checksums of all the files. Compare the checksums against the checksums stored in step 1. If they don't match, test fails.

All that said, this strikes me as a pretty pointless exercise because basically what you're testing is the Erlang zip library. If there were flaws in the Erlang zip library, people would already have complained loud and long and you'd be able to find out there were flaws by simply googling for it.

That is to say, while you can write unit tests for this it strikes me as a waste of time.

Onorio Catenacci
  • 14,928
  • 14
  • 81
  • 132