minitar 0.12.1 → 1.0.0

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.
data/lib/minitar.rb CHANGED
@@ -1,12 +1,300 @@
1
- # coding: utf-8
1
+ require "fileutils"
2
+ require "rbconfig"
2
3
 
3
- require "archive/tar/minitar"
4
+ # == Synopsis
5
+ #
6
+ # Using minitar is easy. The simplest case is:
7
+ #
8
+ # require 'zlib'
9
+ # require 'minitar'
10
+ #
11
+ # # Packs everything that matches Find.find('tests').
12
+ # # test.tar will automatically be closed by Minitar.pack.
13
+ # Minitar.pack('tests', File.open('test.tar', 'wb'))
14
+ #
15
+ # # Unpacks 'test.tar' to 'x', creating 'x' if necessary.
16
+ # Minitar.unpack('test.tar', 'x')
17
+ #
18
+ # A gzipped tar can be written with:
19
+ #
20
+ # # test.tgz will be closed automatically.
21
+ # Minitar.pack('tests', Zlib::GzipWriter.new(File.open('test.tgz', 'wb'))
22
+ #
23
+ # # test.tgz will be closed automatically.
24
+ # Minitar.unpack(Zlib::GzipReader.new(File.open('test.tgz', 'rb')), 'x')
25
+ #
26
+ # As the case above shows, one need not write to a file. However, it will
27
+ # sometimes require that one dive a little deeper into the API, as in the case
28
+ # of StringIO objects. Note that I'm not providing a block with
29
+ # Minitar::Output, as Minitar::Output#close automatically closes both the
30
+ # Output object and the wrapped data stream object.
31
+ #
32
+ # begin
33
+ # sgz = Zlib::GzipWriter.new(StringIO.new(""))
34
+ # tar = Output.new(sgz)
35
+ # Find.find('tests') do |entry|
36
+ # Minitar.pack_file(entry, tar)
37
+ # end
38
+ # ensure
39
+ # # Closes both tar and sgz.
40
+ # tar.close
41
+ # end
42
+ class Minitar
43
+ VERSION = "1.0.0".freeze # :nodoc:
4
44
 
5
- if defined?(::Minitar) && ::Minitar != Archive::Tar::Minitar
6
- warn <<-EOS
7
- ::Minitar is already defined.
8
- This will conflict with future versions of minitar.
9
- EOS
10
- else
11
- ::Minitar = Archive::Tar::Minitar
45
+ # The base class for any minitar error.
46
+ Error = Class.new(::StandardError)
47
+ # Raised when a wrapped data stream class is not seekable.
48
+ NonSeekableStream = Class.new(Error)
49
+ # The exception raised when operations are performed on a stream that has
50
+ # previously been closed.
51
+ ClosedStream = Class.new(Error)
52
+ # The exception raised when a filename exceeds 256 bytes in length, the
53
+ # maximum supported by the standard Tar format.
54
+ FileNameTooLong = Class.new(Error)
55
+ # The exception raised when a data stream ends before the amount of data
56
+ # expected in the archive's PosixHeader.
57
+ UnexpectedEOF = Class.new(StandardError)
58
+ # The exception raised when a file contains a relative path in secure mode
59
+ # (the default for this version).
60
+ SecureRelativePathError = Class.new(Error)
61
+ # The exception raised when a file contains an invalid Posix header.
62
+ InvalidTarStream = Class.new(Error)
12
63
  end
