zip_tricks 4.2.3 → 4.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,100 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe ZipTricks::RemoteUncap, webmock: true do
4
- let(:uri) { URI.parse('http://example.com/file.zip') }
5
-
6
- after :each do
7
- File.unlink('temp.zip') rescue Errno::ENOENT
8
- end
9
-
10
- it 'returns an array of remote entries that can be used to fetch the segments from within the ZIP' do
11
- payload1 = Tempfile.new 'payload1'
12
- payload1 << Random.new.bytes((1024 * 1024 * 5) + 10)
13
- payload1.flush; payload1.rewind;
14
-
15
- payload2 = Tempfile.new 'payload2'
16
- payload2 << Random.new.bytes(1024 * 1024 * 3)
17
- payload2.flush; payload2.rewind
18
-
19
- File.open('temp.zip', 'wb') do |f|
20
- ZipTricks::Streamer.open(f) do | zip |
21
- zip.write_stored_file('first-file.bin') { |w| IO.copy_stream(payload1, w) }
22
- zip.write_stored_file('second-file.bin') { |w| IO.copy_stream(payload2, w) }
23
- end
24
- end
25
- payload1.rewind; payload2.rewind
26
-
27
- expect(File).to be_exist('temp.zip')
28
-
29
- allow_any_instance_of(described_class).to receive(:request_object_size) {
30
- File.size('temp.zip')
31
- }
32
- allow_any_instance_of(described_class).to receive(:request_range) {|_instance, range|
33
- File.open('temp.zip', 'rb') do |f|
34
- f.seek(range.begin)
35
- f.read(range.end - range.begin + 1)
36
- end
37
- }
38
-
39
- payload1.rewind; payload2.rewind
40
-
41
- files = described_class.files_within_zip_at('http://fake.example.com')
42
- expect(files).to be_kind_of(Array)
43
- expect(files.length).to eq(2)
44
-
45
- first, second = *files
46
-
47
- expect(first.filename).to eq('first-file.bin')
48
- expect(first.uncompressed_size).to eq(payload1.size)
49
- File.open('temp.zip', 'rb') do |readback|
50
- readback.seek(first.compressed_data_offset, IO::SEEK_SET)
51
- expect(readback.read(12)).to eq(payload1.read(12))
52
- end
53
-
54
- expect(second.filename).to eq('second-file.bin')
55
- expect(second.uncompressed_size).to eq(payload2.size)
56
- File.open('temp.zip', 'rb') do |readback|
57
- readback.seek(second.compressed_data_offset, IO::SEEK_SET)
58
- expect(readback.read(12)).to eq(payload2.read(12))
59
- end
60
- end
61
-
62
- it 'can cope with an empty file within the zip' do
63
- payload1 = Tempfile.new 'payload1'
64
- payload1.flush; payload1.rewind;
65
-
66
- payload2 = Tempfile.new 'payload2'
67
- payload2 << Random.new.bytes(1024)
68
- payload2.flush; payload2.rewind
69
-
70
- payload1_crc = Zlib.crc32(payload1.read).tap { payload1.rewind }
71
- payload2_crc = Zlib.crc32(payload2.read).tap { payload2.rewind }
72
-
73
- readable_zip = Tempfile.new 'somezip'
74
- ZipTricks::Streamer.open(readable_zip) do | zip |
75
- zip.add_stored_entry(filename: 'first-file-zero-size.bin', size: payload1.size, crc32: payload1_crc)
76
- zip.write_stored_file('second-file.bin') {|w| IO.copy_stream(payload2, w) }
77
- end
78
- readable_zip.flush; readable_zip.rewind
79
-
80
- allow_any_instance_of(described_class).to receive(:request_object_size) {
81
- readable_zip.size
82
- }
83
- allow_any_instance_of(described_class).to receive(:request_range) {|_instance, range|
84
- readable_zip.seek(range.begin, IO::SEEK_SET)
85
- readable_zip.read(range.end - range.begin + 1)
86
- }
87
-
88
- payload1.rewind; payload2.rewind
89
-
90
- first, second = described_class.files_within_zip_at('http://fake.example.com')
91
-
92
- expect(first.filename).to eq('first-file-zero-size.bin')
93
- expect(first.compressed_size).to be_zero
94
-
95
- expect(second.filename).to eq('second-file.bin')
96
- expect(second.uncompressed_size).to eq(payload2.size)
97
- readable_zip.seek(second.compressed_data_offset, IO::SEEK_SET)
98
- expect(readable_zip.read(12)).to eq(payload2.read(12))
99
- end
100
- end
@@ -1,31 +0,0 @@
1
- require_relative '../spec_helper'
2
-
3
- describe ZipTricks::SizeEstimator do
4
- it 'accurately predicts the output zip size' do
5
- # Generate a couple of random files
6
- raw_file_1 = SecureRandom.random_bytes(1024 * 20)
7
- raw_file_2 = SecureRandom.random_bytes(1024 * 128)
8
- raw_file_3 = SecureRandom.random_bytes(1258695)
9
-
10
- predicted_size = described_class.estimate do | estimator |
11
- r = estimator.add_stored_entry(filename: "first-file.bin", size: raw_file_1.size)
12
- expect(r).to eq(estimator), "add_stored_entry should return self"
13
-
14
- estimator.add_stored_entry(filename: "second-file.bin", size: raw_file_2.size)
15
-
16
- r = estimator.add_compressed_entry(filename: "second-flie.bin", compressed_size: raw_file_3.size,
17
- uncompressed_size: raw_file_2.size, )
18
- expect(r).to eq(estimator), "add_compressed_entry should return self"
19
-
20
- r = estimator.add_stored_entry(filename: "first-file-with-descriptor.bin", size: raw_file_1.size,
21
- use_data_descriptor: true)
22
- expect(r).to eq(estimator), "add_stored_entry should return self"
23
-
24
- r = estimator.add_compressed_entry(filename: "second-file-with-descriptor.bin", compressed_size: raw_file_3.size,
25
- uncompressed_size: raw_file_2.size, use_data_descriptor: true)
26
- expect(r).to eq(estimator), "add_compressed_entry should return self"
27
- end
28
-
29
- expect(predicted_size).to eq(2690095)
30
- end
31
- end
@@ -1,38 +0,0 @@
1
- require_relative '../spec_helper'
2
-
3
- describe ZipTricks::StreamCRC32 do
4
- it 'computes the CRC32 of a large binary file' do
5
- raw = StringIO.new(SecureRandom.random_bytes(45 * 1024 * 1024))
6
- crc = Zlib.crc32(raw.string)
7
- via_from_io = described_class.from_io(raw)
8
- expect(via_from_io).to eq(crc)
9
- end
10
-
11
- it 'allows in-place updates' do
12
- raw = StringIO.new(SecureRandom.random_bytes(45 * 1024 * 1024))
13
- crc = Zlib.crc32(raw.string)
14
-
15
- stream_crc = described_class.new
16
- stream_crc << raw.read(1024 * 64) until raw.eof?
17
- expect(stream_crc.to_i).to eq(crc)
18
- end
19
-
20
- it 'supports chained shovel' do
21
- str = 'abcdef'
22
- crc = Zlib.crc32(str)
23
-
24
- stream_crc = described_class.new
25
- stream_crc << 'a' << 'b' << 'c' << 'd' << 'e' << 'f'
26
-
27
- expect(stream_crc.to_i).to eq(crc)
28
- end
29
-
30
- it 'allows in-place update with a known value' do
31
- crc = Zlib.crc32
32
-
33
- stream_crc = described_class.new
34
- stream_crc << "This is some data"
35
- stream_crc.append(45678, 12910)
36
- expect(stream_crc.to_i).to eq(1555667875)
37
- end
38
- end
@@ -1,27 +0,0 @@
1
- require_relative '../../spec_helper'
2
-
3
- describe ZipTricks::Streamer::Writable do
4
- describe '#<<' do
5
- it 'writes the given data to the destination and returns self' do
6
- buf = StringIO.new
7
- subject = described_class.new(buf)
8
-
9
- result = subject << 'hello!'
10
-
11
- expect(buf.string).to eq('hello!')
12
- expect(result).to eq(subject)
13
- end
14
- end
15
-
16
- describe '#write' do
17
- it 'writes the given data to the destination and returns the number of bytes written' do
18
- buf = StringIO.new
19
- subject = described_class.new(buf)
20
-
21
- result = subject.write('hello!')
22
-
23
- expect(buf.string).to eq('hello!')
24
- expect(result).to eq(6)
25
- end
26
- end
27
- end
@@ -1,296 +0,0 @@
1
- require_relative '../spec_helper'
2
-
3
- describe ZipTricks::Streamer do
4
- let(:test_text_file_path) {
5
- File.join(__dir__, 'war-and-peace.txt')
6
- }
7
-
8
- # Run each test in a temporady directory, and nuke it afterwards
9
- around(:each) do |example|
10
- wd = Dir.pwd
11
- Dir.mktmpdir do | td |
12
- Dir.chdir(td)
13
- example.run
14
- end
15
- Dir.chdir(wd)
16
- end
17
-
18
- def rewind_after(*ios)
19
- yield.tap { ios.map(&:rewind) }
20
- end
21
-
22
- it 'raises an InvalidOutput if the given object does not support the methods' do
23
- expect {
24
- described_class.new(nil)
25
- }.to raise_error(ZipTricks::Streamer::InvalidOutput)
26
- end
27
-
28
- it 'allows the writer to be injectable' do
29
- fake_writer = double('ZipWriter')
30
- expect(fake_writer).to receive(:write_local_file_header)
31
- expect(fake_writer).to receive(:write_data_descriptor)
32
- expect(fake_writer).to receive(:write_central_directory_file_header)
33
- expect(fake_writer).to receive(:write_end_of_central_directory)
34
-
35
- described_class.open('', writer: fake_writer) do |zip|
36
- zip.write_deflated_file('stored.txt') do |sink|
37
- sink << File.read(__dir__ + '/war-and-peace.txt')
38
- end
39
- end
40
- end
41
-
42
- it 'returns the position in the IO at every call' do
43
- io = StringIO.new
44
- zip = described_class.new(io)
45
- pos = zip.add_compressed_entry(filename: 'file.jpg', uncompressed_size: 182919, compressed_size: 8912, crc32: 8912)
46
- expect(pos).to eq(io.tell)
47
- expect(pos).to eq(38)
48
-
49
- retval = zip << SecureRandom.random_bytes(8912)
50
- expect(retval).to eq(zip)
51
- expect(io.tell).to eq(8950)
52
-
53
- pos = zip.add_stored_entry(filename: 'filf.jpg', size: 8921, crc32: 182919)
54
- expect(pos).to eq(8988)
55
- zip << SecureRandom.random_bytes(8921)
56
- expect(io.tell).to eq(17909)
57
-
58
- pos = zip.close
59
- expect(pos).to eq(io.tell)
60
- expect(pos).to eq(18068)
61
- end
62
-
63
- it 'can write and then read the block-deflated files' do
64
- f = Tempfile.new('raw')
65
- f.binmode
66
-
67
- rewind_after(f) do
68
- f << ('A' * 1024 * 1024)
69
- f << SecureRandom.random_bytes(1248)
70
- f << ('B' * 1024 * 1024)
71
- end
72
-
73
- crc = rewind_after(f) { Zlib.crc32(f.read) }
74
-
75
- compressed_blockwise = StringIO.new
76
- rewind_after(compressed_blockwise, f) do
77
- ZipTricks::BlockDeflate.deflate_in_blocks_and_terminate(f, compressed_blockwise, block_size: 1024)
78
- end
79
-
80
- # Perform the zipping
81
- zip_file = Tempfile.new('z')
82
- zip_file.binmode
83
-
84
- described_class.open(zip_file) do |zip|
85
- zip.add_compressed_entry(filename: "compressed-file.bin", uncompressed_size: f.size,
86
- crc32: crc, compressed_size: compressed_blockwise.size)
87
- zip << compressed_blockwise.read
88
- end
89
- zip_file.flush
90
-
91
- per_filename = {}
92
- Zip::File.open(zip_file.path) do |zip_file|
93
- # Handle entries one by one
94
- zip_file.each do |entry|
95
- # The entry name gets returned with a binary encoding, we have to force it back.
96
- per_filename[entry.name] = entry.get_input_stream.read
97
- end
98
- end
99
-
100
- expect(per_filename['compressed-file.bin'].bytesize).to eq(f.size)
101
- expect(Digest::SHA1.hexdigest(per_filename['compressed-file.bin'])).to eq(Digest::SHA1.hexdigest(f.read))
102
-
103
- inspect_zip_with_external_tool(zip_file.path)
104
- end
105
-
106
- it 'creates an archive that OSX ArchiveUtility can handle' do
107
- outbuf = Tempfile.new('zip')
108
- outbuf.binmode
109
-
110
- zip = ZipTricks::Streamer.new(outbuf)
111
-
112
- File.open(test_text_file_path, 'rb') do | source_f |
113
- crc32 = rewind_after(source_f) { Zlib.crc32(source_f.read) }
114
-
115
- compressed_buffer = StringIO.new
116
-
117
- expect(ZipTricks::BlockDeflate).to receive(:deflate_chunk).at_least(:twice).and_call_original
118
-
119
- # Compress in blocks of 4 Kb
120
- rewind_after(source_f, compressed_buffer) do
121
- ZipTricks::BlockDeflate.deflate_in_blocks_and_terminate(source_f, compressed_buffer, block_size: 1024 * 4)
122
- end
123
-
124
- # Add this file compressed...
125
- zip.add_compressed_entry(filename: 'war-and-peace.txt', uncompressed_size: source_f.size,
126
- crc32: crc32, compressed_size: compressed_buffer.size)
127
- zip << compressed_buffer.string
128
-
129
- # ...and stored.
130
- zip.add_stored_entry(filename: 'war-and-peace-raw.txt', size: source_f.size, crc32: crc32)
131
- zip << source_f.read
132
-
133
- zip.close
134
-
135
- outbuf.flush
136
- File.unlink('test.zip') rescue nil
137
- File.rename(outbuf.path, 'osx-archive-test.zip')
138
-
139
- # Mark this test as skipped if the system does not have the binary
140
- open_zip_with_archive_utility('osx-archive-test.zip', skip_if_missing: true)
141
- end
142
- FileUtils.rm_rf('osx-archive-test')
143
- FileUtils.rm_rf('osx-archive-test.zip')
144
- end
145
-
146
- it 'archives files which can then be read using the usual means with Rubyzip' do
147
- zip_buf = Tempfile.new('zipp')
148
- zip_buf.binmode
149
- output_io = double('IO')
150
-
151
- # Only allow the methods we provide in BlockWrite.
152
- # Will raise an error if other methods are triggered (the ones that
153
- # might try to rewind the IO).
154
- allow(output_io).to receive(:<<) {|data|
155
- zip_buf << data.to_s.force_encoding(Encoding::BINARY)
156
- }
157
-
158
- allow(output_io).to receive(:tell) { zip_buf.tell }
159
- allow(output_io).to receive(:pos) { zip_buf.pos }
160
- allow(output_io).to receive(:close)
161
-
162
- # Generate a couple of random files
163
- raw_file_1 = SecureRandom.random_bytes(1024 * 20)
164
- raw_file_2 = SecureRandom.random_bytes(1024 * 128)
165
-
166
- # Perform the zipping
167
- zip = described_class.new(output_io)
168
- zip.add_stored_entry(filename: "first-file.bin", size: raw_file_1.size, crc32: Zlib.crc32(raw_file_1))
169
- zip << raw_file_1
170
- zip.add_stored_entry(filename: "second-file.bin", size: raw_file_2.size, crc32: Zlib.crc32(raw_file_2))
171
- zip << raw_file_2
172
- zip.close
173
-
174
- zip_buf.flush
175
-
176
- per_filename = {}
177
- Zip::File.open(zip_buf.path) do |zip_file|
178
- # Handle entries one by one
179
- zip_file.each do |entry|
180
- # The entry name gets returned with a binary encoding, we have to force it back.
181
- # Somehow an empty string gets read
182
- per_filename[entry.name] = entry.get_input_stream.read
183
- end
184
- end
185
-
186
- expect(per_filename['first-file.bin'].unpack("C*")).to eq(raw_file_1.unpack("C*"))
187
- expect(per_filename['second-file.bin'].unpack("C*")).to eq(raw_file_2.unpack("C*"))
188
-
189
- wd = Dir.pwd
190
- Dir.mktmpdir do | td |
191
- Dir.chdir(td)
192
- inspect_zip_with_external_tool(zip_buf.path)
193
- end
194
- Dir.chdir(wd)
195
- end
196
-
197
- it 'sets the general-purpose flag for entries with UTF8 names' do
198
- zip_buf = Tempfile.new('zipp')
199
- zip_buf.binmode
200
-
201
- # Generate a couple of random files
202
- raw_file_1 = SecureRandom.random_bytes(1024 * 20)
203
- raw_file_2 = SecureRandom.random_bytes(1024 * 128)
204
-
205
- # Perform the zipping
206
- zip = described_class.new(zip_buf)
207
- zip.add_stored_entry(filename: "first-file.bin", size: raw_file_1.size, crc32: Zlib.crc32(raw_file_1))
208
- zip << raw_file_1
209
- zip.add_stored_entry(filename: "второй-файл.bin", size: raw_file_2.size, crc32: Zlib.crc32(raw_file_2))
210
- IO.copy_stream(StringIO.new(raw_file_2), zip)
211
- zip.close
212
-
213
- zip_buf.flush
214
-
215
- entries = []
216
- Zip::File.open(zip_buf.path) do |zip_file|
217
- # Handle entries one by one
218
- zip_file.each {|entry| entries << entry }
219
- first_entry, second_entry = entries
220
-
221
- expect(first_entry.gp_flags).to eq(0)
222
- expect(first_entry.name).to eq('first-file.bin')
223
-
224
- # Rubyzip does not properly set the encoding of the entries it reads
225
- expect(second_entry.gp_flags).to eq(2048)
226
- expect(second_entry.name).to eq("второй-файл.bin".force_encoding(Encoding::BINARY))
227
- end
228
- end
229
-
230
- it 'creates an archive with data descriptors that can be opened by Rubyzip, with a small number of very tiny text files' do
231
- tf = ManagedTempfile.new('zip')
232
- z = described_class.open(tf) do |zip|
233
- zip.write_stored_file('deflated.txt') do |sink|
234
- sink << File.read(__dir__ + '/war-and-peace.txt')
235
- end
236
- zip.write_deflated_file('stored.txt') do |sink|
237
- sink << File.read(__dir__ + '/war-and-peace.txt')
238
- end
239
- end
240
- tf.flush
241
-
242
- pending 'https://github.com/rubyzip/rubyzip/issues/295'
243
-
244
- Zip::File.foreach(tf.path) do |entry|
245
- # Make sure it is tagged as UNIX
246
- expect(entry.fstype).to eq(3)
247
-
248
- # The CRC
249
- expect(entry.crc).to eq(Zlib.crc32(File.read(__dir__ + '/war-and-peace.txt')))
250
-
251
- # Check the name
252
- expect(entry.name).to match(/\.txt$/)
253
-
254
- # Check the right external attributes (non-executable on UNIX)
255
- expect(entry.external_file_attributes).to eq(2175008768)
256
-
257
- # Check the file contents
258
- readback = entry.get_input_stream.read
259
- readback.force_encoding(Encoding::BINARY)
260
- expect(readback[0..10]).to eq(File.read(__dir__ + '/war-and-peace.txt')[0..10])
261
- end
262
-
263
- inspect_zip_with_external_tool(tf.path)
264
- end
265
-
266
- it 'can create a valid ZIP archive without any files' do
267
- tf = ManagedTempfile.new('zip')
268
-
269
- described_class.open(tf) do |zip|
270
- end
271
-
272
- tf.flush
273
- tf.rewind
274
-
275
- expect { |b|
276
- Zip::File.foreach(tf.path, &b)
277
- }.not_to yield_control
278
- end
279
-
280
- it 'prevents duplicates in the stored files' do
281
- files = ["README", "README", "file.one\\two.jpg", "file_one.jpg", "file_one (1).jpg",
282
- "file\\one.jpg", "My.Super.file.txt.zip", "My.Super.file.txt.zip"]
283
- fake_writer = double('Writer').as_null_object
284
- seen_filenames = []
285
- allow(fake_writer).to receive(:write_local_file_header) {|filename:, **others|
286
- seen_filenames << filename
287
- }
288
- zip_streamer = described_class.new(StringIO.new, writer: fake_writer)
289
- files.each do |fn|
290
- zip_streamer.add_stored_entry(filename: fn, size: 1024, crc32: 0xCC)
291
- end
292
- expect(seen_filenames).to eq(["README", "README (1)", "file.one_two.jpg", "file_one.jpg",
293
- "file_one (1).jpg", "file_one (2).jpg", "My.Super.file.txt.zip",
294
- "My.Super.file (1).txt.zip"])
295
- end
296
- end