ffi-libarchive 0.1.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/History.txt +4 -0
- data/README.md +70 -0
- data/Rakefile +18 -0
- data/bin/ffi-libarchive-ruby +7 -0
- data/lib/ffi-libarchive.rb +60 -0
- data/lib/ffi-libarchive/archive.rb +306 -0
- data/lib/ffi-libarchive/entry.rb +482 -0
- data/lib/ffi-libarchive/reader.rb +131 -0
- data/lib/ffi-libarchive/stat.rb +31 -0
- data/lib/ffi-libarchive/writer.rb +143 -0
- data/test/data/test.tar.gz +0 -0
- data/test/sets/ts_read.rb +119 -0
- data/test/sets/ts_write.rb +133 -0
- data/test/test_ffi-libarchive.rb +8 -0
- data/version.txt +1 -0
- metadata +91 -0
@@ -0,0 +1,131 @@
|
|
1
|
+
module Archive
|
2
|
+
|
3
|
+
# TODO: Remove this forward-declaration
|
4
|
+
class BaseArchive
|
5
|
+
end
|
6
|
+
|
7
|
+
class Reader < BaseArchive
|
8
|
+
|
9
|
+
private_class_method :new
|
10
|
+
|
11
|
+
def self.open_filename file_name, command = nil
|
12
|
+
if block_given?
|
13
|
+
reader = open_filename file_name, command
|
14
|
+
begin
|
15
|
+
yield reader
|
16
|
+
ensure
|
17
|
+
reader.close
|
18
|
+
end
|
19
|
+
else
|
20
|
+
new :file_name => file_name, :command => command
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.open_memory string, command = nil
|
25
|
+
if block_given?
|
26
|
+
reader = open_memory string, command
|
27
|
+
begin
|
28
|
+
yield reader
|
29
|
+
ensure
|
30
|
+
reader.close
|
31
|
+
end
|
32
|
+
else
|
33
|
+
new :memory => string, :command => command
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize params = {}
|
38
|
+
super C::method(:archive_read_new), C::method(:archive_read_finish)
|
39
|
+
|
40
|
+
if params[:command]
|
41
|
+
cmd = params[:command]
|
42
|
+
raise Error, @archive if C::archive_read_support_compression_program(archive, cmd) != C::OK
|
43
|
+
else
|
44
|
+
raise Error, @archive if C::archive_read_support_compression_all(archive) != C::OK
|
45
|
+
end
|
46
|
+
|
47
|
+
raise Error, @archive if C::archive_read_support_format_all(archive) != C::OK
|
48
|
+
|
49
|
+
if params[:file_name]
|
50
|
+
raise Error, @archive if C::archive_read_open_filename(archive, params[:file_name], 1024) != C::OK
|
51
|
+
elsif params[:memory]
|
52
|
+
str = params[:memory]
|
53
|
+
@data = FFI::MemoryPointer.new str.size
|
54
|
+
@data.put_bytes 0, str
|
55
|
+
raise Error, @archive if C::archive_read_open_memory(archive, @data, @data.size) != C::OK
|
56
|
+
end
|
57
|
+
rescue
|
58
|
+
close
|
59
|
+
raise
|
60
|
+
end
|
61
|
+
|
62
|
+
def extract entry, flags = 0
|
63
|
+
raise ArgumentError, "Expected Archive::Entry as first argument" unless entry.kind_of? Entry
|
64
|
+
raise ArgumentError, "Expected Integer as second argument" unless entry.kind_of? Integer
|
65
|
+
|
66
|
+
flags &= EXTRACT_FLAGS
|
67
|
+
raise Error, @archive if C::archive_read_extract(archive, entry, flags) != C::OK
|
68
|
+
end
|
69
|
+
|
70
|
+
def header_position
|
71
|
+
raise Error, @archive if C::archive_read_header_position archive
|
72
|
+
end
|
73
|
+
|
74
|
+
def next_header
|
75
|
+
entry_ptr = FFI::MemoryPointer.new(:pointer)
|
76
|
+
case C::archive_read_next_header(archive, entry_ptr)
|
77
|
+
when C::OK
|
78
|
+
Entry.from_pointer entry_ptr.read_pointer
|
79
|
+
when C::EOF
|
80
|
+
@eof = true
|
81
|
+
nil
|
82
|
+
else
|
83
|
+
raise Error, @archive
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def each_entry
|
88
|
+
while entry = next_header
|
89
|
+
yield entry
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def each_entry_with_data( size = C::DATA_BUFFER_SIZE )
|
94
|
+
while entry = next_header
|
95
|
+
yield entry, read_data
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def read_data size = C::DATA_BUFFER_SIZE, &block
|
100
|
+
raise ArgumentError, "Buffer size must be > 0 (was: #{size})" unless size.kind_of?(Integer) and size > 0
|
101
|
+
|
102
|
+
data = nil
|
103
|
+
unless block
|
104
|
+
data = ""
|
105
|
+
block = data.method :concat
|
106
|
+
end
|
107
|
+
|
108
|
+
buffer = FFI::Buffer.alloc_out(size)
|
109
|
+
len = 0
|
110
|
+
while (n = C::archive_read_data(archive, buffer, size)) > 0
|
111
|
+
case n
|
112
|
+
when C::FATAL, C::WARN, C::RETRY
|
113
|
+
raise Error, @archive
|
114
|
+
else
|
115
|
+
block.call buffer.get_bytes(0,n)
|
116
|
+
end
|
117
|
+
len += n
|
118
|
+
end
|
119
|
+
|
120
|
+
data || len
|
121
|
+
end
|
122
|
+
|
123
|
+
def save_data file_name
|
124
|
+
IO.sysopen(file_name, "wb") do |fd|
|
125
|
+
raise Error, @archive if C::archive_read_data_into_fd(archive, fd) != C::OK
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'ffi-inliner'
|
2
|
+
|
3
|
+
module Archive
|
4
|
+
module Stat
|
5
|
+
extend Inliner
|
6
|
+
inline do |builder|
|
7
|
+
builder.include 'stdlib.h'
|
8
|
+
builder.include 'sys/types.h'
|
9
|
+
builder.include 'sys/stat.h'
|
10
|
+
builder.c %q{
|
11
|
+
void* ffi_libarchive_create_stat(const char* filename) {
|
12
|
+
struct stat* s = malloc(sizeof(struct stat));
|
13
|
+
if (stat(filename, s) != 0) return NULL;
|
14
|
+
return s;
|
15
|
+
}
|
16
|
+
}
|
17
|
+
builder.c %q{
|
18
|
+
void* ffi_libarchive_create_lstat(const char* filename) {
|
19
|
+
struct stat* s = malloc(sizeof(struct stat));
|
20
|
+
lstat(filename, s);
|
21
|
+
return s;
|
22
|
+
}
|
23
|
+
}
|
24
|
+
builder.c %q{
|
25
|
+
void ffi_libarchive_free_stat(void* s) {
|
26
|
+
free((struct stat*)s);
|
27
|
+
}
|
28
|
+
}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
module Archive
|
2
|
+
|
3
|
+
# TODO: Remove this forward-declaration
|
4
|
+
class BaseArchive
|
5
|
+
end
|
6
|
+
|
7
|
+
class Writer < BaseArchive
|
8
|
+
|
9
|
+
private_class_method :new
|
10
|
+
|
11
|
+
def self.open_filename file_name, compression, format
|
12
|
+
if block_given?
|
13
|
+
writer = open_filename file_name, compression, format
|
14
|
+
begin
|
15
|
+
yield writer
|
16
|
+
ensure
|
17
|
+
writer.close
|
18
|
+
end
|
19
|
+
else
|
20
|
+
new :file_name => file_name, :compression => compression, :format => format
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.open_memory string, compression, format
|
25
|
+
if block_given?
|
26
|
+
writer = open_memory string, compression, format
|
27
|
+
begin
|
28
|
+
yield writer
|
29
|
+
ensure
|
30
|
+
writer.close
|
31
|
+
end
|
32
|
+
else
|
33
|
+
if compression.kind_of? String
|
34
|
+
command = compression
|
35
|
+
compression = -1
|
36
|
+
else
|
37
|
+
command = nil
|
38
|
+
end
|
39
|
+
new :memory => string, :compression => compression, :format => format
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def initialize params = {}
|
44
|
+
super C::method(:archive_write_new), C::method(:archive_write_finish)
|
45
|
+
|
46
|
+
compression = params[:compression]
|
47
|
+
case compression
|
48
|
+
when Symbol
|
49
|
+
compression = Archive::const_get("COMPRESSION_#{compression.to_s.upcase}".intern)
|
50
|
+
end
|
51
|
+
|
52
|
+
format = params[:format]
|
53
|
+
case format
|
54
|
+
when Symbol
|
55
|
+
format = Archive::const_get("FORMAT_#{format.to_s.upcase}".intern)
|
56
|
+
end
|
57
|
+
|
58
|
+
raise Error, @archive if C::archive_write_set_compression(archive, compression) != C::OK
|
59
|
+
|
60
|
+
raise Error, @archive if C::archive_write_set_format(archive, format) != C::OK
|
61
|
+
|
62
|
+
if params[:file_name]
|
63
|
+
raise Error, @archive if C::archive_write_open_filename(archive, params[:file_name]) != C::OK
|
64
|
+
elsif params[:memory]
|
65
|
+
@data = write_callback params[:memory]
|
66
|
+
raise Error, @archive if C::archive_write_open(archive, nil,
|
67
|
+
method(:open_callback),
|
68
|
+
@data,
|
69
|
+
nil) != C::OK
|
70
|
+
end
|
71
|
+
rescue => e
|
72
|
+
close
|
73
|
+
raise
|
74
|
+
end
|
75
|
+
|
76
|
+
def open_callback archive, client
|
77
|
+
if C::archive_write_get_bytes_in_last_block(archive) == -1
|
78
|
+
C::archive_write_set_bytes_in_last_block(archive, 1)
|
79
|
+
end
|
80
|
+
C::OK
|
81
|
+
end
|
82
|
+
private :open_callback
|
83
|
+
|
84
|
+
def write_callback data
|
85
|
+
Proc.new { |ar, client, buffer, length|
|
86
|
+
data.concat buffer.get_bytes(0,length)
|
87
|
+
length
|
88
|
+
}
|
89
|
+
end
|
90
|
+
private :write_callback
|
91
|
+
|
92
|
+
def new_entry
|
93
|
+
entry = Entry.new
|
94
|
+
if block_given?
|
95
|
+
begin
|
96
|
+
result = yield entry
|
97
|
+
ensure
|
98
|
+
entry.close
|
99
|
+
end
|
100
|
+
result
|
101
|
+
else
|
102
|
+
entry
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def add_entry &block
|
107
|
+
raise ArgumentError, "No block given" unless block_given?
|
108
|
+
|
109
|
+
entry = Entry.new
|
110
|
+
data = yield entry
|
111
|
+
write_header entry
|
112
|
+
write_data data if data
|
113
|
+
ensure
|
114
|
+
entry.close
|
115
|
+
end
|
116
|
+
|
117
|
+
def write_data *args
|
118
|
+
if block_given?
|
119
|
+
raise ArgumentError, "wrong number of argument (#{args.size} for 0)" if args.size > 0
|
120
|
+
|
121
|
+
ar = archive
|
122
|
+
len = 0
|
123
|
+
while true do
|
124
|
+
str = yield
|
125
|
+
if ((n = C::archive_write_data(ar, str, str.size)) < 1)
|
126
|
+
return len
|
127
|
+
end
|
128
|
+
len += n
|
129
|
+
end
|
130
|
+
else
|
131
|
+
raise ArgumentError, "wrong number of argument (#{args.size}) for 1)" if args.size != 1
|
132
|
+
str = args[0]
|
133
|
+
C::archive_write_data(archive, str, str.size)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def write_header entry
|
138
|
+
raise Error, @archive if C::archive_write_header(archive, entry.entry) != C::OK
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
Binary file
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'ffi-libarchive'
|
2
|
+
require 'tmpdir'
|
3
|
+
require 'test/unit'
|
4
|
+
|
5
|
+
class TS_ReadArchive < Test::Unit::TestCase
|
6
|
+
|
7
|
+
CONTENT_SPEC =
|
8
|
+
[
|
9
|
+
['test/', 'directory', 0755, nil ],
|
10
|
+
['test/b/', 'directory', 0755, nil ],
|
11
|
+
['test/b/c/', 'directory', 0755, nil ],
|
12
|
+
['test/b/c/c.dat', 'file', 0600, "\266\262\v_\266\243\305\3601\204\277\351\354\265\003\036\036\365f\377\210\205\032\222\346\370b\360u\032Y\301" ],
|
13
|
+
['test/b/c/d/', 'directory', 0711, nil ],
|
14
|
+
['test/b/c/d/d.dat', 'symbolic_link', 0777, "../c.dat" ],
|
15
|
+
['test/b/b.dat', 'file', 0640, "s&\245\354(M\331=\270\000!s\355\240\252\355'N\304\343\bY\317\t\274\210\3128\321\347\234!" ],
|
16
|
+
['test/a.dat', 'file', 0777, "\021\216\231Y\354\236\271\372\336\213\224R\211{D{\277\262\304\211xu\330\\\275@~\035\vSRM" ]
|
17
|
+
]
|
18
|
+
|
19
|
+
def setup
|
20
|
+
File.open('data/test.tar.gz', 'rb') do |f|
|
21
|
+
@archive_content = f.read
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
def test_read_tar_gz_from_file
|
27
|
+
Archive.read_open_filename('data/test.tar.gz') do |ar|
|
28
|
+
verify_content(ar)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_read_tar_gz_from_file_with_external_gunzip
|
33
|
+
Archive.read_open_filename('data/test.tar.gz', 'gunzip') do |ar|
|
34
|
+
verify_content(ar)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_read_tar_gz_from_memory
|
39
|
+
Archive.read_open_memory(@archive_content) do |ar|
|
40
|
+
verify_content(ar)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_read_tar_gz_from_memory_with_external_gunzip
|
45
|
+
Archive.read_open_memory(@archive_content, 'gunzip') do |ar|
|
46
|
+
verify_content(ar)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_read_entry_bigger_than_internal_buffer
|
51
|
+
alphabet = "abcdefghijklmnopqrstuvwxyz"
|
52
|
+
entry_size = 1024 * 4 - 3
|
53
|
+
|
54
|
+
srand
|
55
|
+
content = ""
|
56
|
+
1.upto(entry_size) do |i|
|
57
|
+
index = rand(alphabet.size)
|
58
|
+
content += alphabet[index,1]
|
59
|
+
end
|
60
|
+
|
61
|
+
Dir.mktmpdir do |dir|
|
62
|
+
Archive.write_open_filename(dir + '/test.tar.gz',
|
63
|
+
Archive::COMPRESSION_BZIP2, Archive::FORMAT_TAR) do |ar|
|
64
|
+
ar.new_entry do |entry|
|
65
|
+
entry.pathname = "chubby.dat"
|
66
|
+
entry.mode = 0666
|
67
|
+
entry.filetype = Archive::Entry::FILE
|
68
|
+
entry.atime = Time.now.to_i
|
69
|
+
entry.mtime = Time.now.to_i
|
70
|
+
entry.size = entry_size
|
71
|
+
ar.write_header(entry)
|
72
|
+
ar.write_data(content)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
Archive.read_open_filename(dir + '/test.tar.gz') do |ar|
|
77
|
+
entry = ar.next_header
|
78
|
+
data = ar.read_data
|
79
|
+
|
80
|
+
assert_equal entry_size, data.size
|
81
|
+
assert_equal content.size, data.size
|
82
|
+
assert_equal content, data
|
83
|
+
end
|
84
|
+
|
85
|
+
Archive.read_open_filename(dir + '/test.tar.gz') do |ar|
|
86
|
+
entry = ar.next_header
|
87
|
+
data = ""
|
88
|
+
ar.read_data(128) { |chunk| data += chunk }
|
89
|
+
|
90
|
+
assert_equal content, data
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def verify_content(ar)
|
98
|
+
content_spec_idx = 0
|
99
|
+
|
100
|
+
while entry = ar.next_header
|
101
|
+
expect_pathname, expect_type, expect_mode, expect_content =\
|
102
|
+
CONTENT_SPEC[content_spec_idx]
|
103
|
+
|
104
|
+
assert_equal expect_pathname, entry.pathname
|
105
|
+
assert_equal entry.send("#{expect_type}?"), true
|
106
|
+
assert_equal expect_mode, (entry.mode & 07777)
|
107
|
+
|
108
|
+
if entry.symbolic_link?
|
109
|
+
assert_equal expect_content, entry.symlink
|
110
|
+
elsif entry.file?
|
111
|
+
content = ar.read_data(1024)
|
112
|
+
assert_equal expect_content, content
|
113
|
+
end
|
114
|
+
|
115
|
+
content_spec_idx += 1
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'ffi-libarchive'
|
2
|
+
require 'tmpdir'
|
3
|
+
require 'test/unit'
|
4
|
+
|
5
|
+
class TS_WriteArchive < Test::Unit::TestCase
|
6
|
+
|
7
|
+
CONTENT_SPEC =
|
8
|
+
[
|
9
|
+
['test/', 'directory', 0755, nil ],
|
10
|
+
['test/b/', 'directory', 0755, nil ],
|
11
|
+
['test/b/c/', 'directory', 0755, nil ],
|
12
|
+
['test/b/c/c.dat', 'file', 0600, "\266\262\v_\266\243\305\3601\204\277\351\354\265\003\036\036\365f\377\210\205\032\222\346\370b\360u\032Y\301" ],
|
13
|
+
['test/b/c/d/', 'directory', 0711, nil ],
|
14
|
+
['test/b/c/d/d.dat', 'symbolic_link', 0777, "../c.dat" ],
|
15
|
+
['test/b/b.dat', 'file', 0640, "s&\245\354(M\331=\270\000!s\355\240\252\355'N\304\343\bY\317\t\274\210\3128\321\347\234!" ],
|
16
|
+
['test/a.dat', 'file', 0777, "\021\216\231Y\354\236\271\372\336\213\224R\211{D{\277\262\304\211xu\330\\\275@~\035\vSRM" ]
|
17
|
+
]
|
18
|
+
|
19
|
+
def test_end_to_end_write_read_tar_gz
|
20
|
+
Dir.mktmpdir do |dir|
|
21
|
+
Archive.write_open_filename(dir + '/test.tar.gz', :gzip, :tar) do |ar|
|
22
|
+
write_content(ar)
|
23
|
+
end
|
24
|
+
|
25
|
+
verify_content(dir + '/test.tar.gz')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_end_to_end_write_read_memory
|
30
|
+
memory = ""
|
31
|
+
Archive.write_open_memory(memory, Archive::COMPRESSION_GZIP, Archive::FORMAT_TAR) do |ar|
|
32
|
+
write_content ar
|
33
|
+
end
|
34
|
+
verify_content_memory(memory)
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_end_to_end_write_read_tar_gz_with_external_gzip
|
38
|
+
Dir.mktmpdir do |dir|
|
39
|
+
Archive.write_open_filename(dir + '/test.tar.gz', 'gzip', :tar) do |ar|
|
40
|
+
write_content(ar)
|
41
|
+
end
|
42
|
+
|
43
|
+
verify_content(dir + '/test.tar.gz')
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_end_to_end_write_read_tar_gz
|
48
|
+
Dir.mktmpdir do |dir|
|
49
|
+
Archive.write_open_filename(dir + '/test.tar.gz', :gzip, :tar) do |ar|
|
50
|
+
write_content(ar)
|
51
|
+
end
|
52
|
+
|
53
|
+
verify_content(dir + '/test.tar.gz')
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def write_content(ar)
|
60
|
+
content_spec_idx = 0
|
61
|
+
|
62
|
+
while content_spec_idx < CONTENT_SPEC.size()
|
63
|
+
entry_path, entry_type, entry_mode, entry_content = \
|
64
|
+
CONTENT_SPEC[content_spec_idx]
|
65
|
+
|
66
|
+
ar.new_entry do |entry|
|
67
|
+
entry.pathname = entry_path
|
68
|
+
entry.mode = entry_mode
|
69
|
+
entry.filetype = eval "Archive::Entry::#{entry_type.upcase}"
|
70
|
+
entry.size = entry_content.size if entry_content
|
71
|
+
entry.symlink = entry_content if entry_type == 'symbolic_link'
|
72
|
+
entry.atime = Time.now.to_i
|
73
|
+
entry.mtime = Time.now.to_i
|
74
|
+
ar.write_header(entry)
|
75
|
+
|
76
|
+
if entry_type == 'file'
|
77
|
+
ar.write_data(entry_content)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
content_spec_idx += 1
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def verify_content_memory(memory)
|
86
|
+
Archive.read_open_memory(memory) do |ar|
|
87
|
+
content_spec_idx = 0
|
88
|
+
|
89
|
+
while entry = ar.next_header
|
90
|
+
expect_pathname, expect_type, expect_mode, expect_content =\
|
91
|
+
CONTENT_SPEC[content_spec_idx]
|
92
|
+
|
93
|
+
assert_equal expect_pathname, entry.pathname
|
94
|
+
assert_equal entry.send("#{expect_type}?"), true
|
95
|
+
assert_equal expect_mode, (entry.mode & 07777)
|
96
|
+
|
97
|
+
if entry.symbolic_link?
|
98
|
+
assert_equal expect_content, entry.symlink
|
99
|
+
elsif entry.file?
|
100
|
+
content = ar.read_data(1024)
|
101
|
+
assert_equal expect_content, content
|
102
|
+
end
|
103
|
+
|
104
|
+
content_spec_idx += 1
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def verify_content(filename)
|
110
|
+
Archive.read_open_filename(filename) do |ar|
|
111
|
+
content_spec_idx = 0
|
112
|
+
|
113
|
+
while entry = ar.next_header
|
114
|
+
expect_pathname, expect_type, expect_mode, expect_content =\
|
115
|
+
CONTENT_SPEC[content_spec_idx]
|
116
|
+
|
117
|
+
assert_equal expect_pathname, entry.pathname
|
118
|
+
assert_equal entry.send("#{expect_type}?"), true
|
119
|
+
assert_equal expect_mode, (entry.mode & 07777)
|
120
|
+
|
121
|
+
if entry.symbolic_link?
|
122
|
+
assert_equal expect_content, entry.symlink
|
123
|
+
elsif entry.file?
|
124
|
+
content = ar.read_data(1024)
|
125
|
+
assert_equal expect_content, content
|
126
|
+
end
|
127
|
+
|
128
|
+
content_spec_idx += 1
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|