64
+
65
+ class << Minitar
66
+ # Tests if +path+ refers to a directory. Fixes an apparently
67
+ # corrupted <tt>stat()</tt> call on Windows.
68
+ def dir?(path)
69
+ File.directory?((path[-1] == "/") ? path : "#{path}/")
70
+ end
71
+
72
+ # A convenience method for wrapping Minitar::Input.open
73
+ # (mode +r+) and Minitar::Output.open (mode +w+). No other
74
+ # modes are currently supported.
75
+ def open(dest, mode = "r", &)
76
+ case mode
77
+ when "r"
78
+ Input.open(dest, &)
79
+ when "w"
80
+ Output.open(dest, &block)
81
+ else
82
+ raise "Unknown open mode for Minitar.open."
83
+ end
84
+ end
85
+
86
+ def const_missing(c) # :nodoc:
87
+ case c
88
+ when :BlockRequired
89
+ warn "This constant has been removed."
90
+ const_set(:BlockRequired, Class.new(StandardError))
91
+ else
92
+ super
93
+ end
94
+ end
95
+
96
+ def windows? # :nodoc:
97
+ RbConfig::CONFIG["host_os"] =~ /^(mswin|mingw|cygwin)/
98
+ end
99
+
100
+ # A convenience method to pack the provided +data+ as a file named +entry+. +entry+ may
101
+ # either be a name or a Hash with the fields described below. When only a name is
102
+ # provided, or only some Hash fields are provided, the default values will apply.
103
+ #
104
+ # <tt>:name</tt>:: The filename to be packed into the archive. Required.
105
+ # <tt>:mode</tt>:: The mode to be applied. Defaults to 0o644 for files and 0o755 for
106
+ # directories.
107
+ # <tt>:uid</tt>:: The user owner of the file. Default is +nil+.
108
+ # <tt>:gid</tt>:: The group owner of the file. Default is +nil+.
109
+ # <tt>:mtime</tt>:: The modification Time of the file. Default is +Time.now+.
110
+ #
111
+ # If +data+ is +nil+, a directory will be created. Use an empty String for a normal
112
+ # empty file.
113
+ def pack_as_file(entry, data, outputter) # :yields action, name, stats:
114
+ if outputter.is_a?(Minitar::Output)
115
+ outputter = outputter.tar
116
+ end
117
+
118
+ stats = {
119
+ gid: nil,
120
+ uid: nil,
121
+ mtime: Time.now,
122
+ size: data&.size || 0,
123
+ mode: data ? 0o644 : 0o755
124
+ }
125
+
126
+ if entry.is_a?(Hash)
127
+ name = entry.delete(:name)
128
+ entry.each_pair { stats[_1] = _2 unless _2.nil? }
129
+ else
130
+ name = entry
131
+ end
132
+
133
+ if data.nil? # Create a directory
134
+ yield :dir, name, stats if block_given?
135
+ outputter.mkdir(name, stats)
136
+ else
137
+ outputter.add_file_simple(name, stats) do |os|
138
+ stats[:current] = 0
139
+ yield :file_start, name, stats if block_given?
140
+
141
+ StringIO.open(data, "rb") do |ff|
142
+ until ff.eof?
143
+ stats[:currinc] = os.write(ff.read(4096))
144
+ stats[:current] += stats[:currinc]
145
+ yield :file_progress, name, stats if block_given?
146
+ end
147
+ end
148
+
149
+ yield :file_done, name, stats if block_given?
150
+ end
151
+ end
152
+ end
153
+
154
+ # A convenience method to pack the file provided. +entry+ may either be a filename (in
155
+ # which case various values for the file (see below) will be obtained from
156
+ # <tt>File#stat(entry)</tt> or a Hash with the fields:
157
+ #
158
+ # <tt>:name</tt>:: The filename to be packed into the archive. Required.
159
+ # <tt>:mode</tt>:: The mode to be applied.
160
+ # <tt>:uid</tt>:: The user owner of the file. (Ignored on Windows.)
161
+ # <tt>:gid</tt>:: The group owner of the file. (Ignored on Windows.)
162
+ # <tt>:mtime</tt>:: The modification Time of the file.
163
+ #
164
+ # During packing, if a block is provided, #pack_file yields an +action+ Symol, the
165
+ # full name of the file being packed, and a Hash of statistical information, just as
166
+ # with Minitar::Input#extract_entry.
167
+ #
168
+ # The +action+ will be one of:
169
+ # <tt>:dir</tt>:: The +entry+ is a directory.
170
+ # <tt>:file_start</tt>:: The +entry+ is a file; the extract of the
171
+ # file is just beginning.
172
+ # <tt>:file_progress</tt>:: Yielded every 4096 bytes during the extract
173
+ # of the +entry+.
174
+ # <tt>:file_done</tt>:: Yielded when the +entry+ is completed.
175
+ #
176
+ # The +stats+ hash contains the following keys:
177
+ # <tt>:current</tt>:: The current total number of bytes read in the
178
+ # +entry+.
179
+ # <tt>:currinc</tt>:: The current number of bytes read in this read
180
+ # cycle.
181
+ # <tt>:name</tt>:: The filename to be packed into the tarchive.
182
+ # *REQUIRED*.
183
+ # <tt>:mode</tt>:: The mode to be applied.
184
+ # <tt>:uid</tt>:: The user owner of the file. (+nil+ on Windows.)
185
+ # <tt>:gid</tt>:: The group owner of the file. (+nil+ on Windows.)
186
+ # <tt>:mtime</tt>:: The modification Time of the file.
187
+ def pack_file(entry, outputter) # :yields action, name, stats:
188
+ if outputter.is_a?(Minitar::Output)
189
+ outputter = outputter.tar
190
+ end
191
+
192
+ stats = {}
193
+
194
+ if entry.is_a?(Hash)
195
+ name = entry[:name]
196
+ entry.each { |kk, vv| stats[kk] = vv unless vv.nil? }
197
+ else
198
+ name = entry
199
+ end
200
+
201
+ name = name.sub(%r{\./}, "")
202
+ stat = File.stat(name)
203
+ stats[:mode] ||= stat.mode
204
+ stats[:mtime] ||= stat.mtime
205
+ stats[:size] = stat.size
206
+
207
+ if windows?
208
+ stats[:uid] = nil
209
+ stats[:gid] = nil
210
+ else
211
+ stats[:uid] ||= stat.uid
212
+ stats[:gid] ||= stat.gid
213
+ end
214
+
215
+ if File.file?(name)
216
+ outputter.add_file_simple(name, stats) do |os|
217
+ stats[:current] = 0
218
+ yield :file_start, name, stats if block_given?
219
+ File.open(name, "rb") do |ff|
220
+ until ff.eof?
221
+ stats[:currinc] = os.write(ff.read(4096))
222
+ stats[:current] += stats[:currinc]
223
+ yield :file_progress, name, stats if block_given?
224
+ end
225
+ end
226
+ yield :file_done, name, stats if block_given?
227
+ end
228
+ elsif dir?(name)
229
+ yield :dir, name, stats if block_given?
230
+ outputter.mkdir(name, stats)
231
+ else
232
+ raise "Don't yet know how to pack this type of file."
233
+ end
234
+ end
235
+
236
+ # A convenience method to pack files specified by +src+ into +dest+. If
237
+ # +src+ is an Array, then each file detailed therein will be packed into
238
+ # the resulting Minitar::Output stream; if +recurse_dirs+ is
239
+ # true, then directories will be recursed.
240
+ #
241
+ # If +src+ is not an Array, it will be treated as the result of Find.find;
242
+ # all files matching will be packed.
243
+ def pack(src, dest, recurse_dirs = true, &block)
244
+ require "find"
245
+ Minitar::Output.open(dest) do |outp|
246
+ if src.is_a?(Array)
247
+ src.each do |entry|
248
+ if dir?(entry) && recurse_dirs
249
+ Find.find(entry) do |ee|
250
+ pack_file(ee, outp, &block)
251
+ end
252
+ else
253
+ pack_file(entry, outp, &block)
254
+ end
255
+ end
256
+ else
257
+ Find.find(src) do |entry|
258
+ pack_file(entry, outp, &block)
259
+ end
260
+ end
261
+ end
262
+ end
263
+
264
+ # A convenience method to unpack files from +src+ into the directory
265
+ # specified by +dest+. Only those files named explicitly in +files+
266
+ # will be extracted.
267
+ def unpack(src, dest, files = [], options = {}, &block)
268
+ Minitar::Input.open(src) do |inp|
269
+ if File.exist?(dest) && !dir?(dest)
270
+ raise "Can't unpack to a non-directory."
271
+ end
272
+
273
+ FileUtils.mkdir_p(dest) unless File.exist?(dest)
274
+
275
+ inp.each do |entry|
276
+ if files.empty? || files.include?(entry.full_name)
277
+ inp.extract_entry(dest, entry, options, &block)
278
+ end
279
+ end
280
+ end
281
+ end
282
+
283
+ # Check whether +io+ can seek without errors.
284
+ def seekable?(io, methods = nil)
285
+ # The IO class throws an exception at runtime if we try to change
286
+ # position on a non-regular file.
287
+ if io.respond_to?(:stat)
288
+ io.stat.file?
289
+ else
290
+ # Duck-type the rest of this.
291
+ methods ||= [:pos, :pos=, :seek, :rewind]
292
+ methods = [methods] unless methods.is_a?(Array)
293
+ methods.all? { |m| io.respond_to?(m) }
294
+ end
295
+ end
296
+ end
297
+
298
+ require "minitar/posix_header"
299
+ require "minitar/input"
300
+ require "minitar/output"
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  require "fileutils"
4
2
  require "minitar"
