zippo 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +17 -0
- data/.rspec +1 -0
- data/.yardopts +1 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +83 -0
- data/Rakefile +11 -0
- data/lib/zippo/binary_structure/base.rb +119 -0
- data/lib/zippo/binary_structure/binary_packer.rb +17 -0
- data/lib/zippo/binary_structure/binary_unpacker.rb +32 -0
- data/lib/zippo/binary_structure/meta.rb +146 -0
- data/lib/zippo/binary_structure/structure.rb +24 -0
- data/lib/zippo/binary_structure/structure_member.rb +31 -0
- data/lib/zippo/binary_structure.rb +6 -0
- data/lib/zippo/cd_file_header.rb +36 -0
- data/lib/zippo/central_directory_entries_unpacker.rb +23 -0
- data/lib/zippo/central_directory_reader.rb +44 -0
- data/lib/zippo/end_cd_record.rb +21 -0
- data/lib/zippo/filter/base.rb +29 -0
- data/lib/zippo/filter/compressor/deflate.rb +23 -0
- data/lib/zippo/filter/compressor/store.rb +12 -0
- data/lib/zippo/filter/compressor.rb +42 -0
- data/lib/zippo/filter/compressors.rb +3 -0
- data/lib/zippo/filter/null_filters.rb +15 -0
- data/lib/zippo/filter/uncompressor/deflate.rb +25 -0
- data/lib/zippo/filter/uncompressor/store.rb +12 -0
- data/lib/zippo/filter/uncompressor.rb +59 -0
- data/lib/zippo/filter/uncompressors.rb +3 -0
- data/lib/zippo/io_zip_member.rb +24 -0
- data/lib/zippo/local_file_header.rb +28 -0
- data/lib/zippo/version.rb +3 -0
- data/lib/zippo/zip_directory.rb +80 -0
- data/lib/zippo/zip_file.rb +121 -0
- data/lib/zippo/zip_file_writer.rb +57 -0
- data/lib/zippo/zip_member.rb +85 -0
- data/lib/zippo.rb +18 -0
- data/spec/binary_structure_spec.rb +132 -0
- data/spec/central_directory_entries_unpacker_spec.rb +29 -0
- data/spec/central_directory_parser_spec.rb +50 -0
- data/spec/central_directory_unpacker_spec.rb +31 -0
- data/spec/compressor_spec.rb +14 -0
- data/spec/data/comment.zip +0 -0
- data/spec/data/deflate.zip +0 -0
- data/spec/data/multi.zip +0 -0
- data/spec/data/not_a.zip +1 -0
- data/spec/data/test.zip +0 -0
- data/spec/deflate_compressor_spec.rb +21 -0
- data/spec/deflate_uncompressor_spec.rb +23 -0
- data/spec/integration/compressors_spec.rb +21 -0
- data/spec/integration/zippo_spec.rb +55 -0
- data/spec/io_zip_member_spec.rb +32 -0
- data/spec/local_file_header_spec.rb +18 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/store_compressor_spec.rb +19 -0
- data/spec/store_uncompressor_spec.rb +19 -0
- data/spec/uncompressor_spec.rb +14 -0
- data/spec/zip_directory_spec.rb +63 -0
- data/spec/zip_file_spec.rb +50 -0
- data/spec/zip_file_writer_spec.rb +42 -0
- data/spec/zip_member_spec.rb +42 -0
- data/yard_extensions.rb +10 -0
- data/zippo.gemspec +23 -0
- metadata +163 -0
@@ -0,0 +1,132 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
# TODO - spec that unpackers can take strings
|
4
|
+
|
5
|
+
require 'zippo/binary_structure'
|
6
|
+
|
7
|
+
module Zippo
|
8
|
+
describe BinaryStructure do
|
9
|
+
let(:klass) { Class.new }
|
10
|
+
let(:obj) { klass.new }
|
11
|
+
let(:unpacker) { klass::Unpacker }
|
12
|
+
before(:each) do
|
13
|
+
klass.send :extend, BinaryStructure
|
14
|
+
end
|
15
|
+
context "when used to configure a class as a binary structure" do
|
16
|
+
before(:each) do
|
17
|
+
klass.class_eval do
|
18
|
+
binary_structure do
|
19
|
+
field :foo, 'L'
|
20
|
+
field :yay, 'a4', :signature => "baz"
|
21
|
+
field :bar, 'S'
|
22
|
+
field :quux, 'a*', :size => :foo
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
it "should store field information in the class" do
|
27
|
+
klass.structure.should have(4).fields
|
28
|
+
klass.structure.fields[0].name.should eq :foo
|
29
|
+
klass.structure.fields[3].options[:size].should eq :foo
|
30
|
+
klass.structure.fields[1].should be_signature
|
31
|
+
end
|
32
|
+
it "should have dependent fields" do
|
33
|
+
obj.quux = "foobar"
|
34
|
+
obj.foo.should eq 6
|
35
|
+
obj.quux.should eq "foobar"
|
36
|
+
end
|
37
|
+
it "should not allow mutation of dependent fields" do
|
38
|
+
-> { obj.foo = 5 }.should raise_error
|
39
|
+
end
|
40
|
+
it "should have regular fields" do
|
41
|
+
obj.bar = 10
|
42
|
+
obj.bar.should eq 10
|
43
|
+
end
|
44
|
+
it "should have an unpacker" do
|
45
|
+
array = [10,"baz", 42,"foobar baz"]
|
46
|
+
packed = array.pack 'La4Sa*'
|
47
|
+
io = StringIO.new packed
|
48
|
+
obj = klass::Unpacker.new(io).unpack
|
49
|
+
obj.foo.should eq 10
|
50
|
+
obj.bar.should eq 42
|
51
|
+
obj.quux.should eq "foobar baz"
|
52
|
+
end
|
53
|
+
it "should unpack oversized strings correctly" do
|
54
|
+
array = [3,"baz", 42,"foobar baz"]
|
55
|
+
packed = array.pack 'La4Sa*'
|
56
|
+
io = StringIO.new packed
|
57
|
+
obj = klass::Unpacker.new(io).unpack
|
58
|
+
obj.quux.should eq "foo"
|
59
|
+
end
|
60
|
+
it "should have a packer" do
|
61
|
+
array = [10,"baz", 42,"foobar baz"]
|
62
|
+
packed = array.pack 'La4Sa*'
|
63
|
+
io = StringIO.new
|
64
|
+
obj.bar = 42
|
65
|
+
obj.yay = "baz"
|
66
|
+
obj.quux = "foobar baz"
|
67
|
+
klass::Packer.new(io).pack obj
|
68
|
+
io.string.should eq packed
|
69
|
+
end
|
70
|
+
it "should have a .size method" do
|
71
|
+
obj.bar = 42
|
72
|
+
obj.quux = "foobar baz"
|
73
|
+
obj.size.should eq 20
|
74
|
+
end
|
75
|
+
context "when there is another class" do
|
76
|
+
let(:other_klass) { Class.new }
|
77
|
+
before(:each) do
|
78
|
+
other_klass.send :extend, BinaryStructure
|
79
|
+
other_klass.class_eval do
|
80
|
+
binary_structure do
|
81
|
+
field :bar, 'S'
|
82
|
+
field :yay, 'a4', :signature => "quux"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
describe ".convert_to" do
|
87
|
+
it "should convert to the other class" do
|
88
|
+
obj.quux = "foobar"
|
89
|
+
obj.bar = 5
|
90
|
+
other_obj = obj.convert_to other_klass
|
91
|
+
other_obj.bar.should eq 5
|
92
|
+
end
|
93
|
+
it "should not convert a signature" do
|
94
|
+
obj.quux = "foobar"
|
95
|
+
obj.bar = 5
|
96
|
+
obj.yay = "baz"
|
97
|
+
other_obj = obj.convert_to other_klass
|
98
|
+
other_obj.bar.should eq 5
|
99
|
+
other_obj.yay.should eq "quux"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
pending "should handle binary structures with multiple lots of fixed fields interspersed with variable length fields"
|
105
|
+
# none of the current binary structures use a
|
106
|
+
# feature like this, they're all a bunch of fixed
|
107
|
+
# fields followed by some variable length fields.
|
108
|
+
# need to test a few fixed fields, followed by a
|
109
|
+
# variable length field, then a few more fixed
|
110
|
+
# fields
|
111
|
+
=begin
|
112
|
+
it "should complain if the order is bad" do
|
113
|
+
pending
|
114
|
+
klass = Class.new BinaryStructure
|
115
|
+
lambda { klass.class_eval do
|
116
|
+
field :foo, 'S'
|
117
|
+
field :bar, 'a*', :size => :baz
|
118
|
+
field :baz, 'S'
|
119
|
+
end }.should raise_error "size not found"
|
120
|
+
end
|
121
|
+
it "should remain silent if the order is ok" do
|
122
|
+
pending
|
123
|
+
klass = Class.new BinaryStructure
|
124
|
+
lambda { klass.class_eval do
|
125
|
+
field :foo, 'S'
|
126
|
+
field :baz, 'S'
|
127
|
+
field :bar, 'a*', :size => :baz
|
128
|
+
end }.should_not raise_error
|
129
|
+
end
|
130
|
+
=end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
require "zippo/central_directory_entries_unpacker"
|
4
|
+
|
5
|
+
module Zippo
|
6
|
+
describe CentralDirectoryEntriesUnpacker do
|
7
|
+
subject { CentralDirectoryEntriesUnpacker.new(io, size, offset).unpack }
|
8
|
+
|
9
|
+
context "when it's a simple file" do
|
10
|
+
let(:io) { File.open(test_file "test.zip") }
|
11
|
+
let(:offset) { 112 }
|
12
|
+
let(:size) { 79 }
|
13
|
+
|
14
|
+
specify { subject.should have(1).items }
|
15
|
+
specify { subject.first.name.should eq "test.file" }
|
16
|
+
end
|
17
|
+
|
18
|
+
context "when it's a complex file" do
|
19
|
+
let(:io) { File.open(test_file "multi.zip") }
|
20
|
+
let(:offset) { 242 }
|
21
|
+
let(:size) { 158 }
|
22
|
+
|
23
|
+
specify { subject.should have(2).items }
|
24
|
+
specify { subject[0].name.should eq "test.file" }
|
25
|
+
specify { subject[1].name.should eq "other.test" }
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
require "zippo/central_directory_reader"
|
4
|
+
require "zippo/zip_file"
|
5
|
+
|
6
|
+
module Zippo
|
7
|
+
describe CentralDirectoryReader do
|
8
|
+
let(:io) { File.open(file, "rb:ASCII-8BIT") }
|
9
|
+
let(:file) { test_file "test.zip" }
|
10
|
+
let(:parser) { CentralDirectoryReader.new(io) }
|
11
|
+
after(:each) { io.close }
|
12
|
+
|
13
|
+
context "when parsing a simple file" do
|
14
|
+
let(:file) { test_file "test.zip" }
|
15
|
+
it "should parse the End of Central Directory Record" do
|
16
|
+
parser.end_of_cd_record.total_records.should eq 1
|
17
|
+
end
|
18
|
+
|
19
|
+
specify { parser.end_of_cd_record_position.should eq 0xbf }
|
20
|
+
specify { parser.should have(1).cd_file_headers }
|
21
|
+
end
|
22
|
+
|
23
|
+
context "when parsing a file with a comment" do
|
24
|
+
let(:file) { test_file "comment.zip" }
|
25
|
+
specify { parser.end_of_cd_record_position.should eq 0xbf }
|
26
|
+
specify { parser.end_of_cd_record.comment.should eq "this is a comment to make things tricky" }
|
27
|
+
end
|
28
|
+
|
29
|
+
context "when parsing a multi-entry file" do
|
30
|
+
let(:file) { test_file "multi.zip" }
|
31
|
+
specify { parser.end_of_cd_record_position.should eq 0x191 }
|
32
|
+
end
|
33
|
+
|
34
|
+
context "when parsing a file that is not a zip" do
|
35
|
+
let(:file) { test_file "not_a.zip" }
|
36
|
+
specify { -> {parser.end_of_cd_record_position}.should raise_error(/not found/) }
|
37
|
+
end
|
38
|
+
|
39
|
+
context "when parsing a zip file larger than the maximum comment size" do
|
40
|
+
it "should not barf" do
|
41
|
+
in_working_directory do
|
42
|
+
Zippo::ZipFile.open("large.zip", "w") do |v|
|
43
|
+
v["test.file"] = "a" * 65535
|
44
|
+
end
|
45
|
+
Zippo::ZipFile.open("large.zip") {|v| v["test.file"].read }.should eq "a" * 65535
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
require "zippo/end_cd_record"
|
4
|
+
|
5
|
+
module Zippo
|
6
|
+
describe "EndCdRecord::Unpacker" do
|
7
|
+
subject { EndCdRecord::Unpacker.new(str).unpack }
|
8
|
+
|
9
|
+
context "when it's a simple file" do
|
10
|
+
let(:str) { File.binread(test_file("test.zip"))[-22..-1] }
|
11
|
+
|
12
|
+
specify { subject.comment.should eq "" }
|
13
|
+
specify { subject.total_records.should eq 1 }
|
14
|
+
specify { subject.cd_offset.should eq 112 }
|
15
|
+
end
|
16
|
+
|
17
|
+
context "when it's a multi entry file" do
|
18
|
+
let(:str) { File.binread(test_file("multi.zip"))[-22..-1] }
|
19
|
+
|
20
|
+
specify { subject.total_records.should eq 2 }
|
21
|
+
end
|
22
|
+
|
23
|
+
context "when there is a comment" do
|
24
|
+
let(:str) { File.binread(test_file("comment.zip"))[-61..-1] }
|
25
|
+
|
26
|
+
specify { subject.total_records.should eq 1 }
|
27
|
+
specify { subject.comment.should eq "this is a comment to make things tricky" }
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
require "zippo/filter/compressor"
|
4
|
+
require "zippo/filter/compressors"
|
5
|
+
|
6
|
+
module Zippo::Filter
|
7
|
+
describe Compressor do
|
8
|
+
it "should be a factory for compressors" do
|
9
|
+
Compressor.for(StoreCompressor::METHOD).should eq StoreCompressor
|
10
|
+
Compressor.for(DeflateCompressor::METHOD).should eq DeflateCompressor
|
11
|
+
-> { Compressor.for(113) }.should raise_error(/unknown compression method/)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
Binary file
|
Binary file
|
data/spec/data/multi.zip
ADDED
Binary file
|
data/spec/data/not_a.zip
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
this is not a zip file
|
data/spec/data/test.zip
ADDED
Binary file
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# Encoding: BINARY
|
2
|
+
require "spec_helper"
|
3
|
+
|
4
|
+
require "zippo/filter/compressor/deflate"
|
5
|
+
|
6
|
+
module Zippo::Filter
|
7
|
+
describe DeflateCompressor do
|
8
|
+
let (:data) { "a" * 20 }
|
9
|
+
let (:zdata) { "KL\xC4\u0004\u0000" }
|
10
|
+
let (:io) { StringIO.new data }
|
11
|
+
let (:out) { StringIO.new "" }
|
12
|
+
it "should write the data" do
|
13
|
+
compressor = DeflateCompressor.new(io)
|
14
|
+
csize, size, crc32 = compressor.compress_to out
|
15
|
+
size.should eq 20
|
16
|
+
csize.should eq zdata.size
|
17
|
+
crc32.should eq Zlib.crc32(data)
|
18
|
+
out.string.should eq zdata
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
require "zippo/filter/uncompressor/deflate"
|
4
|
+
|
5
|
+
module Zippo::Filter
|
6
|
+
describe DeflateUncompressor do
|
7
|
+
|
8
|
+
let (:zstring) { Zlib::Deflate.new(Zlib::BEST_COMPRESSION, -Zlib::MAX_WBITS).deflate("a" * 20, Zlib::FINISH) }
|
9
|
+
let (:size) { zstring.size }
|
10
|
+
let (:string) { "aaa" + zstring + "bbb" }
|
11
|
+
let (:out) { StringIO.new }
|
12
|
+
let (:io) { StringIO.new string }
|
13
|
+
it "should deflate only the compressed string" do
|
14
|
+
io.seek 3
|
15
|
+
DeflateUncompressor.new(io, size).uncompress.should eq "a" * 20
|
16
|
+
end
|
17
|
+
it "should deflate to an IO" do
|
18
|
+
io.seek 3
|
19
|
+
DeflateUncompressor.new(io, size).uncompress_to(out)
|
20
|
+
out.string.should eq "a" * 20
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
require "zippo/filter/uncompressor"
|
4
|
+
require "zippo/filter/uncompressors"
|
5
|
+
require "zippo/filter/compressor"
|
6
|
+
require 'zippo/filter/compressors'
|
7
|
+
|
8
|
+
module Zippo::Filter
|
9
|
+
describe Uncompressor do
|
10
|
+
let (:zstring) { Zlib::Deflate.new(Zlib::BEST_COMPRESSION, -Zlib::MAX_WBITS).deflate("a" * 20, Zlib::FINISH) }
|
11
|
+
it "should allow uncompressors to work with compressors" do
|
12
|
+
stream = StringIO.new zstring
|
13
|
+
deflater = DeflateUncompressor.new(stream, zstring.size)
|
14
|
+
storer = StoreCompressor.new(deflater)
|
15
|
+
out = StringIO.new
|
16
|
+
out.set_encoding "BINARY"
|
17
|
+
storer.compress_to(out)
|
18
|
+
out.string.should eq "a" * 20
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# Encoding: BINARY
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
require 'zippo'
|
5
|
+
|
6
|
+
module Zippo
|
7
|
+
describe ZipFile do
|
8
|
+
let(:file) { test_file "test.zip" }
|
9
|
+
let(:member_data) { "The quick brown fox jumps over the lazy dog.\n" }
|
10
|
+
let(:zip) { Zippo::ZipFile.open(file) }
|
11
|
+
describe "when reading a zip file" do
|
12
|
+
after(:each) do
|
13
|
+
zip.close
|
14
|
+
end
|
15
|
+
it "should be able to read a member of the file" do
|
16
|
+
data = zip["test.file"].read
|
17
|
+
data.should eq member_data
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should read a compressed file" do
|
21
|
+
zip = Zippo::ZipFile.open(test_file("deflate.zip"))
|
22
|
+
zip["weasels.txt"].read.should eq "Methinks it is like a weasel.\n" * 10
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should work like File.open" do
|
26
|
+
io = File.open(file)
|
27
|
+
File.should_receive(:open).with(file, 'r:ASCII-8BIT').and_return(io)
|
28
|
+
s = Zippo.open(file) { |v| v['test.file'].read }
|
29
|
+
s.should eq member_data
|
30
|
+
io.should be_closed
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should create zip files" do
|
34
|
+
in_working_directory do
|
35
|
+
File.write "xyzzy.txt", "plugh"
|
36
|
+
Zippo.open("new.zip", "w") {|v| v['xyzzy.txt'] = "plugh" }
|
37
|
+
Pathname.new("new.zip").should exist
|
38
|
+
Zippo.open("new.zip") {|v| v['xyzzy.txt'].read }.should eq "plugh"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should update in place with mode rw" do
|
43
|
+
in_working_directory do
|
44
|
+
Zippo.open("out.zip", "w") { |v| v['foo1'] = "data1" }
|
45
|
+
Zippo.open("out.zip", "rw") { |v| v['foo2'] = "data2" }
|
46
|
+
IO.write "/tmp/bar.zip", IO.read("out.zip")
|
47
|
+
Zippo.open("out.zip") do |v|
|
48
|
+
v['foo1'].read.should eq "data1"
|
49
|
+
v['foo2'].read.should eq "data2"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
require "zippo/io_zip_member"
|
4
|
+
require "zippo/filter/compressor/store"
|
5
|
+
|
6
|
+
module Zippo
|
7
|
+
describe IOZipMember do
|
8
|
+
let(:io) { StringIO.new "foobar" }
|
9
|
+
let(:out) { StringIO.new "" }
|
10
|
+
let(:name) { "name" }
|
11
|
+
subject { IOZipMember.new name, io }
|
12
|
+
specify { subject.name.should eq "name" }
|
13
|
+
specify { subject.read.should eq "foobar" }
|
14
|
+
context "when writing" do
|
15
|
+
before(:each) do
|
16
|
+
@size, @csize, @crc32 = subject.write_to out, 0
|
17
|
+
end
|
18
|
+
it "should write to the io" do
|
19
|
+
out.string.should eq "foobar"
|
20
|
+
end
|
21
|
+
it "should have returned the original size" do
|
22
|
+
@size.should eq 6
|
23
|
+
end
|
24
|
+
it "should return the compressed size" do
|
25
|
+
@csize.should eq 6
|
26
|
+
end
|
27
|
+
it "should return the CRC32" do
|
28
|
+
@crc32.should eq Zlib.crc32("foobar")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
require "zippo/local_file_header"
|
4
|
+
|
5
|
+
module Zippo
|
6
|
+
describe "LocalFileHeader::Unpacker" do
|
7
|
+
before(:each) do
|
8
|
+
@io = File.open test_file("test.zip"), "rb:ASCII-8BIT"
|
9
|
+
@io.seek 0
|
10
|
+
end
|
11
|
+
after(:each) do
|
12
|
+
@io.close
|
13
|
+
end
|
14
|
+
it "should unpack from an io" do
|
15
|
+
LocalFileHeader::Unpacker.new(@io.read).unpack.name.should eq "test.file"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
def io_for(filename)
|
2
|
+
File.open test_file(filename), "rb:ASCII-8BIT"
|
3
|
+
end
|
4
|
+
def test_file(filename)
|
5
|
+
File.join(File.join(File.dirname(__FILE__), 'data'), filename)
|
6
|
+
end
|
7
|
+
require 'tmpdir'
|
8
|
+
def in_working_directory
|
9
|
+
Dir.mktmpdir do |dir|
|
10
|
+
Dir.chdir(dir) { yield }
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
require "zippo/filter/compressor/store"
|
4
|
+
|
5
|
+
module Zippo::Filter
|
6
|
+
describe StoreCompressor do
|
7
|
+
let (:data) { "a" * 20 }
|
8
|
+
let (:io) { StringIO.new data }
|
9
|
+
let (:out) { StringIO.new }
|
10
|
+
it "should write the data" do
|
11
|
+
compressor = StoreCompressor.new(io)
|
12
|
+
size, csize, crc32 = compressor.compress_to out
|
13
|
+
size.should eq 20
|
14
|
+
csize.should eq 20
|
15
|
+
crc32.should eq Zlib.crc32(data)
|
16
|
+
out.string.should eq data
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
require "zippo/filter/uncompressor/store"
|
4
|
+
|
5
|
+
module Zippo::Filter
|
6
|
+
describe StoreUncompressor do
|
7
|
+
let(:io) { StringIO.new "foobarbazquux" }
|
8
|
+
let (:out) { StringIO.new }
|
9
|
+
it "should read only the specified amount" do
|
10
|
+
io.seek 3
|
11
|
+
StoreUncompressor.new(io, 3).uncompress.should eq "bar"
|
12
|
+
end
|
13
|
+
it "should deflate to an IO" do
|
14
|
+
io.seek 3
|
15
|
+
StoreUncompressor.new(io, 3).uncompress_to(out)
|
16
|
+
out.string.should eq "bar"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
require "zippo/filter/uncompressor"
|
4
|
+
require "zippo/filter/uncompressors"
|
5
|
+
|
6
|
+
module Zippo::Filter
|
7
|
+
describe Uncompressor do
|
8
|
+
it "should be a factory for uncompressors" do
|
9
|
+
Uncompressor.for(0).should eq StoreUncompressor
|
10
|
+
Uncompressor.for(8).should eq DeflateUncompressor
|
11
|
+
-> { Uncompressor.for(113) }.should raise_error(/unknown compression method/)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'zippo/zip_file'
|
4
|
+
|
5
|
+
module Zippo
|
6
|
+
describe ZipDirectory do
|
7
|
+
context "when opening a file" do
|
8
|
+
let(:io) { File.open(file, "rb:ASCII-8BIT") }
|
9
|
+
after(:each) { io.close }
|
10
|
+
subject { ZipDirectory.new io }
|
11
|
+
context "when reading a simple file" do
|
12
|
+
let(:file) { test_file "test.zip" }
|
13
|
+
it { should have(1).entries }
|
14
|
+
specify { subject["test.file"].should_not be_nil }
|
15
|
+
it { should_not be_empty }
|
16
|
+
end
|
17
|
+
context "when reading a larger zip" do
|
18
|
+
let(:file) { test_file "multi.zip" }
|
19
|
+
it { should have(2).entries }
|
20
|
+
specify { subject["test.file"].should_not be_nil }
|
21
|
+
specify { subject["other.test"].should_not be_nil }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
context "when inserting" do
|
25
|
+
let(:io) { StringIO.new "" }
|
26
|
+
it "should allow the insertion of new members" do
|
27
|
+
subject["new.file"] = "file data"
|
28
|
+
subject["new.file"].read.should eq "file data"
|
29
|
+
subject["new.file"].write_to io, 0
|
30
|
+
io.string.should eq "file data"
|
31
|
+
end
|
32
|
+
it "should allow the insertion of IO objects" do
|
33
|
+
in_working_directory do
|
34
|
+
File.write "xyzzy.txt", "plugh"
|
35
|
+
file = File.open "xyzzy.txt"
|
36
|
+
subject.insert "woo.txt", file
|
37
|
+
subject["woo.txt"].read.should eq "plugh"
|
38
|
+
subject["woo.txt"].write_to io, 0
|
39
|
+
file.close
|
40
|
+
io.string.should eq "plugh"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
it "should allow insertion of file contents" do
|
44
|
+
in_working_directory do
|
45
|
+
File.write "xyzzy.txt", "plugh"
|
46
|
+
subject.insert "woo.txt", "xyzzy.txt"
|
47
|
+
subject["woo.txt"].read.should eq "plugh"
|
48
|
+
subject["woo.txt"].write_to io, 0
|
49
|
+
io.string.should eq "plugh"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
it "should allow the insertion of ZipMember contents" do
|
53
|
+
zip = Zippo::ZipFile.open test_file "test.zip"
|
54
|
+
subject.insert "hmm.txt", zip['test.file']
|
55
|
+
subject["hmm.txt"].read.should eq "The quick brown fox jumps over the lazy dog.\n"
|
56
|
+
subject["hmm.txt"].write_to io, 0
|
57
|
+
zip.close
|
58
|
+
io.string.should eq "The quick brown fox jumps over the lazy dog.\n"
|
59
|
+
-> { subject["hmm.txt"].read }.should raise_error # after closing original zip
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
require "zippo/zip_file"
|
4
|
+
require "zippo/filter/uncompressors"
|
5
|
+
require "zippo/filter/compressors"
|
6
|
+
|
7
|
+
module Zippo
|
8
|
+
describe ZipFile do
|
9
|
+
# XXX unit test for open
|
10
|
+
describe ".open" do
|
11
|
+
let(:zip) { ZipFile.open(file, mode) }
|
12
|
+
let(:file) { test_file "test.zip" }
|
13
|
+
context "when in read mode" do
|
14
|
+
let(:mode) { "r" }
|
15
|
+
it "should allow reading of zip members" do
|
16
|
+
zip['test.file'].should be_a ZipMember
|
17
|
+
zip['test.file'].read.should eq "The quick brown fox jumps over the lazy dog.\n"
|
18
|
+
end
|
19
|
+
it "should allow iterating over the directory" do
|
20
|
+
files = []
|
21
|
+
zip.each { |m| files << m.name }
|
22
|
+
files.should include("test.file")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
context "when in write mode" do
|
26
|
+
let(:mode) { "w" }
|
27
|
+
let(:file) { "/tmp/foo.zip" }
|
28
|
+
it "should not allow reading of zip members that haven't been written" do
|
29
|
+
zip['test.file'].should be_nil
|
30
|
+
end
|
31
|
+
context "when a member is inserted" do
|
32
|
+
before(:each) do
|
33
|
+
zip['new.file'] = "foo"
|
34
|
+
end
|
35
|
+
it "should write the file when closed" do
|
36
|
+
zip.close
|
37
|
+
Pathname.new(file).should exist
|
38
|
+
Zippo::ZipFile.open(file) {|f| f["new.file"].read }.should eq "foo"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
context "when in update mode" do
|
43
|
+
let(:mode) { "rw" }
|
44
|
+
it "should read the zip file" do
|
45
|
+
zip.directory.should_not be_empty
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
require "zippo" #
|
4
|
+
require "zippo/zip_file_writer"
|
5
|
+
require "zippo/zip_directory"
|
6
|
+
|
7
|
+
module Zippo
|
8
|
+
describe ZipFileWriter do
|
9
|
+
describe ".write" do
|
10
|
+
it "should write the zip file" do
|
11
|
+
in_working_directory do
|
12
|
+
directory = ZipDirectory.new
|
13
|
+
directory["file.ext"] = "foo"
|
14
|
+
writer = ZipFileWriter.new directory, "out.zip"
|
15
|
+
writer.write
|
16
|
+
File.open("/home/jma/yay.zip","w") {|f| f.write File.read("out.zip") }
|
17
|
+
Zippo.open("out.zip") do |f|
|
18
|
+
f['file.ext'].crc32.should eq 0x8c736521
|
19
|
+
f['file.ext'].compressed_size.should eq 5
|
20
|
+
f['file.ext'].uncompressed_size.should eq 3
|
21
|
+
f['file.ext'].read.should eq "foo"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
it "should perform direct zip copying" do
|
26
|
+
in_working_directory do
|
27
|
+
zip = Zippo.open(test_file "test.zip")
|
28
|
+
Zippo.open("out.zip", "w") do |out|
|
29
|
+
out.directory.insert 'out.file', zip['test.file']
|
30
|
+
end
|
31
|
+
zip.close
|
32
|
+
|
33
|
+
IO.write "/tmp/out.zip", IO.read("out.zip")
|
34
|
+
|
35
|
+
Zippo.open("out.zip") { |v| v['out.file'].read }.should eq "The quick brown fox jumps over the lazy dog.\n"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
pending "should be able to peform direct zip copying after the source zip is closed"
|
39
|
+
pending "should be configurable in terms of compression method and effort"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|