5
3
 
@@ -1,13 +1,12 @@
1
- # frozen_string_literal: true
2
-
3
1
  module TarTestHelpers
4
- include Archive::Tar::Minitar::ByteSize
5
-
6
2
  Field = Struct.new(:name, :offset, :length)
7
3
  def self.Field(name, length)
4
+ # standard:disable ThreadSafety/InstanceVariableInClassMethod
8
5
  @offset ||= 0
9
6
  field = Field.new(name, @offset, length)
10
7
  @offset += length
8
+ # standard:enable ThreadSafety/InstanceVariableInClassMethod
9
+
11
10
  FIELDS[name] = field
12
11
  FIELD_ORDER << name
13
12
  field
@@ -51,7 +50,7 @@ module TarTestHelpers
51
50
  offset = FIELDS[field].offset
52
51
  length = FIELDS[field].length
53
52
 
54
- assert_equal(expected[offset, length], actual[offset, length], message)
53
+ assert_equal expected[offset, length], actual[offset, length], message
55
54
  end
56
55
  end
57
56
 
@@ -95,8 +94,8 @@ module TarTestHelpers
95
94
  ]
96
95
 
97
96
  h = arr.join.bytes.to_a.pack("C100C8C8C8C12C12C8CC100C6C2C32C32C8C8C155")
98
- ret = h + "\0" * (512 - bytesize(h))
99
- assert_equal(512, bytesize(ret))
97
+ ret = h + "\0" * (512 - h.bytesize)
98
+ assert_equal 512, ret.bytesize
100
99
  ret
101
100
  end
102
101
 
@@ -116,7 +115,7 @@ module TarTestHelpers
116
115
  end
117
116
 
118
117
  def asciiz(str, length)
119
- str + "\0" * (length - bytesize(str))
118
+ str + "\0" * (length - str.bytesize)
120
119
  end
121
120
 
122
121
  def sp(s)
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "minitar"
4
+ require "minitest_helper"
5
+ require "base64"
6
+ require "zlib"
7
+
8
+ class TestIssue46 < Minitest::Test
9
+ SUPERLONG_TGZ = Base64.decode64(<<~EOS).freeze
10
+ H4sIAK1+smYAA+3WQQ6CMBAF0K49BScAprYd3XkALoECSiQlQYzXt0IkSKLGBdXE
11
+ /zbtNF000PkQRmG0SWq7T0p7FPOIHaNUNzrTkWI5zPt1IiYtgmSm8zw4n9q0CQLR
12
+ 1HX7at/lkOeVjwP5FZNcKm14tU63uyyPUP91/e3rCJ75uF/j/Gej+6yXw/fArbnM
13
+ Z2ZlDKlb/ktNrEQQ+3gA9/xP3aS0z/e5bUXh40B+/Vj+oJ63Xkzff26zoqzmzf13
14
+ /d/98437n0izQf8DAAAAAAAAAAAAAAAAAHziCqQuXDYAKAAA
15
+ EOS
16
+
17
+ FILETIMES = Time.utc(2004).to_i
18
+
19
+ superlong_name = (["0123456789abcde"] * 33).join("/")
20
+
21
+ SUPERLONG_CONTENTS = {
22
+ superlong_name => {size: 496, mode: 0o644},
23
+ "endfile" => {size: 0, mode: 0o644}
24
+ }
25
+
26
+ def test_each_works
27
+ reader = Zlib::GzipReader.new(StringIO.new(SUPERLONG_TGZ))
28
+
29
+ Minitar::Input.open(reader) do |stream|
30
+ outer = 0
31
+ stream.each.with_index do |entry, i|
32
+ assert_kind_of Minitar::Reader::EntryStream, entry
33
+ assert SUPERLONG_CONTENTS.key?(entry.name), "File #{entry.name} not defined"
34
+
35
+ assert_equal SUPERLONG_CONTENTS[entry.name][:size],
36
+ entry.size,
37
+ "File sizes sizes do not match: #{entry.name}"
38
+
39
+ assert_modes_equal(SUPERLONG_CONTENTS[entry.name][:mode],
40
+ entry.mode, entry.name)
41
+ assert_equal FILETIMES, entry.mtime, "entry.mtime"
42
+
43
+ outer += 1
44
+ end
45
+
46
+ assert_equal 2, outer
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "minitar"
4
+ require "minitest_helper"
5
+ require "zlib"
6
+
7
+ class TestMinitar < Minitest::Test
8
+ FILE_2004 = Time.utc(2004).to_i
9
+
10
+ def test_pack_as_file
11
+ input = [
12
+ ["path", nil],
13
+ ["test", "test"],
14
+ ["extra/test", "extra/test"],
15
+ [{name: "empty2004", mtime: FILE_2004, mode: 0o755}, ""]
16
+ ]
17
+
18
+ writer = StringIO.new
19
+ Minitar::Output.open(writer) do |out_stream|
20
+ input.each do |(name, data)|
21
+ Minitar.pack_as_file(name, data, out_stream)
22
+ end
23
+ end
24
+
25
+ expected = [
26
+ {name: "path", size: 0, mode: 0o755},
27
+ {name: "test", size: 4, mode: 0o644, data: "test"},
28
+ {name: "extra/test", size: 10, mode: 0o0644, data: "extra/test"},
29
+ {name: "empty2004", size: 0, mode: 0o755, mtime: FILE_2004, nil: true}
30
+ ]
31
+
32
+ count = 0
33
+ reader = StringIO.new(writer.string)
34
+ Minitar::Input.open(reader) do |stream|
35
+ stream.each.with_index do |entry, i|
36
+ assert_kind_of Minitar::Reader::EntryStream, entry
37
+
38
+ assert_equal expected[i][:name], entry.name
39
+ assert_equal expected[i][:size], entry.size
40
+ assert_equal expected[i][:mode], entry.mode
41
+
42
+ if expected[i].key?(:mtime)
43
+ assert_equal expected[i][:mtime], entry.mtime
44
+ end
45
+
46
+ if expected[i].key?(:data)
47
+ assert_equal expected[i][:data], entry.read
48
+ end
49
+
50
+ if expected[i].key?(:nil)
51
+ assert_nil entry.read
52
+ end
53
+
54
+ count += 1
55
+ end
56
+ end
57
+
58
+ assert_equal expected.size, count
59
+ end
60
+ end
@@ -1,81 +1,79 @@
1
- # frozen_string_literal: true
2
-
3
1
  require "minitest_helper"
4
2
 
5
3
  class TestTarHeader < Minitest::Test
6
4
  def test_arguments_are_checked
7
- ph = Archive::Tar::Minitar::PosixHeader
5
+ ph = Minitar::PosixHeader
8
6
  assert_raises(ArgumentError) {
9
- ph.new(:name => "", :size => "", :mode => "")
7
+ ph.new(name: "", size: "", mode: "")
10
8
  }
11
9
  assert_raises(ArgumentError) {
12
- ph.new(:name => "", :size => "", :prefix => "")
10
+ ph.new(name: "", size: "", prefix: "")
13
11
  }
14
12
  assert_raises(ArgumentError) {
15
- ph.new(:name => "", :prefix => "", :mode => "")
13
+ ph.new(name: "", prefix: "", mode: "")
16
14
  }
17
15
  assert_raises(ArgumentError) {
18
- ph.new(:prefix => "", :size => "", :mode => "")
16
+ ph.new(prefix: "", size: "", mode: "")
19
17
  }
20
18
  end
21
19
 
22
20
  def test_basic_headers
23
21
  header = {
24
- :name => "bla",
25
- :mode => 0o12345,
26
- :size => 10,
27
- :prefix => "",
28
- :typeflag => "0"
22
+ name: "bla",
23
+ mode: 0o12345,
24
+ size: 10,
25
+ prefix: "",
26
+ typeflag: "0"
29
27
  }
30
28
  assert_headers_equal(tar_file_header("bla", "", 0o12345, 10),
31
- Archive::Tar::Minitar::PosixHeader.new(header).to_s)
29
+ Minitar::PosixHeader.new(header).to_s)
32
30
 
33
31
  header = {
34
- :name => "bla",
35
- :mode => 0o12345,
36
- :size => 0,
37
- :prefix => "",
38
- :typeflag => "5"
32
+ name: "bla",
33
+ mode: 0o12345,
34
+ size: 0,
35
+ prefix: "",
36
+ typeflag: "5"
39
37
  }
40
38
  assert_headers_equal(tar_dir_header("bla", "", 0o12345),
41
- Archive::Tar::Minitar::PosixHeader.new(header).to_s)
39
+ Minitar::PosixHeader.new(header).to_s)
42
40
  end
43
41
 
44
42
  def test_long_name_works
45
43
  header = {
46
- :name => "a" * 100, :mode => 0o12345, :size => 10, :prefix => ""
44
+ name: "a" * 100, mode: 0o12345, size: 10, prefix: ""
47
45
  }
48
46
  assert_headers_equal(tar_file_header("a" * 100, "", 0o12345, 10),
49
- Archive::Tar::Minitar::PosixHeader.new(header).to_s)
47
+ Minitar::PosixHeader.new(header).to_s)
50
48
  header = {
51
- :name => "a" * 100, :mode => 0o12345, :size => 10, :prefix => "bb" * 60
49
+ name: "a" * 100, mode: 0o12345, size: 10, prefix: "bb" * 60
52
50
  }
53
51
  assert_headers_equal(tar_file_header("a" * 100, "bb" * 60, 0o12345, 10),
54
- Archive::Tar::Minitar::PosixHeader.new(header).to_s)
52
+ Minitar::PosixHeader.new(header).to_s)
55
53
  end
56
54
 
57
55
  def test_from_stream
58
56
  header = tar_file_header("a" * 100, "", 0o12345, 10)
59
57
  header = StringIO.new(header)
60
- h = Archive::Tar::Minitar::PosixHeader.from_stream(header)
61
- assert_equal("a" * 100, h.name)
62
- assert_equal(0o12345, h.mode)
63
- assert_equal(10, h.size)
64
- assert_equal("", h.prefix)
65
- assert_equal("ustar", h.magic)
58
+ h = Minitar::PosixHeader.from_stream(header)
59
+ assert_equal "a" * 100, h.name
60
+ assert_equal 0o12345, h.mode
61
+ assert_equal 10, h.size
62
+ assert_equal "", h.prefix
63
+ assert_equal "ustar", h.magic
66
64
  end
67
65
 
68
66
  def test_from_stream_with_evil_name
69
67
  header = tar_file_header("a \0" + "\0" * 97, "", 0o12345, 10)
70
68
  header = StringIO.new(header)
71
- h = Archive::Tar::Minitar::PosixHeader.from_stream header
72
- assert_equal("a ", h.name)
69
+ h = Minitar::PosixHeader.from_stream header
70
+ assert_equal "a ", h.name
73
71
  end
74
72
 
75
73
  def test_valid_with_valid_header
76
74
  header = tar_file_header("a" * 100, "", 0o12345, 10)
77
75
  header = StringIO.new(header)
78
- h = Archive::Tar::Minitar::PosixHeader.from_stream header
76
+ h = Minitar::PosixHeader.from_stream header
79
77
 
80
78
  assert(h.valid?)
81
79
  end
@@ -85,7 +83,7 @@ class TestTarHeader < Minitest::Test
85
83
  io = StringIO.new(header)
86
84
 
87
85
  assert_raises(ArgumentError) do
88
- Archive::Tar::Minitar::PosixHeader.from_stream(io)
86
+ Minitar::PosixHeader.from_stream(io)
89
87
  end
90
88
  end
91
89
 
@@ -98,14 +96,14 @@ class TestTarHeader < Minitest::Test
98
96
 
99
97
  header = update_checksum(header)
100
98
  io = StringIO.new(header)
101
- header = Archive::Tar::Minitar::PosixHeader.from_stream(io)
99
+ header = Minitar::PosixHeader.from_stream(io)
102
100
 
103
- assert_equal(651, header.size)
101
+ assert_equal 651, header.size
104
102
  end
105
103
 
106
104
  def test_valid_with_invalid_header
107
105
  header = StringIO.new("testing")
108
- h = Archive::Tar::Minitar::PosixHeader.from_stream header
106
+ h = Minitar::PosixHeader.from_stream header
109
107
 
110
108
  refute(h.valid?)
111
109
  end