memfs 0.4.1 → 0.4.2
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.
- checksums.yaml +7 -0
- data/.rubocop.yml +29 -0
- data/CHANGELOG.md +7 -0
- data/Guardfile +5 -6
- data/README.md +6 -6
- data/Rakefile +9 -1
- data/lib/memfs.rb +6 -4
- data/lib/memfs/dir.rb +17 -21
- data/lib/memfs/fake/entry.rb +2 -2
- data/lib/memfs/fake/file/content.rb +3 -2
- data/lib/memfs/file.rb +54 -116
- data/lib/memfs/file/stat.rb +2 -2
- data/lib/memfs/file_system.rb +11 -13
- data/lib/memfs/io.rb +201 -0
- data/lib/memfs/version.rb +1 -1
- data/memfs.gemspec +17 -17
- data/memfs.png +0 -0
- data/spec/fileutils_spec.rb +233 -228
- data/spec/memfs/dir_spec.rb +76 -76
- data/spec/memfs/fake/directory_spec.rb +20 -20
- data/spec/memfs/fake/entry_spec.rb +24 -24
- data/spec/memfs/fake/file/content_spec.rb +43 -45
- data/spec/memfs/fake/file_spec.rb +14 -14
- data/spec/memfs/fake/symlink_spec.rb +22 -22
- data/spec/memfs/file/stat_spec.rb +314 -314
- data/spec/memfs/file_spec.rb +1636 -970
- data/spec/memfs/file_system_spec.rb +83 -83
- data/spec/memfs_spec.rb +15 -15
- data/spec/spec_helper.rb +17 -1
- metadata +36 -57
data/spec/memfs/file_spec.rb
CHANGED
@@ -2,723 +2,819 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
module MemFs
|
4
4
|
describe File do
|
5
|
-
subject {
|
5
|
+
subject { subject_class.new('/test-file') }
|
6
|
+
let(:subject_class) { MemFs::File }
|
7
|
+
let(:write_subject) { subject_class.new('/test-file', 'w') }
|
6
8
|
|
7
|
-
let(:file) { subject.new('/test-file') }
|
8
9
|
let(:random_string) { ('a'..'z').to_a.sample(10).join }
|
9
10
|
|
10
|
-
before
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
before do
|
12
|
+
_fs.mkdir '/test-dir'
|
13
|
+
_fs.touch '/test-file', '/test-file2'
|
14
|
+
subject_class.symlink '/test-file', '/test-link'
|
15
|
+
subject_class.symlink '/no-file', '/no-link'
|
15
16
|
end
|
16
17
|
|
18
|
+
|
17
19
|
it 'implements Enumerable' do
|
18
|
-
expect(
|
20
|
+
expect(subject_class.ancestors).to include Enumerable
|
19
21
|
end
|
20
22
|
|
21
23
|
describe '.absolute_path' do
|
22
|
-
before
|
23
|
-
MemFs::Dir.chdir('/test-dir')
|
24
|
-
end
|
24
|
+
before { MemFs::Dir.chdir('/test-dir') }
|
25
25
|
|
26
|
-
it
|
27
|
-
path =
|
28
|
-
expect(path).to eq
|
26
|
+
it 'converts a pathname to an absolute pathname' do
|
27
|
+
path = subject_class.absolute_path('./test-file')
|
28
|
+
expect(path).to eq '/test-dir/test-file'
|
29
29
|
end
|
30
30
|
|
31
|
-
context
|
32
|
-
it
|
33
|
-
path =
|
34
|
-
expect(path).to eq
|
31
|
+
context 'when +dir_string+ is given' do
|
32
|
+
it 'uses it as the starting point' do
|
33
|
+
path = subject_class.absolute_path('./test-file', '/no-dir')
|
34
|
+
expect(path).to eq '/no-dir/test-file'
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
38
|
context "when the given pathname starts with a '~'" do
|
39
|
-
it
|
40
|
-
path =
|
41
|
-
expect(path).to eq
|
39
|
+
it 'does not expanded' do
|
40
|
+
path = subject_class.absolute_path('~/test-file')
|
41
|
+
expect(path).to eq '/test-dir/~/test-file'
|
42
42
|
end
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
46
|
describe '.atime' do
|
47
|
-
it
|
48
|
-
expect(
|
47
|
+
it 'returns the last access time for the named file as a Time object' do
|
48
|
+
expect(subject_class.atime('/test-file')).to be_a Time
|
49
49
|
end
|
50
50
|
|
51
|
-
it
|
52
|
-
expect {
|
51
|
+
it 'raises an error if the entry does not exist' do
|
52
|
+
expect { subject_class.atime('/no-file') }.to raise_error Errno::ENOENT
|
53
53
|
end
|
54
54
|
|
55
|
-
context
|
56
|
-
let(:time) { Time.now -
|
55
|
+
context 'when the entry is a symlink' do
|
56
|
+
let(:time) { Time.now - 500_000 }
|
57
|
+
|
58
|
+
it 'returns the last access time of the last target of the link chain' do
|
59
|
+
_fs.find!('/test-file').atime = time
|
60
|
+
subject_class.symlink '/test-link', '/test-link2'
|
57
61
|
|
58
|
-
|
59
|
-
fs.find!('/test-file').atime = time
|
60
|
-
subject.symlink('/test-link', '/test-link2')
|
61
|
-
expect(subject.atime('/test-link2')).to eq(time)
|
62
|
+
expect(subject_class.atime('/test-link2')).to eq time
|
62
63
|
end
|
63
64
|
end
|
64
65
|
end
|
65
66
|
|
66
|
-
describe
|
67
|
-
context
|
68
|
-
context
|
69
|
-
it
|
70
|
-
|
71
|
-
file =
|
67
|
+
describe '.blockdev?' do
|
68
|
+
context 'when the name file exists' do
|
69
|
+
context 'and it is a block device' do
|
70
|
+
it 'returns true' do
|
71
|
+
_fs.touch('/block-file')
|
72
|
+
file = _fs.find('/block-file')
|
72
73
|
file.block_device = true
|
73
|
-
|
74
|
-
|
74
|
+
|
75
|
+
blockdev = subject_class.blockdev?('/block-file')
|
76
|
+
expect(blockdev).to be true
|
75
77
|
end
|
76
78
|
end
|
77
79
|
|
78
|
-
context
|
79
|
-
it
|
80
|
-
blockdev =
|
81
|
-
expect(blockdev).to
|
80
|
+
context 'and it is not a block device' do
|
81
|
+
it 'returns false' do
|
82
|
+
blockdev = subject_class.blockdev?('/test-file')
|
83
|
+
expect(blockdev).to be false
|
82
84
|
end
|
83
85
|
end
|
84
86
|
end
|
85
87
|
|
86
|
-
context
|
87
|
-
it
|
88
|
-
blockdev =
|
89
|
-
expect(blockdev).to
|
88
|
+
context 'when the name file does not exist' do
|
89
|
+
it 'returns false' do
|
90
|
+
blockdev = subject_class.blockdev?('/no-file')
|
91
|
+
expect(blockdev).to be false
|
90
92
|
end
|
91
93
|
end
|
92
94
|
end
|
93
95
|
|
94
|
-
describe
|
95
|
-
it
|
96
|
-
|
96
|
+
describe '.basename' do
|
97
|
+
it 'returns the last component of the filename given in +file_name+' do
|
98
|
+
basename = subject_class.basename('/path/to/file.txt')
|
99
|
+
expect(basename).to eq 'file.txt'
|
97
100
|
end
|
98
101
|
|
99
|
-
context
|
100
|
-
context
|
101
|
-
it
|
102
|
-
|
102
|
+
context 'when +suffix+ is given' do
|
103
|
+
context 'when it is present at the end of +file_name+' do
|
104
|
+
it 'removes the +suffix+ from the filename basename' do
|
105
|
+
basename = subject_class.basename('/path/to/file.txt', '.txt')
|
106
|
+
expect(basename).to eq 'file'
|
103
107
|
end
|
104
108
|
end
|
105
109
|
end
|
106
110
|
end
|
107
111
|
|
108
|
-
describe
|
109
|
-
context
|
110
|
-
context
|
111
|
-
it
|
112
|
-
|
113
|
-
file =
|
112
|
+
describe '.chardev?' do
|
113
|
+
context 'when the name file exists' do
|
114
|
+
context 'and it is a character device' do
|
115
|
+
it 'returns true' do
|
116
|
+
_fs.touch '/character-file'
|
117
|
+
file = _fs.find('/character-file')
|
114
118
|
file.character_device = true
|
115
|
-
|
116
|
-
|
119
|
+
|
120
|
+
chardev = subject_class.chardev?('/character-file')
|
121
|
+
expect(chardev).to be true
|
117
122
|
end
|
118
123
|
end
|
119
124
|
|
120
|
-
context
|
121
|
-
it
|
122
|
-
chardev =
|
123
|
-
expect(chardev).to
|
125
|
+
context 'and it is not a character device' do
|
126
|
+
it 'returns false' do
|
127
|
+
chardev = subject_class.chardev?('/test-file')
|
128
|
+
expect(chardev).to be false
|
124
129
|
end
|
125
130
|
end
|
126
131
|
end
|
127
132
|
|
128
|
-
context
|
129
|
-
it
|
130
|
-
chardev =
|
131
|
-
expect(chardev).to
|
133
|
+
context 'when the name file does not exist' do
|
134
|
+
it 'returns false' do
|
135
|
+
chardev = subject_class.chardev?('/no-file')
|
136
|
+
expect(chardev).to be false
|
132
137
|
end
|
133
138
|
end
|
134
139
|
end
|
135
140
|
|
136
141
|
describe '.chmod' do
|
137
|
-
it
|
138
|
-
|
139
|
-
|
142
|
+
it 'changes permission bits on the named file' do
|
143
|
+
subject_class.chmod 0777, '/test-file'
|
144
|
+
|
145
|
+
mode = subject_class.stat('/test-file').mode
|
146
|
+
expect(mode).to be 0100777
|
140
147
|
end
|
141
148
|
|
142
|
-
it
|
143
|
-
|
144
|
-
|
149
|
+
it 'changes permission bits on the named files (in list)' do
|
150
|
+
subject_class.chmod 0777, '/test-file', '/test-file2'
|
151
|
+
|
152
|
+
mode = subject_class.stat('/test-file2').mode
|
153
|
+
expect(mode).to be 0100777
|
145
154
|
end
|
146
155
|
end
|
147
156
|
|
148
|
-
describe
|
149
|
-
it
|
150
|
-
|
151
|
-
|
157
|
+
describe '.chown' do
|
158
|
+
it 'changes the owner of the named file to the given numeric owner id' do
|
159
|
+
subject_class.chown 42, nil, '/test-file'
|
160
|
+
|
161
|
+
uid = subject_class.stat('/test-file').uid
|
162
|
+
expect(uid).to be 42
|
152
163
|
end
|
153
164
|
|
154
|
-
it
|
155
|
-
|
156
|
-
|
165
|
+
it 'changes owner on the named files (in list)' do
|
166
|
+
subject_class.chown 42, nil, '/test-file', '/test-file2'
|
167
|
+
|
168
|
+
uid = subject_class.stat('/test-file2').uid
|
169
|
+
expect(uid).to be 42
|
157
170
|
end
|
158
171
|
|
159
|
-
it
|
160
|
-
|
161
|
-
|
172
|
+
it 'changes the group of the named file to the given numeric group id' do
|
173
|
+
subject_class.chown nil, 42, '/test-file'
|
174
|
+
|
175
|
+
gid = subject_class.stat('/test-file').gid
|
176
|
+
expect(gid).to be 42
|
162
177
|
end
|
163
178
|
|
164
|
-
it
|
165
|
-
|
179
|
+
it 'returns the number of files' do
|
180
|
+
returned_value = subject_class.chown(42, 42, '/test-file', '/test-file2')
|
181
|
+
expect(returned_value).to be 2
|
166
182
|
end
|
167
183
|
|
168
|
-
it
|
184
|
+
it 'ignores nil user id' do
|
169
185
|
expect {
|
170
|
-
|
171
|
-
}.to_not change{
|
186
|
+
subject_class.chown nil, 42, '/test-file'
|
187
|
+
}.to_not change { subject_class.stat('/test-file').uid }
|
172
188
|
end
|
173
189
|
|
174
|
-
it
|
190
|
+
it 'ignores nil group id' do
|
175
191
|
expect {
|
176
|
-
|
177
|
-
}.to_not change{
|
192
|
+
subject_class.chown 42, nil, '/test-file'
|
193
|
+
}.to_not change { subject_class.stat('/test-file').gid }
|
178
194
|
end
|
179
195
|
|
180
|
-
it
|
196
|
+
it 'ignores -1 user id' do
|
181
197
|
expect {
|
182
|
-
|
183
|
-
}.to_not change{
|
198
|
+
subject_class.chown -1, 42, '/test-file'
|
199
|
+
}.to_not change { subject_class.stat('/test-file').uid }
|
184
200
|
end
|
185
201
|
|
186
|
-
it
|
202
|
+
it 'ignores -1 group id' do
|
187
203
|
expect {
|
188
|
-
|
189
|
-
}.to_not change{
|
204
|
+
subject_class.chown 42, -1, '/test-file'
|
205
|
+
}.to_not change { subject_class.stat('/test-file').gid }
|
190
206
|
end
|
191
207
|
|
192
|
-
context
|
193
|
-
it
|
194
|
-
|
195
|
-
|
208
|
+
context 'when the named entry is a symlink' do
|
209
|
+
it 'changes the owner on the last target of the link chain' do
|
210
|
+
subject_class.chown 42, nil, '/test-link'
|
211
|
+
|
212
|
+
uid = subject_class.stat('/test-file').uid
|
213
|
+
expect(uid).to be 42
|
196
214
|
end
|
197
215
|
|
198
|
-
it
|
199
|
-
|
200
|
-
|
216
|
+
it 'changes the group on the last target of the link chain' do
|
217
|
+
subject_class.chown nil, 42, '/test-link'
|
218
|
+
|
219
|
+
gid = subject_class.stat('/test-file').gid
|
220
|
+
expect(gid).to be 42
|
201
221
|
end
|
202
222
|
|
203
|
-
it
|
204
|
-
|
205
|
-
|
223
|
+
it 'does not change the owner of the symlink' do
|
224
|
+
subject_class.chown(42, nil, '/test-link')
|
225
|
+
|
226
|
+
uid = subject_class.lstat('/test-link').uid
|
227
|
+
expect(uid).not_to be 42
|
206
228
|
end
|
207
229
|
|
208
|
-
it
|
209
|
-
|
210
|
-
|
230
|
+
it 'does not change the group of the symlink' do
|
231
|
+
subject_class.chown nil, 42, '/test-link'
|
232
|
+
|
233
|
+
gid = subject_class.lstat('/test-link').gid
|
234
|
+
expect(gid).not_to be 42
|
211
235
|
end
|
212
236
|
end
|
213
237
|
end
|
214
238
|
|
215
|
-
describe
|
216
|
-
it
|
217
|
-
|
239
|
+
describe '.ctime' do
|
240
|
+
it 'returns the change time for the named file as a Time object' do
|
241
|
+
ctime = subject_class.ctime('/test-file')
|
242
|
+
expect(ctime).to be_a Time
|
218
243
|
end
|
219
244
|
|
220
|
-
it
|
221
|
-
expect {
|
245
|
+
it 'raises an error if the entry does not exist' do
|
246
|
+
expect { subject_class.ctime '/no-file' }.to raise_error Errno::ENOENT
|
222
247
|
end
|
223
248
|
|
224
|
-
context
|
225
|
-
let(:time) { Time.now -
|
249
|
+
context 'when the entry is a symlink' do
|
250
|
+
let(:time) { Time.now - 500_000 }
|
226
251
|
|
227
|
-
it
|
228
|
-
|
229
|
-
|
230
|
-
|
252
|
+
it 'returns the last access time of the last target of the link chain' do
|
253
|
+
_fs.find!('/test-file').ctime = time
|
254
|
+
subject_class.symlink '/test-link', '/test-link2'
|
255
|
+
|
256
|
+
ctime = subject_class.ctime('/test-link2')
|
257
|
+
expect(ctime).to eq time
|
231
258
|
end
|
232
259
|
end
|
233
260
|
end
|
234
261
|
|
235
|
-
describe
|
262
|
+
describe '.delete' do
|
263
|
+
subject { subject_class }
|
264
|
+
|
236
265
|
it_behaves_like 'aliased method', :delete, :unlink
|
237
266
|
end
|
238
267
|
|
239
268
|
describe '.directory?' do
|
240
|
-
context
|
241
|
-
it
|
242
|
-
|
269
|
+
context 'when the named entry is a directory' do
|
270
|
+
it 'returns true' do
|
271
|
+
is_directory = subject_class.directory?('/test-dir')
|
272
|
+
expect(is_directory).to be true
|
243
273
|
end
|
244
274
|
end
|
245
275
|
|
246
|
-
context
|
247
|
-
it
|
248
|
-
|
276
|
+
context 'when the named entry is not a directory' do
|
277
|
+
it 'returns false' do
|
278
|
+
is_directory = subject_class.directory?('/test-file')
|
279
|
+
expect(is_directory).to be false
|
249
280
|
end
|
250
281
|
end
|
251
282
|
end
|
252
283
|
|
253
|
-
describe
|
254
|
-
it
|
255
|
-
|
284
|
+
describe '.dirname' do
|
285
|
+
it 'returns all components of the filename given in +file_name+ except the last one' do
|
286
|
+
dirname = subject_class.dirname('/path/to/some/file.txt')
|
287
|
+
expect(dirname).to eq '/path/to/some'
|
256
288
|
end
|
257
289
|
|
258
|
-
it
|
259
|
-
|
290
|
+
it 'returns / if file_name is /' do
|
291
|
+
dirname = subject_class.dirname('/')
|
292
|
+
expect(dirname).to eq '/'
|
260
293
|
end
|
261
294
|
end
|
262
295
|
|
263
|
-
describe
|
296
|
+
describe '.executable?' do
|
264
297
|
let(:access) { 0 }
|
265
298
|
let(:gid) { 0 }
|
266
299
|
let(:uid) { 0 }
|
267
300
|
|
268
|
-
before
|
269
|
-
|
270
|
-
|
301
|
+
before do
|
302
|
+
subject_class.chmod access, '/test-file'
|
303
|
+
subject_class.chown uid, gid, '/test-file'
|
271
304
|
end
|
272
305
|
|
273
|
-
context
|
274
|
-
it
|
275
|
-
executable =
|
276
|
-
expect(executable).to
|
306
|
+
context 'when the file is not executable by anyone' do
|
307
|
+
it 'return false' do
|
308
|
+
executable = subject_class.executable?('/test-file')
|
309
|
+
expect(executable).to be false
|
277
310
|
end
|
278
311
|
end
|
279
312
|
|
280
|
-
context
|
313
|
+
context 'when the file is user executable' do
|
281
314
|
let(:access) { MemFs::Fake::Entry::UEXEC }
|
282
315
|
|
283
|
-
context
|
284
|
-
before
|
316
|
+
context 'and the current user owns the file' do
|
317
|
+
before { subject_class.chown uid, 0, '/test-file' }
|
318
|
+
|
285
319
|
let(:uid) { Process.euid }
|
286
320
|
|
287
|
-
it
|
288
|
-
executable =
|
289
|
-
expect(executable).to
|
321
|
+
it 'returns true' do
|
322
|
+
executable = subject_class.executable?('/test-file')
|
323
|
+
expect(executable).to be true
|
290
324
|
end
|
291
325
|
end
|
292
326
|
end
|
293
327
|
|
294
|
-
context
|
328
|
+
context 'when the file is group executable' do
|
295
329
|
let(:access) { MemFs::Fake::Entry::GEXEC }
|
296
330
|
|
297
|
-
context
|
331
|
+
context 'and the current user is part of the owner group' do
|
298
332
|
let(:gid) { Process.egid }
|
299
333
|
|
300
|
-
it
|
301
|
-
executable =
|
302
|
-
expect(executable).to
|
334
|
+
it 'returns true' do
|
335
|
+
executable = subject_class.executable?('/test-file')
|
336
|
+
expect(executable).to be true
|
303
337
|
end
|
304
338
|
end
|
305
339
|
end
|
306
340
|
|
307
|
-
context
|
341
|
+
context 'when the file is executable by anyone' do
|
308
342
|
let(:access) { MemFs::Fake::Entry::OEXEC }
|
309
343
|
|
310
|
-
context
|
311
|
-
it
|
312
|
-
executable =
|
313
|
-
expect(executable).to
|
344
|
+
context 'and the user has no specific right on it' do
|
345
|
+
it 'returns true' do
|
346
|
+
executable = subject_class.executable?('/test-file')
|
347
|
+
expect(executable).to be true
|
314
348
|
end
|
315
349
|
end
|
316
350
|
end
|
317
351
|
|
318
|
-
context
|
319
|
-
it
|
320
|
-
executable =
|
321
|
-
expect(executable).to
|
352
|
+
context 'when the file does not exist' do
|
353
|
+
it 'returns false' do
|
354
|
+
executable = subject_class.executable?('/no-file')
|
355
|
+
expect(executable).to be false
|
322
356
|
end
|
323
357
|
end
|
324
358
|
end
|
325
359
|
|
326
|
-
describe
|
360
|
+
describe '.executable_real?' do
|
327
361
|
let(:access) { 0 }
|
328
362
|
let(:gid) { 0 }
|
329
363
|
let(:uid) { 0 }
|
330
364
|
|
331
|
-
before
|
332
|
-
|
333
|
-
|
365
|
+
before do
|
366
|
+
subject_class.chmod access, '/test-file'
|
367
|
+
subject_class.chown uid, gid, '/test-file'
|
334
368
|
end
|
335
369
|
|
336
|
-
context
|
337
|
-
it
|
338
|
-
|
339
|
-
expect(
|
370
|
+
context 'when the file is not executable by anyone' do
|
371
|
+
it 'return false' do
|
372
|
+
executable_real = subject_class.executable_real?('/test-file')
|
373
|
+
expect(executable_real).to be false
|
340
374
|
end
|
341
375
|
end
|
342
376
|
|
343
|
-
context
|
377
|
+
context 'when the file is user executable' do
|
344
378
|
let(:access) { MemFs::Fake::Entry::UEXEC }
|
345
379
|
|
346
|
-
context
|
347
|
-
before(:each) { subject.chown(uid, 0, '/test-file') }
|
380
|
+
context 'and the current user owns the file' do
|
348
381
|
let(:uid) { Process.uid }
|
349
382
|
|
350
|
-
|
351
|
-
|
352
|
-
|
383
|
+
before { subject_class.chown uid, 0, '/test-file' }
|
384
|
+
|
385
|
+
it 'returns true' do
|
386
|
+
executable_real = subject_class.executable_real?('/test-file')
|
387
|
+
expect(executable_real).to be true
|
353
388
|
end
|
354
389
|
end
|
355
390
|
end
|
356
391
|
|
357
|
-
context
|
392
|
+
context 'when the file is group executable' do
|
358
393
|
let(:access) { MemFs::Fake::Entry::GEXEC }
|
359
394
|
|
360
|
-
context
|
395
|
+
context 'and the current user is part of the owner group' do
|
361
396
|
let(:gid) { Process.gid }
|
362
397
|
|
363
|
-
it
|
364
|
-
|
365
|
-
expect(
|
398
|
+
it 'returns true' do
|
399
|
+
executable_real = subject_class.executable_real?('/test-file')
|
400
|
+
expect(executable_real).to be true
|
366
401
|
end
|
367
402
|
end
|
368
403
|
end
|
369
404
|
|
370
|
-
context
|
405
|
+
context 'when the file is executable by anyone' do
|
371
406
|
let(:access) { MemFs::Fake::Entry::OEXEC }
|
372
407
|
|
373
|
-
context
|
374
|
-
it
|
375
|
-
|
376
|
-
expect(
|
408
|
+
context 'and the user has no specific right on it' do
|
409
|
+
it 'returns true' do
|
410
|
+
executable_real = subject_class.executable_real?('/test-file')
|
411
|
+
expect(executable_real).to be true
|
377
412
|
end
|
378
413
|
end
|
379
414
|
end
|
380
415
|
|
381
|
-
context
|
382
|
-
it
|
383
|
-
|
384
|
-
expect(
|
416
|
+
context 'when the file does not exist' do
|
417
|
+
it 'returns false' do
|
418
|
+
executable_real = subject_class.executable_real?('/no-file')
|
419
|
+
expect(executable_real).to be false
|
385
420
|
end
|
386
421
|
end
|
387
422
|
end
|
388
423
|
|
389
|
-
describe
|
390
|
-
|
391
|
-
|
424
|
+
describe '.exists?' do
|
425
|
+
context 'when the file exists' do
|
426
|
+
it 'returns true' do
|
427
|
+
exists = subject_class.exists?('/test-file')
|
428
|
+
expect(exists).to be true
|
429
|
+
end
|
392
430
|
end
|
393
431
|
|
394
|
-
|
395
|
-
|
432
|
+
context 'when the file does not exist' do
|
433
|
+
it 'returns false' do
|
434
|
+
exists = subject_class.exists?('/no-file')
|
435
|
+
expect(exists).to be false
|
436
|
+
end
|
396
437
|
end
|
397
438
|
end
|
398
439
|
|
399
|
-
describe
|
440
|
+
describe '.exist?' do
|
441
|
+
subject { subject_class }
|
442
|
+
|
400
443
|
it_behaves_like 'aliased method', :exist?, :exists?
|
401
444
|
end
|
402
445
|
|
403
|
-
describe
|
404
|
-
it
|
405
|
-
|
406
|
-
|
446
|
+
describe '.expand_path' do
|
447
|
+
it 'converts a pathname to an absolute pathname' do
|
448
|
+
_fs.chdir '/'
|
449
|
+
|
450
|
+
expanded_path = subject_class.expand_path('test-file')
|
451
|
+
expect(expanded_path).to eq '/test-file'
|
407
452
|
end
|
408
453
|
|
409
|
-
it
|
410
|
-
|
411
|
-
|
454
|
+
it 'references path from the current working directory' do
|
455
|
+
_fs.chdir '/test-dir'
|
456
|
+
|
457
|
+
expanded_path = subject_class.expand_path('test-file')
|
458
|
+
expect(expanded_path).to eq '/test-dir/test-file'
|
412
459
|
end
|
413
460
|
|
414
|
-
context
|
415
|
-
it
|
416
|
-
|
461
|
+
context 'when +dir_string+ is provided' do
|
462
|
+
it 'uses +dir_string+ as the stating point' do
|
463
|
+
expanded_path = subject_class.expand_path('test-file', '/test')
|
464
|
+
expect(expanded_path).to eq '/test/test-file'
|
417
465
|
end
|
418
466
|
end
|
419
467
|
end
|
420
468
|
|
421
|
-
describe
|
422
|
-
it
|
423
|
-
|
469
|
+
describe '.extname' do
|
470
|
+
it 'returns the extension of the given path' do
|
471
|
+
extname = subject_class.extname('test-file.txt')
|
472
|
+
expect(extname).to eq '.txt'
|
424
473
|
end
|
425
474
|
|
426
|
-
context
|
427
|
-
context
|
428
|
-
it
|
429
|
-
|
475
|
+
context 'when the given path starts with a period' do
|
476
|
+
context 'and the path has no extension' do
|
477
|
+
it 'returns an empty string' do
|
478
|
+
extname = subject_class.extname('.test-file')
|
479
|
+
expect(extname).to eq ''
|
430
480
|
end
|
431
481
|
end
|
432
482
|
|
433
|
-
context
|
434
|
-
it
|
435
|
-
|
483
|
+
context 'and the path has an extension' do
|
484
|
+
it 'returns the extension' do
|
485
|
+
extname = subject_class.extname('.test-file.txt')
|
486
|
+
expect(extname).to eq '.txt'
|
436
487
|
end
|
437
488
|
end
|
438
489
|
end
|
439
490
|
|
440
|
-
context
|
441
|
-
it
|
442
|
-
|
491
|
+
context 'when the period is the last character in path' do
|
492
|
+
it 'returns an empty string' do
|
493
|
+
extname = subject_class.extname('test-subject.')
|
494
|
+
expect(extname).to eq ''
|
443
495
|
end
|
444
496
|
end
|
445
497
|
end
|
446
498
|
|
447
|
-
describe
|
448
|
-
context
|
449
|
-
context
|
450
|
-
it
|
451
|
-
|
499
|
+
describe '.file?' do
|
500
|
+
context 'when the named file exists' do
|
501
|
+
context 'and it is a regular file' do
|
502
|
+
it 'returns true' do
|
503
|
+
is_file = subject_class.file?('/test-file')
|
504
|
+
expect(is_file).to be true
|
452
505
|
end
|
453
506
|
end
|
454
507
|
|
455
|
-
context
|
456
|
-
it
|
457
|
-
|
508
|
+
context 'and it is not a regular file' do
|
509
|
+
it 'returns false' do
|
510
|
+
is_file = subject_class.file?('/test-dir')
|
511
|
+
expect(is_file).to be false
|
458
512
|
end
|
459
513
|
end
|
460
514
|
end
|
461
515
|
|
462
|
-
context
|
463
|
-
it
|
464
|
-
|
516
|
+
context 'when the named file does not exist' do
|
517
|
+
it 'returns false' do
|
518
|
+
is_file = subject_class.file?('/no-file')
|
519
|
+
expect(is_file).to be false
|
465
520
|
end
|
466
521
|
end
|
467
522
|
end
|
468
523
|
|
469
|
-
describe
|
470
|
-
context
|
471
|
-
it
|
472
|
-
|
524
|
+
describe '.fnmatch' do
|
525
|
+
context 'when the given path matches against the given pattern' do
|
526
|
+
it 'returns true' do
|
527
|
+
matching = subject_class.fnmatch('c?t', 'cat')
|
528
|
+
expect(matching).to be true
|
473
529
|
end
|
474
530
|
end
|
475
531
|
|
476
|
-
context
|
477
|
-
it
|
478
|
-
|
532
|
+
context 'when the given path does not match against the given pattern' do
|
533
|
+
it 'returns false' do
|
534
|
+
matching = File.fnmatch('c?t', 'tac')
|
535
|
+
expect(matching).to be false
|
479
536
|
end
|
480
537
|
end
|
481
538
|
end
|
482
539
|
|
483
|
-
describe
|
540
|
+
describe '.fnmatch?' do
|
541
|
+
subject { subject_class }
|
542
|
+
|
484
543
|
it_behaves_like 'aliased method', :fnmatch?, :fnmatch
|
485
544
|
end
|
486
545
|
|
487
|
-
describe
|
488
|
-
context
|
546
|
+
describe '.ftype' do
|
547
|
+
context 'when the named entry is a regular file' do
|
489
548
|
it "returns 'file'" do
|
490
|
-
|
549
|
+
ftype = subject_class.ftype('/test-file')
|
550
|
+
expect(ftype).to eq 'file'
|
491
551
|
end
|
492
552
|
end
|
493
553
|
|
494
|
-
context
|
554
|
+
context 'when the named entry is a directory' do
|
495
555
|
it "returns 'directory'" do
|
496
|
-
|
556
|
+
ftype = subject_class.ftype('/test-dir')
|
557
|
+
expect(ftype).to eq 'directory'
|
497
558
|
end
|
498
559
|
end
|
499
560
|
|
500
|
-
context
|
561
|
+
context 'when the named entry is a block device' do
|
501
562
|
it "returns 'blockSpecial'" do
|
502
|
-
|
503
|
-
file =
|
563
|
+
_fs.touch '/block-file'
|
564
|
+
file = _fs.find('/block-file')
|
504
565
|
file.block_device = true
|
505
|
-
|
566
|
+
|
567
|
+
ftype = subject_class.ftype('/block-file')
|
568
|
+
expect(ftype).to eq 'blockSpecial'
|
506
569
|
end
|
507
570
|
end
|
508
571
|
|
509
|
-
context
|
572
|
+
context 'when the named entry is a character device' do
|
510
573
|
it "returns 'characterSpecial'" do
|
511
|
-
|
512
|
-
file =
|
574
|
+
_fs.touch '/character-file'
|
575
|
+
file = _fs.find('/character-file')
|
513
576
|
file.character_device = true
|
514
|
-
|
577
|
+
|
578
|
+
ftype = subject_class.ftype('/character-file')
|
579
|
+
expect(ftype).to eq 'characterSpecial'
|
515
580
|
end
|
516
581
|
end
|
517
582
|
|
518
|
-
context
|
583
|
+
context 'when the named entry is a symlink' do
|
519
584
|
it "returns 'link'" do
|
520
|
-
|
585
|
+
ftype = subject_class.ftype('/test-link')
|
586
|
+
expect(ftype).to eq 'link'
|
521
587
|
end
|
522
588
|
end
|
523
589
|
|
524
590
|
# fifo and socket not handled for now
|
525
591
|
|
526
|
-
context
|
592
|
+
context 'when the named entry has no specific type' do
|
527
593
|
it "returns 'unknown'" do
|
528
|
-
root =
|
594
|
+
root = _fs.find '/'
|
529
595
|
root.add_entry Fake::Entry.new('test-entry')
|
530
|
-
|
596
|
+
|
597
|
+
ftype = subject_class.ftype('/test-entry')
|
598
|
+
expect(ftype).to eq 'unknown'
|
531
599
|
end
|
532
600
|
end
|
533
601
|
end
|
534
602
|
|
535
|
-
describe
|
536
|
-
context
|
537
|
-
context
|
538
|
-
it
|
539
|
-
|
540
|
-
|
603
|
+
describe '.grpowned?' do
|
604
|
+
context 'when the named file exists' do
|
605
|
+
context 'and the effective user group owns of the file' do
|
606
|
+
it 'returns true' do
|
607
|
+
subject_class.chown 0, Process.egid, '/test-file'
|
608
|
+
|
609
|
+
grpowned = File.grpowned?('/test-file')
|
610
|
+
expect(grpowned).to be true
|
541
611
|
end
|
542
612
|
end
|
543
613
|
|
544
|
-
context
|
545
|
-
it
|
546
|
-
|
547
|
-
|
614
|
+
context 'and the effective user group does not own of the file' do
|
615
|
+
it 'returns false' do
|
616
|
+
subject_class.chown 0, 0, '/test-file'
|
617
|
+
|
618
|
+
grpowned = File.grpowned?('/test-file')
|
619
|
+
expect(grpowned).to be false
|
548
620
|
end
|
549
621
|
end
|
550
622
|
end
|
551
623
|
|
552
|
-
context
|
553
|
-
it
|
554
|
-
|
624
|
+
context 'when the named file does not exist' do
|
625
|
+
it 'returns false' do
|
626
|
+
grpowned = File.grpowned?('/no-file')
|
627
|
+
expect(grpowned).to be false
|
555
628
|
end
|
556
629
|
end
|
557
630
|
end
|
558
631
|
|
559
|
-
describe
|
560
|
-
before
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
632
|
+
describe '.identical?' do
|
633
|
+
before do
|
634
|
+
subject_class.open('/test-file', 'w') { |f| f.puts 'test' }
|
635
|
+
subject_class.open('/test-file2', 'w') { |f| f.puts 'test' }
|
636
|
+
subject_class.symlink '/test-file', '/test-file-link'
|
637
|
+
subject_class.symlink '/test-file', '/test-file-link2'
|
638
|
+
subject_class.symlink '/test-file2', '/test-file2-link'
|
566
639
|
end
|
567
640
|
|
568
|
-
context
|
569
|
-
it
|
570
|
-
|
641
|
+
context 'when two paths represent the same path' do
|
642
|
+
it 'returns true' do
|
643
|
+
identical = subject_class.identical?('/test-file', '/test-file')
|
644
|
+
expect(identical).to be true
|
571
645
|
end
|
572
646
|
end
|
573
647
|
|
574
|
-
context
|
575
|
-
it
|
576
|
-
|
648
|
+
context 'when two paths do not represent the same file' do
|
649
|
+
it 'returns false' do
|
650
|
+
identical = subject_class.identical?('/test-file', '/test-file2')
|
651
|
+
expect(identical).to be false
|
577
652
|
end
|
578
653
|
end
|
579
654
|
|
580
|
-
context
|
581
|
-
it
|
582
|
-
|
655
|
+
context 'when one of the paths does not exist' do
|
656
|
+
it 'returns false' do
|
657
|
+
identical = subject_class.identical?('/test-file', '/no-file')
|
658
|
+
expect(identical).to be false
|
583
659
|
end
|
584
660
|
end
|
585
661
|
|
586
|
-
context
|
587
|
-
context
|
588
|
-
it
|
589
|
-
|
662
|
+
context 'when a path is a symlink' do
|
663
|
+
context 'and the linked file is the same as the other path' do
|
664
|
+
it 'returns true' do
|
665
|
+
identical = subject_class.identical?('/test-file', '/test-file-link')
|
666
|
+
expect(identical).to be true
|
590
667
|
end
|
591
668
|
end
|
592
669
|
|
593
|
-
context
|
594
|
-
it
|
595
|
-
|
670
|
+
context 'and the linked file is different from the other path' do
|
671
|
+
it 'returns false' do
|
672
|
+
identical = subject_class.identical?('/test-file2', '/test-file-link')
|
673
|
+
expect(identical).to be false
|
596
674
|
end
|
597
675
|
end
|
598
676
|
end
|
599
677
|
|
600
|
-
context
|
601
|
-
context
|
602
|
-
it
|
603
|
-
|
678
|
+
context 'when the two paths are symlinks' do
|
679
|
+
context 'and both links point to the same file' do
|
680
|
+
it 'returns true' do
|
681
|
+
identical = subject_class.identical?('/test-file-link', '/test-file-link2')
|
682
|
+
expect(identical).to be true
|
604
683
|
end
|
605
684
|
end
|
606
685
|
|
607
|
-
context
|
608
|
-
it
|
609
|
-
|
686
|
+
context 'and both links do not point to the same file' do
|
687
|
+
it 'returns false' do
|
688
|
+
identical = subject_class.identical?('/test-file-link', '/test-file2-link')
|
689
|
+
expect(identical).to be false
|
610
690
|
end
|
611
691
|
end
|
612
692
|
end
|
613
693
|
end
|
614
694
|
|
615
|
-
describe
|
616
|
-
it
|
617
|
-
|
695
|
+
describe '.join' do
|
696
|
+
it 'returns a new string formed by joining the strings using File::SEPARATOR' do
|
697
|
+
returned_value = subject_class.join('a', 'b', 'c')
|
698
|
+
expect(returned_value).to eq 'a/b/c'
|
618
699
|
end
|
619
700
|
end
|
620
701
|
|
621
|
-
describe
|
622
|
-
context
|
623
|
-
it
|
624
|
-
|
625
|
-
|
702
|
+
describe '.lchmod' do
|
703
|
+
context 'when the named file is a regular file' do
|
704
|
+
it 'acts like chmod' do
|
705
|
+
subject_class.lchmod 0777, '/test-file'
|
706
|
+
|
707
|
+
mode = subject_class.stat('/test-file').mode
|
708
|
+
expect(mode).to be 0100777
|
626
709
|
end
|
627
710
|
end
|
628
711
|
|
629
|
-
context
|
630
|
-
it
|
631
|
-
|
632
|
-
|
712
|
+
context 'when the named file is a symlink' do
|
713
|
+
it 'changes permission bits on the symlink' do
|
714
|
+
subject_class.lchmod 0777, '/test-link'
|
715
|
+
|
716
|
+
mode = subject_class.lstat('/test-link').mode
|
717
|
+
expect(mode).to be 0100777
|
633
718
|
end
|
634
719
|
|
635
720
|
it "does not change permission bits on the link's target" do
|
636
|
-
|
637
|
-
|
638
|
-
|
721
|
+
old_mode = subject_class.stat('/test-file').mode
|
722
|
+
subject_class.lchmod 0777, '/test-link'
|
723
|
+
|
724
|
+
mode = subject_class.stat('/test-file').mode
|
725
|
+
expect(mode).to eq old_mode
|
639
726
|
end
|
640
727
|
end
|
641
728
|
end
|
642
729
|
|
643
|
-
describe
|
644
|
-
before
|
645
|
-
|
730
|
+
describe '.link' do
|
731
|
+
before do
|
732
|
+
subject_class.open('/test-file', 'w') { |f| f.puts 'test' }
|
646
733
|
end
|
647
734
|
|
648
|
-
it
|
649
|
-
|
650
|
-
|
735
|
+
it 'creates a new name for an existing file using a hard link' do
|
736
|
+
subject_class.link '/test-file', '/new-file'
|
737
|
+
|
738
|
+
original_content = subject_class.read('/test-file')
|
739
|
+
copy_content = subject_class.read('/new-file')
|
740
|
+
expect(copy_content).to eq original_content
|
651
741
|
end
|
652
742
|
|
653
|
-
it
|
654
|
-
|
743
|
+
it 'returns zero' do
|
744
|
+
returned_value = subject_class.link('/test-file', '/new-file')
|
745
|
+
expect(returned_value).to be_zero
|
655
746
|
end
|
656
747
|
|
657
|
-
context
|
658
|
-
it
|
748
|
+
context 'when +old_name+ does not exist' do
|
749
|
+
it 'raises an exception' do
|
659
750
|
expect {
|
660
|
-
|
661
|
-
}.to raise_error
|
751
|
+
subject_class.link '/no-file', '/nowhere'
|
752
|
+
}.to raise_error Errno::ENOENT
|
662
753
|
end
|
663
754
|
end
|
664
755
|
|
665
|
-
context
|
666
|
-
it
|
667
|
-
|
756
|
+
context 'when +new_name+ already exists' do
|
757
|
+
it 'raises an exception' do
|
758
|
+
subject_class.open('/test-file2', 'w') { |f| f.puts 'test2' }
|
759
|
+
|
668
760
|
expect {
|
669
|
-
|
670
|
-
}.to raise_error
|
761
|
+
subject_class.link '/test-file', '/test-file2'
|
762
|
+
}.to raise_error SystemCallError
|
671
763
|
end
|
672
764
|
end
|
673
765
|
end
|
674
766
|
|
675
767
|
describe '.lstat' do
|
676
|
-
it
|
677
|
-
|
768
|
+
it 'returns a File::Stat object for the named file' do
|
769
|
+
stat = subject_class.lstat('/test-file')
|
770
|
+
expect(stat).to be_a File::Stat
|
678
771
|
end
|
679
772
|
|
680
|
-
context
|
681
|
-
it
|
682
|
-
expect {
|
773
|
+
context 'when the named file does not exist' do
|
774
|
+
it 'raises an exception' do
|
775
|
+
expect { subject_class.lstat '/no-file' }.to raise_error Errno::ENOENT
|
683
776
|
end
|
684
777
|
end
|
685
778
|
|
686
|
-
context
|
687
|
-
it
|
688
|
-
|
779
|
+
context 'when the named file is a symlink' do
|
780
|
+
it 'does not follow the last symbolic link' do
|
781
|
+
is_symlink = subject_class.lstat('/test-link').symlink?
|
782
|
+
expect(is_symlink).to be true
|
689
783
|
end
|
690
784
|
|
691
|
-
context
|
692
|
-
it
|
785
|
+
context 'and its target does not exist' do
|
786
|
+
it 'ignores errors' do
|
693
787
|
expect {
|
694
|
-
|
695
|
-
}.not_to raise_error
|
788
|
+
subject_class.lstat('/no-link')
|
789
|
+
}.not_to raise_error
|
696
790
|
end
|
697
791
|
end
|
698
792
|
end
|
699
793
|
end
|
700
794
|
|
701
795
|
describe '.new' do
|
702
|
-
context
|
703
|
-
context
|
704
|
-
|
705
|
-
|
706
|
-
|
796
|
+
context 'when the mode is provided' do
|
797
|
+
context 'and it is an integer' do
|
798
|
+
subject { subject_class.new('/test-file', File::RDWR) }
|
799
|
+
|
800
|
+
it 'sets the mode to the integer value' do
|
801
|
+
expect(subject.send(:opening_mode)).to eq File::RDWR
|
707
802
|
end
|
708
803
|
end
|
709
804
|
|
710
|
-
context
|
711
|
-
|
712
|
-
|
713
|
-
|
805
|
+
context 'and it is a string' do
|
806
|
+
subject { subject_class.new('/test-file', 'r+') }
|
807
|
+
|
808
|
+
it 'sets the mode to the integer value' do
|
809
|
+
expect(subject.send(:opening_mode)).to eq File::RDWR
|
714
810
|
end
|
715
811
|
end
|
716
812
|
|
717
|
-
context
|
718
|
-
context
|
719
|
-
it
|
720
|
-
|
721
|
-
|
813
|
+
context 'and it specifies that the file must be created' do
|
814
|
+
context 'and the file already exists' do
|
815
|
+
it 'changes the mtime of the file' do
|
816
|
+
subject_class.new '/test-file', 'w'
|
817
|
+
subject_class.exist?('/test-file')
|
722
818
|
end
|
723
819
|
end
|
724
820
|
end
|
@@ -726,714 +822,775 @@ module MemFs
|
|
726
822
|
context 'and it specifies that the file must be truncated' do
|
727
823
|
context 'and the file already exists' do
|
728
824
|
it 'truncates its content' do
|
729
|
-
|
730
|
-
file =
|
825
|
+
subject_class.open('/test-file', 'w') { |f| f.puts 'hello' }
|
826
|
+
file = subject_class.new('/test-file', 'w')
|
731
827
|
file.close
|
732
|
-
|
828
|
+
|
829
|
+
expect(subject_class.read('/test-file')).to eq ''
|
733
830
|
end
|
734
831
|
end
|
735
832
|
end
|
736
833
|
end
|
737
834
|
|
738
|
-
context
|
739
|
-
it
|
740
|
-
expect {
|
835
|
+
context 'when no argument is given' do
|
836
|
+
it 'raises an exception' do
|
837
|
+
expect { subject_class.new }.to raise_error ArgumentError
|
741
838
|
end
|
742
839
|
end
|
743
840
|
|
744
|
-
context
|
745
|
-
it
|
746
|
-
expect {
|
841
|
+
context 'when too many arguments are given' do
|
842
|
+
it 'raises an exception' do
|
843
|
+
expect { subject_class.new(1, 2, 3, 4) }.to raise_error(ArgumentError)
|
747
844
|
end
|
748
845
|
end
|
749
846
|
end
|
750
847
|
|
751
|
-
describe
|
752
|
-
context
|
753
|
-
context
|
754
|
-
it
|
755
|
-
|
756
|
-
|
848
|
+
describe '.owned?' do
|
849
|
+
context 'when the named file exists' do
|
850
|
+
context 'and the effective user owns of the file' do
|
851
|
+
it 'returns true' do
|
852
|
+
subject_class.chown Process.euid, 0, '/test-file'
|
853
|
+
|
854
|
+
owned = File.owned?('/test-file')
|
855
|
+
expect(owned).to be true
|
757
856
|
end
|
758
857
|
end
|
759
858
|
|
760
|
-
context
|
761
|
-
it
|
762
|
-
|
763
|
-
|
859
|
+
context 'and the effective user does not own of the file' do
|
860
|
+
it 'returns false' do
|
861
|
+
subject_class.chown 0, 0, '/test-file'
|
862
|
+
|
863
|
+
owned = File.owned?('/test-file')
|
864
|
+
expect(owned).to be false
|
764
865
|
end
|
765
866
|
end
|
766
867
|
end
|
767
868
|
|
768
|
-
context
|
769
|
-
it
|
770
|
-
|
869
|
+
context 'when the named file does not exist' do
|
870
|
+
it 'returns false' do
|
871
|
+
owned = File.owned?('/no-file')
|
872
|
+
expect(owned).to be false
|
771
873
|
end
|
772
874
|
end
|
773
875
|
end
|
774
876
|
|
775
877
|
describe '.path' do
|
776
|
-
context
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
expect(subject.path(path)).to eq('/some/path')
|
878
|
+
context 'when the path is a string' do
|
879
|
+
it 'returns the string representation of the path' do
|
880
|
+
path = subject_class.path('/some/path')
|
881
|
+
expect(path).to eq '/some/path'
|
781
882
|
end
|
782
883
|
end
|
783
884
|
|
784
|
-
context
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
expect(subject.path(path)).to eq('/some/path')
|
885
|
+
context 'when the path is a Pathname' do
|
886
|
+
it 'returns the string representation of the path' do
|
887
|
+
path = subject_class.path(Pathname.new('/some/path'))
|
888
|
+
expect(path).to eq '/some/path'
|
789
889
|
end
|
790
890
|
end
|
791
891
|
end
|
792
892
|
|
793
|
-
describe
|
893
|
+
describe '.pipe?' do
|
794
894
|
# Pipes are not handled for now
|
795
895
|
|
796
|
-
context
|
797
|
-
it
|
798
|
-
|
799
|
-
expect(
|
896
|
+
context 'when the named file is not a pipe' do
|
897
|
+
it 'returns false' do
|
898
|
+
is_pipe = File.pipe?('/test-file')
|
899
|
+
expect(is_pipe).to be false
|
800
900
|
end
|
801
901
|
end
|
802
902
|
end
|
803
903
|
|
804
|
-
describe
|
805
|
-
before
|
806
|
-
|
904
|
+
describe '.read' do
|
905
|
+
before do
|
906
|
+
subject_class.open('/test-file', 'w') { |f| f.puts 'test' }
|
807
907
|
end
|
808
908
|
|
809
|
-
it
|
810
|
-
|
909
|
+
it 'reads the content of the given file' do
|
910
|
+
read_content = subject_class.read('/test-file')
|
911
|
+
expect(read_content).to eq "test\n"
|
811
912
|
end
|
812
913
|
|
813
|
-
context
|
814
|
-
it
|
815
|
-
|
914
|
+
context 'when +lenght+ is provided' do
|
915
|
+
it 'reads only +length+ characters' do
|
916
|
+
read_content = subject_class.read('/test-file', 2)
|
917
|
+
expect(read_content).to eq 'te'
|
816
918
|
end
|
817
919
|
|
818
|
-
context
|
819
|
-
it
|
820
|
-
|
920
|
+
context 'when +length+ is bigger than the file size' do
|
921
|
+
it 'reads until the end of the file' do
|
922
|
+
read_content = subject_class.read('/test-file', 1000)
|
923
|
+
expect(read_content).to eq "test\n"
|
821
924
|
end
|
822
925
|
end
|
823
926
|
end
|
824
927
|
|
825
|
-
context
|
826
|
-
it
|
827
|
-
|
928
|
+
context 'when +offset+ is provided' do
|
929
|
+
it 'starts reading from the offset' do
|
930
|
+
read_content = subject_class.read('/test-file', 2, 1)
|
931
|
+
expect(read_content).to eq 'es'
|
828
932
|
end
|
829
933
|
|
830
|
-
it
|
934
|
+
it 'raises an error if offset is negative' do
|
831
935
|
expect {
|
832
|
-
|
833
|
-
}.to raise_error
|
936
|
+
subject_class.read '/test-file', 2, -1
|
937
|
+
}.to raise_error Errno::EINVAL
|
834
938
|
end
|
835
939
|
end
|
836
940
|
|
837
|
-
context
|
838
|
-
it
|
839
|
-
expect(
|
941
|
+
context 'when the last argument is a hash' do
|
942
|
+
it 'passes the contained options to +open+' do
|
943
|
+
expect(subject_class).to receive(:open)
|
840
944
|
.with('/test-file', File::RDONLY, encoding: 'UTF-8')
|
841
|
-
.and_return(
|
842
|
-
|
945
|
+
.and_return(subject)
|
946
|
+
|
947
|
+
subject_class.read '/test-file', encoding: 'UTF-8'
|
843
948
|
end
|
844
949
|
|
845
|
-
context
|
846
|
-
it
|
847
|
-
expect(
|
950
|
+
context 'when it contains the +open_args+ key' do
|
951
|
+
it 'takes precedence over the other options' do
|
952
|
+
expect(subject_class).to receive(:open)
|
848
953
|
.with('/test-file', 'r')
|
849
|
-
.and_return(
|
850
|
-
|
954
|
+
.and_return(subject)
|
955
|
+
|
956
|
+
subject_class.read '/test-file', mode: 'w', open_args: ['r']
|
851
957
|
end
|
852
958
|
end
|
853
959
|
end
|
854
960
|
end
|
855
961
|
|
856
|
-
describe
|
962
|
+
describe '.readable?' do
|
857
963
|
let(:access) { 0 }
|
858
964
|
let(:gid) { 0 }
|
859
965
|
let(:uid) { 0 }
|
860
966
|
|
861
|
-
before
|
862
|
-
|
863
|
-
|
967
|
+
before do
|
968
|
+
subject_class.chmod access, '/test-file'
|
969
|
+
subject_class.chown uid, gid, '/test-file'
|
864
970
|
end
|
865
971
|
|
866
|
-
context
|
867
|
-
it
|
868
|
-
readable =
|
869
|
-
expect(readable).to
|
972
|
+
context 'when the file is not readable by anyone' do
|
973
|
+
it 'return false' do
|
974
|
+
readable = subject_class.readable?('/test-file')
|
975
|
+
expect(readable).to be false
|
870
976
|
end
|
871
977
|
end
|
872
978
|
|
873
|
-
context
|
979
|
+
context 'when the file is user readable' do
|
874
980
|
let(:access) { MemFs::Fake::Entry::UREAD }
|
875
981
|
|
876
|
-
context
|
877
|
-
before(:each) { subject.chown(uid, 0, '/test-file') }
|
982
|
+
context 'and the current user owns the file' do
|
878
983
|
let(:uid) { Process.euid }
|
879
984
|
|
880
|
-
|
881
|
-
|
882
|
-
|
985
|
+
before { subject_class.chown uid, 0, '/test-file' }
|
986
|
+
|
987
|
+
it 'returns true' do
|
988
|
+
readable = subject_class.readable?('/test-file')
|
989
|
+
expect(readable).to be true
|
883
990
|
end
|
884
991
|
end
|
885
992
|
end
|
886
993
|
|
887
|
-
context
|
994
|
+
context 'when the file is group readable' do
|
888
995
|
let(:access) { MemFs::Fake::Entry::GREAD }
|
889
996
|
|
890
|
-
context
|
997
|
+
context 'and the current user is part of the owner group' do
|
891
998
|
let(:gid) { Process.egid }
|
892
999
|
|
893
|
-
it
|
894
|
-
readable =
|
895
|
-
expect(readable).to
|
1000
|
+
it 'returns true' do
|
1001
|
+
readable = subject_class.readable?('/test-file')
|
1002
|
+
expect(readable).to be true
|
896
1003
|
end
|
897
1004
|
end
|
898
1005
|
end
|
899
1006
|
|
900
|
-
context
|
1007
|
+
context 'when the file is readable by anyone' do
|
901
1008
|
let(:access) { MemFs::Fake::Entry::OREAD }
|
902
1009
|
|
903
|
-
context
|
904
|
-
it
|
905
|
-
readable =
|
906
|
-
expect(readable).to
|
1010
|
+
context 'and the user has no specific right on it' do
|
1011
|
+
it 'returns true' do
|
1012
|
+
readable = subject_class.readable?('/test-file')
|
1013
|
+
expect(readable).to be true
|
907
1014
|
end
|
908
1015
|
end
|
909
1016
|
end
|
910
1017
|
|
911
|
-
context
|
912
|
-
it
|
913
|
-
readable =
|
914
|
-
expect(readable).to
|
1018
|
+
context 'when the file does not exist' do
|
1019
|
+
it 'returns false' do
|
1020
|
+
readable = subject_class.readable?('/no-file')
|
1021
|
+
expect(readable).to be false
|
915
1022
|
end
|
916
1023
|
end
|
917
1024
|
end
|
918
1025
|
|
919
|
-
describe
|
1026
|
+
describe '.readable_real?' do
|
920
1027
|
let(:access) { 0 }
|
921
1028
|
let(:gid) { 0 }
|
922
1029
|
let(:uid) { 0 }
|
923
1030
|
|
924
|
-
before
|
925
|
-
|
926
|
-
|
1031
|
+
before do
|
1032
|
+
subject_class.chmod access, '/test-file'
|
1033
|
+
subject_class.chown uid, gid, '/test-file'
|
927
1034
|
end
|
928
1035
|
|
929
|
-
context
|
930
|
-
it
|
931
|
-
|
932
|
-
expect(
|
1036
|
+
context 'when the file is not readable by anyone' do
|
1037
|
+
it 'return false' do
|
1038
|
+
readable_real = subject_class.readable_real?('/test-file')
|
1039
|
+
expect(readable_real).to be false
|
933
1040
|
end
|
934
1041
|
end
|
935
1042
|
|
936
|
-
context
|
1043
|
+
context 'when the file is user readable' do
|
937
1044
|
let(:access) { MemFs::Fake::Entry::UREAD }
|
938
1045
|
|
939
|
-
context
|
940
|
-
before(:each) { subject.chown(uid, 0, '/test-file') }
|
1046
|
+
context 'and the current user owns the file' do
|
941
1047
|
let(:uid) { Process.uid }
|
942
1048
|
|
943
|
-
|
944
|
-
|
945
|
-
|
1049
|
+
before { subject_class.chown uid, 0, '/test-file' }
|
1050
|
+
|
1051
|
+
it 'returns true' do
|
1052
|
+
readable_real = subject_class.readable_real?('/test-file')
|
1053
|
+
expect(readable_real).to be true
|
946
1054
|
end
|
947
1055
|
end
|
948
1056
|
end
|
949
1057
|
|
950
|
-
context
|
1058
|
+
context 'when the file is group readable' do
|
951
1059
|
let(:access) { MemFs::Fake::Entry::GREAD }
|
952
1060
|
|
953
|
-
context
|
1061
|
+
context 'and the current user is part of the owner group' do
|
954
1062
|
let(:gid) { Process.gid }
|
955
1063
|
|
956
|
-
it
|
957
|
-
|
958
|
-
expect(
|
1064
|
+
it 'returns true' do
|
1065
|
+
readable_real = subject_class.readable_real?('/test-file')
|
1066
|
+
expect(readable_real).to be true
|
959
1067
|
end
|
960
1068
|
end
|
961
1069
|
end
|
962
1070
|
|
963
|
-
context
|
1071
|
+
context 'when the file is readable by anyone' do
|
964
1072
|
let(:access) { MemFs::Fake::Entry::OREAD }
|
965
1073
|
|
966
|
-
context
|
967
|
-
it
|
968
|
-
|
969
|
-
expect(
|
1074
|
+
context 'and the user has no specific right on it' do
|
1075
|
+
it 'returns true' do
|
1076
|
+
readable_real = subject_class.readable_real?('/test-file')
|
1077
|
+
expect(readable_real).to be true
|
970
1078
|
end
|
971
1079
|
end
|
972
1080
|
end
|
973
1081
|
|
974
|
-
context
|
975
|
-
it
|
976
|
-
|
977
|
-
expect(
|
1082
|
+
context 'when the file does not exist' do
|
1083
|
+
it 'returns false' do
|
1084
|
+
readable_real = subject_class.readable_real?('/no-file')
|
1085
|
+
expect(readable_real).to be false
|
978
1086
|
end
|
979
1087
|
end
|
980
1088
|
end
|
981
1089
|
|
982
|
-
describe
|
983
|
-
it
|
984
|
-
expect(
|
1090
|
+
describe '.readlink' do
|
1091
|
+
it 'returns the name of the file referenced by the given link' do
|
1092
|
+
expect(subject_class.readlink('/test-link')).to eq '/test-file'
|
985
1093
|
end
|
986
1094
|
end
|
987
1095
|
|
988
|
-
describe
|
989
|
-
before
|
990
|
-
|
991
|
-
|
992
|
-
|
1096
|
+
describe '.realdirpath' do
|
1097
|
+
before do
|
1098
|
+
_fs.mkdir '/test-dir/sub-dir'
|
1099
|
+
_fs.symlink '/test-dir/sub-dir', '/test-dir/sub-dir-link'
|
1100
|
+
_fs.touch '/test-dir/sub-dir/test-file'
|
993
1101
|
end
|
994
1102
|
|
995
|
-
context
|
996
|
-
it
|
997
|
-
path =
|
998
|
-
expect(path).to eq
|
1103
|
+
context 'when the path does not contain any symlink or useless dots' do
|
1104
|
+
it 'returns the path itself' do
|
1105
|
+
path = subject_class.realdirpath('/test-file')
|
1106
|
+
expect(path).to eq '/test-file'
|
999
1107
|
end
|
1000
1108
|
end
|
1001
1109
|
|
1002
|
-
context
|
1003
|
-
context
|
1004
|
-
it
|
1005
|
-
path =
|
1006
|
-
expect(path).to eq
|
1110
|
+
context 'when the path contains a symlink' do
|
1111
|
+
context 'and the symlink is a middle part' do
|
1112
|
+
it 'returns the path with the symlink dereferrenced' do
|
1113
|
+
path = subject_class.realdirpath('/test-dir/sub-dir-link/test-file')
|
1114
|
+
expect(path).to eq '/test-dir/sub-dir/test-file'
|
1007
1115
|
end
|
1008
1116
|
end
|
1009
1117
|
|
1010
|
-
context
|
1011
|
-
it
|
1012
|
-
path =
|
1013
|
-
expect(path).to eq
|
1118
|
+
context 'and the symlink is the last part' do
|
1119
|
+
it 'returns the path with the symlink dereferrenced' do
|
1120
|
+
path = subject_class.realdirpath('/test-dir/sub-dir-link')
|
1121
|
+
expect(path).to eq '/test-dir/sub-dir'
|
1014
1122
|
end
|
1015
1123
|
end
|
1016
1124
|
end
|
1017
1125
|
|
1018
|
-
context
|
1019
|
-
it
|
1020
|
-
path =
|
1021
|
-
expect(path).to eq
|
1126
|
+
context 'when the path contains useless dots' do
|
1127
|
+
it 'returns the path with the useless dots interpolated' do
|
1128
|
+
path = subject_class.realdirpath('/test-dir/../test-dir/./sub-dir/test-file')
|
1129
|
+
expect(path).to eq '/test-dir/sub-dir/test-file'
|
1022
1130
|
end
|
1023
1131
|
end
|
1024
1132
|
|
1025
1133
|
context 'when the given path is relative' do
|
1026
|
-
context
|
1027
|
-
it
|
1028
|
-
|
1029
|
-
path =
|
1030
|
-
expect(path).to eq
|
1134
|
+
context 'and +dir_string+ is not provided' do
|
1135
|
+
it 'uses the current working directory has base directory' do
|
1136
|
+
_fs.chdir '/test-dir'
|
1137
|
+
path = subject_class.realdirpath('../test-dir/./sub-dir/test-file')
|
1138
|
+
expect(path).to eq '/test-dir/sub-dir/test-file'
|
1031
1139
|
end
|
1032
1140
|
end
|
1033
1141
|
|
1034
|
-
context
|
1035
|
-
it
|
1036
|
-
path =
|
1037
|
-
expect(path).to eq
|
1142
|
+
context 'and +dir_string+ is provided' do
|
1143
|
+
it 'uses the given directory has base directory' do
|
1144
|
+
path = subject_class.realdirpath('../test-dir/./sub-dir/test-file', '/test-dir')
|
1145
|
+
expect(path).to eq '/test-dir/sub-dir/test-file'
|
1038
1146
|
end
|
1039
1147
|
end
|
1040
1148
|
end
|
1041
1149
|
|
1042
|
-
context
|
1043
|
-
context
|
1044
|
-
before
|
1045
|
-
|
1150
|
+
context 'when the last part of the given path is a symlink' do
|
1151
|
+
context 'and its target does not exist' do
|
1152
|
+
before do
|
1153
|
+
_fs.symlink '/test-dir/sub-dir/test', '/test-dir/sub-dir/test-link'
|
1046
1154
|
end
|
1047
1155
|
|
1048
|
-
it
|
1049
|
-
path =
|
1050
|
-
expect(path).to eq
|
1156
|
+
it 'uses the name of the target in the resulting path' do
|
1157
|
+
path = subject_class.realdirpath('/test-dir/sub-dir/test-link')
|
1158
|
+
expect(path).to eq '/test-dir/sub-dir/test'
|
1051
1159
|
end
|
1052
1160
|
end
|
1053
1161
|
end
|
1054
1162
|
|
1055
|
-
context
|
1056
|
-
it
|
1057
|
-
path =
|
1058
|
-
expect(path).to eq
|
1163
|
+
context 'when the last part of the given path does not exist' do
|
1164
|
+
it 'uses its name in the resulting path' do
|
1165
|
+
path = subject_class.realdirpath('/test-dir/sub-dir/test')
|
1166
|
+
expect(path).to eq '/test-dir/sub-dir/test'
|
1059
1167
|
end
|
1060
1168
|
end
|
1061
1169
|
|
1062
|
-
context
|
1063
|
-
it
|
1170
|
+
context 'when a middle part of the given path does not exist' do
|
1171
|
+
it 'raises an exception' do
|
1064
1172
|
expect {
|
1065
|
-
|
1173
|
+
subject_class.realdirpath '/no-dir/test-file'
|
1066
1174
|
}.to raise_error
|
1067
1175
|
end
|
1068
1176
|
end
|
1069
1177
|
end
|
1070
1178
|
|
1071
|
-
describe
|
1072
|
-
before
|
1073
|
-
|
1074
|
-
|
1075
|
-
|
1179
|
+
describe '.realpath' do
|
1180
|
+
before do
|
1181
|
+
_fs.mkdir '/test-dir/sub-dir'
|
1182
|
+
_fs.symlink '/test-dir/sub-dir', '/test-dir/sub-dir-link'
|
1183
|
+
_fs.touch '/test-dir/sub-dir/test-file'
|
1076
1184
|
end
|
1077
1185
|
|
1078
|
-
context
|
1079
|
-
it
|
1080
|
-
path =
|
1081
|
-
expect(path).to eq
|
1186
|
+
context 'when the path does not contain any symlink or useless dots' do
|
1187
|
+
it 'returns the path itself' do
|
1188
|
+
path = subject_class.realpath('/test-file')
|
1189
|
+
expect(path).to eq '/test-file'
|
1082
1190
|
end
|
1083
1191
|
end
|
1084
1192
|
|
1085
|
-
context
|
1086
|
-
context
|
1087
|
-
it
|
1088
|
-
path =
|
1089
|
-
expect(path).to eq
|
1193
|
+
context 'when the path contains a symlink' do
|
1194
|
+
context 'and the symlink is a middle part' do
|
1195
|
+
it 'returns the path with the symlink dereferrenced' do
|
1196
|
+
path = subject_class.realpath('/test-dir/sub-dir-link/test-file')
|
1197
|
+
expect(path).to eq '/test-dir/sub-dir/test-file'
|
1090
1198
|
end
|
1091
1199
|
end
|
1092
1200
|
|
1093
|
-
context
|
1094
|
-
it
|
1095
|
-
path =
|
1096
|
-
expect(path).to eq
|
1201
|
+
context 'and the symlink is the last part' do
|
1202
|
+
it 'returns the path with the symlink dereferrenced' do
|
1203
|
+
path = subject_class.realpath('/test-dir/sub-dir-link')
|
1204
|
+
expect(path).to eq '/test-dir/sub-dir'
|
1097
1205
|
end
|
1098
1206
|
end
|
1099
1207
|
end
|
1100
1208
|
|
1101
|
-
context
|
1102
|
-
it
|
1103
|
-
path =
|
1104
|
-
expect(path).to eq
|
1209
|
+
context 'when the path contains useless dots' do
|
1210
|
+
it 'returns the path with the useless dots interpolated' do
|
1211
|
+
path = subject_class.realpath('/test-dir/../test-dir/./sub-dir/test-file')
|
1212
|
+
expect(path).to eq '/test-dir/sub-dir/test-file'
|
1105
1213
|
end
|
1106
1214
|
end
|
1107
1215
|
|
1108
1216
|
context 'when the given path is relative' do
|
1109
|
-
context
|
1110
|
-
it
|
1111
|
-
|
1112
|
-
|
1113
|
-
|
1217
|
+
context 'and +dir_string+ is not provided' do
|
1218
|
+
it 'uses the current working directory has base directory' do
|
1219
|
+
_fs.chdir '/test-dir'
|
1220
|
+
|
1221
|
+
path = subject_class.realpath('../test-dir/./sub-dir/test-file')
|
1222
|
+
expect(path).to eq '/test-dir/sub-dir/test-file'
|
1114
1223
|
end
|
1115
1224
|
end
|
1116
1225
|
|
1117
|
-
context
|
1118
|
-
it
|
1119
|
-
path =
|
1120
|
-
expect(path).to eq
|
1226
|
+
context 'and +dir_string+ is provided' do
|
1227
|
+
it 'uses the given directory has base directory' do
|
1228
|
+
path = subject_class.realpath('../test-dir/./sub-dir/test-file', '/test-dir')
|
1229
|
+
expect(path).to eq '/test-dir/sub-dir/test-file'
|
1121
1230
|
end
|
1122
1231
|
end
|
1123
1232
|
end
|
1124
1233
|
|
1125
|
-
context
|
1126
|
-
it
|
1234
|
+
context 'when a part of the given path does not exist' do
|
1235
|
+
it 'raises an exception' do
|
1127
1236
|
expect {
|
1128
|
-
|
1237
|
+
subject_class.realpath '/no-dir/test-file'
|
1129
1238
|
}.to raise_error
|
1130
1239
|
end
|
1131
1240
|
end
|
1132
1241
|
end
|
1133
1242
|
|
1134
|
-
describe
|
1135
|
-
it
|
1136
|
-
|
1137
|
-
|
1243
|
+
describe '.rename' do
|
1244
|
+
it 'renames the given file to the new name' do
|
1245
|
+
subject_class.rename '/test-file', '/test-file2'
|
1246
|
+
|
1247
|
+
exists = subject_class.exists?('/test-file2')
|
1248
|
+
expect(exists).to be true
|
1138
1249
|
end
|
1139
1250
|
|
1140
|
-
it
|
1141
|
-
|
1251
|
+
it 'returns zero' do
|
1252
|
+
returned_value = subject_class.rename('/test-file', '/test-file2')
|
1253
|
+
expect(returned_value).to be_zero
|
1142
1254
|
end
|
1143
1255
|
end
|
1144
1256
|
|
1145
|
-
describe
|
1146
|
-
context
|
1147
|
-
context
|
1148
|
-
it
|
1149
|
-
|
1150
|
-
|
1257
|
+
describe '.setgid?' do
|
1258
|
+
context 'when the named file exists' do
|
1259
|
+
context 'and the named file has the setgid bit set' do
|
1260
|
+
it 'returns true' do
|
1261
|
+
_fs.chmod 02000, '/test-file'
|
1262
|
+
|
1263
|
+
setgid = File.setgid?('/test-file')
|
1264
|
+
expect(setgid).to be true
|
1151
1265
|
end
|
1152
1266
|
end
|
1153
1267
|
|
1154
|
-
context
|
1155
|
-
it
|
1156
|
-
|
1268
|
+
context 'and the named file does not have the setgid bit set' do
|
1269
|
+
it 'returns false' do
|
1270
|
+
setgid = File.setgid?('/test-file')
|
1271
|
+
expect(setgid).not_to be true
|
1157
1272
|
end
|
1158
1273
|
end
|
1159
1274
|
end
|
1160
1275
|
|
1161
|
-
context
|
1162
|
-
it
|
1163
|
-
|
1276
|
+
context 'when the named file does not exist' do
|
1277
|
+
it 'returns false' do
|
1278
|
+
setgid = File.setgid?('/no-file')
|
1279
|
+
expect(setgid).to be false
|
1164
1280
|
end
|
1165
1281
|
end
|
1166
1282
|
end
|
1167
1283
|
|
1168
|
-
describe
|
1169
|
-
context
|
1170
|
-
context
|
1171
|
-
it
|
1172
|
-
|
1173
|
-
|
1284
|
+
describe '.setuid?' do
|
1285
|
+
context 'when the named file exists' do
|
1286
|
+
context 'and the named file has the setuid bit set' do
|
1287
|
+
it 'returns true' do
|
1288
|
+
_fs.chmod 04000, '/test-file'
|
1289
|
+
|
1290
|
+
setuid = File.setuid?('/test-file')
|
1291
|
+
expect(setuid).to be true
|
1174
1292
|
end
|
1175
1293
|
end
|
1176
1294
|
|
1177
|
-
context
|
1178
|
-
it
|
1179
|
-
|
1295
|
+
context 'and the named file does not have the setuid bit set' do
|
1296
|
+
it 'returns false' do
|
1297
|
+
setuid = File.setuid?('/test-file')
|
1298
|
+
expect(setuid).not_to be true
|
1180
1299
|
end
|
1181
1300
|
end
|
1182
1301
|
end
|
1183
1302
|
|
1184
|
-
context
|
1185
|
-
it
|
1186
|
-
|
1303
|
+
context 'when the named file does not exist' do
|
1304
|
+
it 'returns false' do
|
1305
|
+
setuid = File.setuid?('/no-file')
|
1306
|
+
expect(setuid).to be false
|
1187
1307
|
end
|
1188
1308
|
end
|
1189
1309
|
end
|
1190
1310
|
|
1191
|
-
describe
|
1192
|
-
it
|
1193
|
-
|
1194
|
-
|
1311
|
+
describe '.size' do
|
1312
|
+
it 'returns the size of the file' do
|
1313
|
+
subject_class.open('/test-file', 'w') { |f| f.puts random_string }
|
1314
|
+
|
1315
|
+
size = subject_class.size('/test-file')
|
1316
|
+
expect(size).to eq random_string.size + 1
|
1195
1317
|
end
|
1196
1318
|
end
|
1197
1319
|
|
1198
|
-
describe
|
1199
|
-
context
|
1200
|
-
context
|
1201
|
-
it
|
1202
|
-
|
1320
|
+
describe '.size?' do
|
1321
|
+
context 'when the named file exists' do
|
1322
|
+
context 'and it is empty' do
|
1323
|
+
it 'returns false' do
|
1324
|
+
size = File.size?('/empty-file')
|
1325
|
+
expect(size).to be false
|
1203
1326
|
end
|
1204
1327
|
end
|
1205
1328
|
|
1206
|
-
context
|
1207
|
-
it
|
1329
|
+
context 'and it is not empty' do
|
1330
|
+
it 'returns the size of the file' do
|
1208
1331
|
File.open('/content-file', 'w') { |f| f.write 'test' }
|
1209
|
-
|
1332
|
+
|
1333
|
+
size = File.size?('/content-file')
|
1334
|
+
expect(size).to be 4
|
1210
1335
|
end
|
1211
1336
|
end
|
1212
1337
|
end
|
1213
1338
|
|
1214
|
-
context
|
1215
|
-
it
|
1216
|
-
|
1339
|
+
context 'when the named file does not exist' do
|
1340
|
+
it 'returns false' do
|
1341
|
+
size = File.size?('/no-file')
|
1342
|
+
expect(size).to be false
|
1217
1343
|
end
|
1218
1344
|
end
|
1219
1345
|
end
|
1220
1346
|
|
1221
|
-
describe
|
1347
|
+
describe '.socket?' do
|
1222
1348
|
# Sockets are not handled for now
|
1223
1349
|
|
1224
|
-
context
|
1225
|
-
it
|
1226
|
-
|
1227
|
-
expect(
|
1350
|
+
context 'when the named file is not a socket' do
|
1351
|
+
it 'returns false' do
|
1352
|
+
is_socket = File.socket?('/test-file')
|
1353
|
+
expect(is_socket).to be false
|
1228
1354
|
end
|
1229
1355
|
end
|
1230
1356
|
end
|
1231
1357
|
|
1232
1358
|
describe '.split' do
|
1233
|
-
it
|
1234
|
-
|
1235
|
-
expect(
|
1359
|
+
it 'splits the given string into a directory and a file component' do
|
1360
|
+
returned_value = subject_class.split('/path/to/some-file')
|
1361
|
+
expect(returned_value).to eq ['/path/to', 'some-file']
|
1236
1362
|
end
|
1237
1363
|
end
|
1238
1364
|
|
1239
1365
|
describe '.stat' do
|
1240
|
-
it
|
1241
|
-
|
1366
|
+
it 'returns a File::Stat object for the named file' do
|
1367
|
+
stat = subject_class.stat('/test-file')
|
1368
|
+
expect(stat).to be_a File::Stat
|
1242
1369
|
end
|
1243
1370
|
|
1244
|
-
it
|
1245
|
-
|
1371
|
+
it 'follows the last symbolic link' do
|
1372
|
+
stat = subject_class.stat('/test-link').symlink?
|
1373
|
+
expect(stat).to be false
|
1246
1374
|
end
|
1247
1375
|
|
1248
|
-
context
|
1249
|
-
it
|
1250
|
-
expect {
|
1376
|
+
context 'when the named file does not exist' do
|
1377
|
+
it 'raises an exception' do
|
1378
|
+
expect {
|
1379
|
+
subject_class.stat('/no-file')
|
1380
|
+
}.to raise_error Errno::ENOENT
|
1251
1381
|
end
|
1252
1382
|
end
|
1253
1383
|
|
1254
|
-
context
|
1255
|
-
context
|
1256
|
-
it
|
1257
|
-
expect {
|
1384
|
+
context 'when the named file is a symlink' do
|
1385
|
+
context 'and its target does not exist' do
|
1386
|
+
it 'raises an exception' do
|
1387
|
+
expect {
|
1388
|
+
subject_class.stat('/no-link')
|
1389
|
+
}.to raise_error Errno::ENOENT
|
1258
1390
|
end
|
1259
1391
|
end
|
1260
1392
|
end
|
1261
1393
|
|
1262
|
-
it
|
1263
|
-
|
1264
|
-
|
1394
|
+
it 'always returns a new object' do
|
1395
|
+
stat_1 = subject_class.stat('/test-file')
|
1396
|
+
stat_2 = subject_class.stat('/test-file')
|
1397
|
+
|
1398
|
+
expect(stat_2).not_to be stat_1
|
1265
1399
|
end
|
1266
1400
|
end
|
1267
1401
|
|
1268
|
-
describe
|
1269
|
-
context
|
1270
|
-
it
|
1271
|
-
|
1272
|
-
|
1273
|
-
|
1402
|
+
describe '.sticky?' do
|
1403
|
+
context 'when the named file exists' do
|
1404
|
+
it 'returns true if the named file has the sticky bit set' do
|
1405
|
+
_fs.touch '/test-file'
|
1406
|
+
_fs.chmod 01777, '/test-file'
|
1407
|
+
|
1408
|
+
sticky = File.sticky?('/test-file')
|
1409
|
+
expect(sticky).to be true
|
1274
1410
|
end
|
1275
1411
|
|
1276
1412
|
it "returns false if the named file hasn't' the sticky bit set" do
|
1277
|
-
|
1278
|
-
|
1413
|
+
_fs.touch '/test-file'
|
1414
|
+
|
1415
|
+
sticky = File.sticky?('/test-file')
|
1416
|
+
expect(sticky).to be false
|
1279
1417
|
end
|
1280
1418
|
end
|
1281
1419
|
|
1282
|
-
context
|
1283
|
-
it
|
1284
|
-
|
1420
|
+
context 'when the named file does not exist' do
|
1421
|
+
it 'returns false' do
|
1422
|
+
sticky = File.sticky?('/no-file')
|
1423
|
+
expect(sticky).to be false
|
1285
1424
|
end
|
1286
1425
|
end
|
1287
1426
|
end
|
1288
1427
|
|
1289
1428
|
describe '.symlink' do
|
1290
|
-
it
|
1291
|
-
|
1429
|
+
it 'creates a symbolic link named new_name' do
|
1430
|
+
is_symlink = subject_class.symlink?('/test-link')
|
1431
|
+
expect(is_symlink).to be true
|
1292
1432
|
end
|
1293
1433
|
|
1294
|
-
it
|
1295
|
-
|
1434
|
+
it 'creates a symbolic link that points to an entry named old_name' do
|
1435
|
+
target = _fs.find!('/test-link').target
|
1436
|
+
expect(target).to eq '/test-file'
|
1296
1437
|
end
|
1297
1438
|
|
1298
|
-
context
|
1299
|
-
it
|
1300
|
-
|
1439
|
+
context 'when the target does not exist' do
|
1440
|
+
it 'creates a symbolic link' do
|
1441
|
+
is_symlink = subject_class.symlink?('/no-link')
|
1442
|
+
expect(is_symlink).to be true
|
1301
1443
|
end
|
1302
1444
|
end
|
1303
1445
|
|
1304
|
-
it
|
1305
|
-
|
1446
|
+
it 'returns 0' do
|
1447
|
+
returned_value = subject_class.symlink('/test-file', '/new-link')
|
1448
|
+
expect(returned_value).to be_zero
|
1306
1449
|
end
|
1307
1450
|
end
|
1308
1451
|
|
1309
1452
|
describe '.symlink?' do
|
1310
|
-
context
|
1311
|
-
it
|
1312
|
-
|
1453
|
+
context 'when the named entry is a symlink' do
|
1454
|
+
it 'returns true' do
|
1455
|
+
is_symlink = subject_class.symlink?('/test-link')
|
1456
|
+
expect(is_symlink).to be true
|
1313
1457
|
end
|
1314
1458
|
end
|
1315
1459
|
|
1316
|
-
context
|
1317
|
-
it
|
1318
|
-
|
1460
|
+
context 'when the named entry is not a symlink' do
|
1461
|
+
it 'returns false' do
|
1462
|
+
is_symlink = subject_class.symlink?('/test-file')
|
1463
|
+
expect(is_symlink).to be false
|
1319
1464
|
end
|
1320
1465
|
end
|
1321
1466
|
|
1322
|
-
context
|
1323
|
-
it
|
1324
|
-
|
1467
|
+
context 'when the named entry does not exist' do
|
1468
|
+
it 'returns false' do
|
1469
|
+
is_symlink = subject_class.symlink?('/no-file')
|
1470
|
+
expect(is_symlink).to be false
|
1325
1471
|
end
|
1326
1472
|
end
|
1327
1473
|
end
|
1328
1474
|
|
1329
1475
|
describe '.truncate' do
|
1330
|
-
before
|
1331
|
-
|
1476
|
+
before do
|
1477
|
+
subject_class.open('/test-file', 'w') { |f| f.write 'x' * 50 }
|
1332
1478
|
end
|
1333
1479
|
|
1334
|
-
it
|
1335
|
-
|
1336
|
-
|
1480
|
+
it 'truncates the named file to the given size' do
|
1481
|
+
subject_class.truncate('/test-file', 5)
|
1482
|
+
|
1483
|
+
size = subject_class.size('/test-file')
|
1484
|
+
expect(size).to be 5
|
1337
1485
|
end
|
1338
1486
|
|
1339
|
-
it
|
1340
|
-
|
1341
|
-
expect(
|
1487
|
+
it 'returns zero' do
|
1488
|
+
returned_value = subject_class.truncate('/test-file', 5)
|
1489
|
+
expect(returned_value).to be_zero
|
1342
1490
|
end
|
1343
1491
|
|
1344
|
-
context
|
1345
|
-
it
|
1346
|
-
expect {
|
1492
|
+
context 'when the named file does not exist' do
|
1493
|
+
it 'raises an exception' do
|
1494
|
+
expect { subject_class.truncate '/no-file', 5 }.to raise_error
|
1347
1495
|
end
|
1348
1496
|
end
|
1349
1497
|
|
1350
|
-
context
|
1351
|
-
it
|
1352
|
-
expect {
|
1498
|
+
context 'when the given size is negative' do
|
1499
|
+
it 'it raises an exception' do
|
1500
|
+
expect { subject_class.truncate '/test-file', -1 }.to raise_error
|
1353
1501
|
end
|
1354
1502
|
end
|
1355
1503
|
end
|
1356
1504
|
|
1357
1505
|
describe '.umask' do
|
1358
|
-
before
|
1359
|
-
subject.umask(0022)
|
1360
|
-
end
|
1506
|
+
before { subject_class.umask 0022 }
|
1361
1507
|
|
1362
|
-
it
|
1363
|
-
expect(
|
1508
|
+
it 'returns the current umask value for this process' do
|
1509
|
+
expect(subject_class.umask).to be 0022
|
1364
1510
|
end
|
1365
1511
|
|
1366
|
-
context
|
1367
|
-
it
|
1368
|
-
|
1369
|
-
expect(
|
1512
|
+
context 'when the optional argument is given' do
|
1513
|
+
it 'sets the umask to that value' do
|
1514
|
+
subject_class.umask 0777
|
1515
|
+
expect(subject_class.umask).to be 0777
|
1370
1516
|
end
|
1371
1517
|
|
1372
|
-
it
|
1373
|
-
|
1518
|
+
it 'return the previous value' do
|
1519
|
+
previous_umask = subject_class.umask(0777)
|
1520
|
+
expect(previous_umask).to be 0022
|
1374
1521
|
end
|
1375
1522
|
end
|
1376
1523
|
end
|
1377
1524
|
|
1378
|
-
describe
|
1379
|
-
it
|
1380
|
-
|
1381
|
-
|
1525
|
+
describe '.unlink' do
|
1526
|
+
it 'deletes the named file' do
|
1527
|
+
subject_class.unlink('/test-file')
|
1528
|
+
|
1529
|
+
exists = subject_class.exists?('/test-file')
|
1530
|
+
expect(exists).to be false
|
1382
1531
|
end
|
1383
1532
|
|
1384
|
-
it
|
1385
|
-
|
1533
|
+
it 'returns the number of names passed as arguments' do
|
1534
|
+
returned_value = subject_class.unlink('/test-file', '/test-file2')
|
1535
|
+
expect(returned_value).to be 2
|
1386
1536
|
end
|
1387
1537
|
|
1388
|
-
context
|
1389
|
-
it
|
1390
|
-
|
1391
|
-
|
1538
|
+
context 'when multiple file names are given' do
|
1539
|
+
it 'deletes the named files' do
|
1540
|
+
subject_class.unlink '/test-file', '/test-file2'
|
1541
|
+
|
1542
|
+
exists = subject_class.exists?('/test-file2')
|
1543
|
+
expect(exists).to be false
|
1392
1544
|
end
|
1393
1545
|
end
|
1394
1546
|
|
1395
|
-
context
|
1396
|
-
it
|
1397
|
-
expect {
|
1547
|
+
context 'when the entry is a directory' do
|
1548
|
+
it 'raises an exception' do
|
1549
|
+
expect {
|
1550
|
+
subject_class.unlink '/test-dir'
|
1551
|
+
}.to raise_error Errno::EPERM
|
1398
1552
|
end
|
1399
1553
|
end
|
1400
1554
|
end
|
1401
1555
|
|
1402
1556
|
describe '.utime' do
|
1403
|
-
let(:time) { Time.now -
|
1557
|
+
let(:time) { Time.now - 500_000 }
|
1558
|
+
|
1559
|
+
it 'sets the access time of each named file to the first argument' do
|
1560
|
+
subject_class.utime time, time, '/test-file'
|
1404
1561
|
|
1405
|
-
|
1406
|
-
|
1407
|
-
expect(subject.atime('/test-file')).to eq(time)
|
1562
|
+
atime = subject_class.atime('/test-file')
|
1563
|
+
expect(atime).to eq time
|
1408
1564
|
end
|
1409
1565
|
|
1410
|
-
it
|
1411
|
-
|
1412
|
-
|
1566
|
+
it 'sets the modification time of each named file to the second argument' do
|
1567
|
+
subject_class.utime time, time, '/test-file'
|
1568
|
+
|
1569
|
+
mtime = subject_class.mtime('/test-file')
|
1570
|
+
expect(mtime).to eq time
|
1413
1571
|
end
|
1414
1572
|
|
1415
|
-
it
|
1416
|
-
|
1573
|
+
it 'returns the number of file names in the argument list' do
|
1574
|
+
utime = subject_class.utime(time, time, '/test-file', '/test-file2')
|
1575
|
+
expect(utime).to be 2
|
1417
1576
|
end
|
1418
1577
|
|
1419
|
-
it
|
1578
|
+
it 'raises en error if the entry does not exist' do
|
1420
1579
|
expect {
|
1421
|
-
|
1422
|
-
}.to raise_error
|
1580
|
+
subject_class.utime time, time, '/no-file'
|
1581
|
+
}.to raise_error Errno::ENOENT
|
1423
1582
|
end
|
1424
1583
|
end
|
1425
1584
|
|
1426
1585
|
describe '.world_readable?' do
|
1427
|
-
before
|
1428
|
-
subject.chmod(access, '/test-file')
|
1429
|
-
end
|
1586
|
+
before { subject_class.chmod access, '/test-file' }
|
1430
1587
|
|
1431
1588
|
context 'when file_name is readable by others' do
|
1432
1589
|
let(:access) { MemFs::Fake::Entry::OREAD }
|
1433
1590
|
|
1434
1591
|
it 'returns an integer representing the file permission bits' do
|
1435
|
-
|
1436
|
-
expect(
|
1592
|
+
world_readable = subject_class.world_readable?('/test-file')
|
1593
|
+
expect(world_readable).to eq MemFs::Fake::Entry::OREAD
|
1437
1594
|
end
|
1438
1595
|
end
|
1439
1596
|
|
@@ -1441,23 +1598,21 @@ module MemFs
|
|
1441
1598
|
let(:access) { MemFs::Fake::Entry::UREAD }
|
1442
1599
|
|
1443
1600
|
it 'returns nil' do
|
1444
|
-
|
1445
|
-
expect(
|
1601
|
+
world_readable = subject_class.world_readable?('/test-file')
|
1602
|
+
expect(world_readable).to be_nil
|
1446
1603
|
end
|
1447
1604
|
end
|
1448
1605
|
end
|
1449
1606
|
|
1450
1607
|
describe '.world_writable?' do
|
1451
|
-
before
|
1452
|
-
subject.chmod(access, '/test-file')
|
1453
|
-
end
|
1608
|
+
before { subject_class.chmod access, '/test-file' }
|
1454
1609
|
|
1455
1610
|
context 'when file_name is writable by others' do
|
1456
1611
|
let(:access) { MemFs::Fake::Entry::OWRITE }
|
1457
1612
|
|
1458
1613
|
it 'returns an integer representing the file permission bits' do
|
1459
|
-
|
1460
|
-
expect(
|
1614
|
+
world_writable = subject_class.world_writable?('/test-file')
|
1615
|
+
expect(world_writable).to eq MemFs::Fake::Entry::OWRITE
|
1461
1616
|
end
|
1462
1617
|
end
|
1463
1618
|
|
@@ -1465,512 +1620,1023 @@ module MemFs
|
|
1465
1620
|
let(:access) { MemFs::Fake::Entry::UWRITE }
|
1466
1621
|
|
1467
1622
|
it 'returns nil' do
|
1468
|
-
|
1469
|
-
expect(
|
1623
|
+
world_writable = subject_class.world_writable?('/test-file')
|
1624
|
+
expect(world_writable).to be_nil
|
1470
1625
|
end
|
1471
1626
|
end
|
1472
1627
|
end
|
1473
1628
|
|
1474
|
-
describe
|
1629
|
+
describe '.writable?' do
|
1475
1630
|
let(:access) { 0 }
|
1476
1631
|
let(:gid) { 0 }
|
1477
1632
|
let(:uid) { 0 }
|
1478
1633
|
|
1479
|
-
before
|
1480
|
-
|
1481
|
-
|
1634
|
+
before do
|
1635
|
+
subject_class.chmod access, '/test-file'
|
1636
|
+
subject_class.chown uid, gid, '/test-file'
|
1482
1637
|
end
|
1483
1638
|
|
1484
|
-
context
|
1485
|
-
it
|
1486
|
-
writable =
|
1487
|
-
expect(writable).to
|
1639
|
+
context 'when the file is not writable by anyone' do
|
1640
|
+
it 'return false' do
|
1641
|
+
writable = subject_class.writable?('/test-file')
|
1642
|
+
expect(writable).to be false
|
1488
1643
|
end
|
1489
1644
|
end
|
1490
1645
|
|
1491
|
-
context
|
1646
|
+
context 'when the file is user writable' do
|
1492
1647
|
let(:access) { MemFs::Fake::Entry::UWRITE }
|
1493
1648
|
|
1494
|
-
context
|
1495
|
-
before
|
1649
|
+
context 'and the current user owns the file' do
|
1650
|
+
before { subject_class.chown uid, 0, '/test-file' }
|
1651
|
+
|
1496
1652
|
let(:uid) { Process.euid }
|
1497
1653
|
|
1498
|
-
it
|
1499
|
-
writable =
|
1500
|
-
expect(writable).to
|
1654
|
+
it 'returns true' do
|
1655
|
+
writable = subject_class.writable?('/test-file')
|
1656
|
+
expect(writable).to be true
|
1501
1657
|
end
|
1502
1658
|
end
|
1503
1659
|
end
|
1504
1660
|
|
1505
|
-
context
|
1661
|
+
context 'when the file is group writable' do
|
1506
1662
|
let(:access) { MemFs::Fake::Entry::GWRITE }
|
1507
1663
|
|
1508
|
-
context
|
1664
|
+
context 'and the current user is part of the owner group' do
|
1509
1665
|
let(:gid) { Process.egid }
|
1510
1666
|
|
1511
|
-
it
|
1512
|
-
writable =
|
1513
|
-
expect(writable).to
|
1667
|
+
it 'returns true' do
|
1668
|
+
writable = subject_class.writable?('/test-file')
|
1669
|
+
expect(writable).to be true
|
1514
1670
|
end
|
1515
1671
|
end
|
1516
1672
|
end
|
1517
1673
|
|
1518
|
-
context
|
1674
|
+
context 'when the file is writable by anyone' do
|
1519
1675
|
let(:access) { MemFs::Fake::Entry::OWRITE }
|
1520
1676
|
|
1521
|
-
context
|
1522
|
-
it
|
1523
|
-
writable =
|
1524
|
-
expect(writable).to
|
1677
|
+
context 'and the user has no specific right on it' do
|
1678
|
+
it 'returns true' do
|
1679
|
+
writable = subject_class.writable?('/test-file')
|
1680
|
+
expect(writable).to be true
|
1525
1681
|
end
|
1526
1682
|
end
|
1527
1683
|
end
|
1528
1684
|
|
1529
|
-
context
|
1530
|
-
it
|
1531
|
-
writable =
|
1532
|
-
expect(writable).to
|
1685
|
+
context 'when the file does not exist' do
|
1686
|
+
it 'returns false' do
|
1687
|
+
writable = subject_class.writable?('/no-file')
|
1688
|
+
expect(writable).to be false
|
1533
1689
|
end
|
1534
1690
|
end
|
1535
1691
|
end
|
1536
1692
|
|
1537
|
-
describe
|
1693
|
+
describe '.writable_real?' do
|
1538
1694
|
let(:access) { 0 }
|
1539
1695
|
let(:gid) { 0 }
|
1540
1696
|
let(:uid) { 0 }
|
1541
1697
|
|
1542
|
-
before
|
1543
|
-
|
1544
|
-
|
1698
|
+
before do
|
1699
|
+
subject_class.chmod access, '/test-file'
|
1700
|
+
subject_class.chown uid, gid, '/test-file'
|
1545
1701
|
end
|
1546
1702
|
|
1547
|
-
context
|
1548
|
-
it
|
1549
|
-
|
1550
|
-
expect(
|
1703
|
+
context 'when the file is not writable by anyone' do
|
1704
|
+
it 'return false' do
|
1705
|
+
writable_real = subject_class.writable_real?('/test-file')
|
1706
|
+
expect(writable_real).to be false
|
1551
1707
|
end
|
1552
1708
|
end
|
1553
1709
|
|
1554
|
-
context
|
1710
|
+
context 'when the file is user writable' do
|
1555
1711
|
let(:access) { MemFs::Fake::Entry::UWRITE }
|
1556
1712
|
|
1557
|
-
context
|
1558
|
-
before(:each) { subject.chown(uid, 0, '/test-file') }
|
1713
|
+
context 'and the current user owns the file' do
|
1559
1714
|
let(:uid) { Process.uid }
|
1560
1715
|
|
1561
|
-
|
1562
|
-
|
1563
|
-
|
1716
|
+
before { subject_class.chown uid, 0, '/test-file' }
|
1717
|
+
|
1718
|
+
it 'returns true' do
|
1719
|
+
writable_real = subject_class.writable_real?('/test-file')
|
1720
|
+
expect(writable_real).to be true
|
1564
1721
|
end
|
1565
1722
|
end
|
1566
1723
|
end
|
1567
1724
|
|
1568
|
-
context
|
1725
|
+
context 'when the file is group writable' do
|
1569
1726
|
let(:access) { MemFs::Fake::Entry::GWRITE }
|
1570
1727
|
|
1571
|
-
context
|
1728
|
+
context 'and the current user is part of the owner group' do
|
1572
1729
|
let(:gid) { Process.gid }
|
1573
1730
|
|
1574
|
-
it
|
1575
|
-
|
1576
|
-
expect(
|
1731
|
+
it 'returns true' do
|
1732
|
+
writable_real = subject_class.writable_real?('/test-file')
|
1733
|
+
expect(writable_real).to be true
|
1577
1734
|
end
|
1578
1735
|
end
|
1579
1736
|
end
|
1580
1737
|
|
1581
|
-
context
|
1738
|
+
context 'when the file is writable by anyone' do
|
1582
1739
|
let(:access) { MemFs::Fake::Entry::OWRITE }
|
1583
1740
|
|
1584
|
-
context
|
1585
|
-
it
|
1586
|
-
|
1587
|
-
expect(
|
1741
|
+
context 'and the user has no specific right on it' do
|
1742
|
+
it 'returns true' do
|
1743
|
+
writable_real = subject_class.writable_real?('/test-file')
|
1744
|
+
expect(writable_real).to be true
|
1588
1745
|
end
|
1589
1746
|
end
|
1590
1747
|
end
|
1591
1748
|
|
1592
|
-
context
|
1593
|
-
it
|
1594
|
-
|
1595
|
-
expect(
|
1749
|
+
context 'when the file does not exist' do
|
1750
|
+
it 'returns false' do
|
1751
|
+
writable_real = subject_class.writable_real?('/no-file')
|
1752
|
+
expect(writable_real).to be false
|
1596
1753
|
end
|
1597
1754
|
end
|
1598
1755
|
end
|
1599
1756
|
|
1600
|
-
describe
|
1601
|
-
context
|
1602
|
-
context
|
1603
|
-
it
|
1604
|
-
zero =
|
1605
|
-
expect(zero).to
|
1757
|
+
describe '.zero?' do
|
1758
|
+
context 'when the named file exists' do
|
1759
|
+
context 'and has a zero size' do
|
1760
|
+
it 'returns true' do
|
1761
|
+
zero = subject_class.zero?('/test-file')
|
1762
|
+
expect(zero).to be true
|
1606
1763
|
end
|
1607
1764
|
end
|
1608
1765
|
|
1609
|
-
context
|
1610
|
-
before
|
1766
|
+
context 'and does not have a zero size' do
|
1767
|
+
before do
|
1611
1768
|
File.open('/test-file', 'w') { |f| f.puts 'test' }
|
1612
1769
|
end
|
1613
1770
|
|
1614
|
-
it
|
1615
|
-
zero =
|
1616
|
-
expect(zero).to
|
1771
|
+
it 'returns false' do
|
1772
|
+
zero = subject_class.zero?('/test-file')
|
1773
|
+
expect(zero).to be false
|
1774
|
+
end
|
1775
|
+
end
|
1776
|
+
end
|
1777
|
+
|
1778
|
+
context 'when the named file does not exist' do
|
1779
|
+
it 'returns false' do
|
1780
|
+
zero = subject_class.zero?('/no-file')
|
1781
|
+
expect(zero).to be false
|
1782
|
+
end
|
1783
|
+
end
|
1784
|
+
end
|
1785
|
+
|
1786
|
+
describe '#<<' do
|
1787
|
+
it 'writes the given string in the file' do
|
1788
|
+
write_subject << 'Hello'
|
1789
|
+
|
1790
|
+
content = write_subject.send(:content)
|
1791
|
+
expect(content).to eq 'Hello'
|
1792
|
+
end
|
1793
|
+
|
1794
|
+
it 'can be chained' do
|
1795
|
+
write_subject << 'Hello ' << "World\n"
|
1796
|
+
|
1797
|
+
content = write_subject.send(:content)
|
1798
|
+
expect(content).to eq "Hello World\n"
|
1799
|
+
end
|
1800
|
+
|
1801
|
+
context 'when the given object is not a string' do
|
1802
|
+
it 'converts the object to a string with to_s' do
|
1803
|
+
write_subject << 42
|
1804
|
+
|
1805
|
+
content = write_subject.send(:content)
|
1806
|
+
expect(content).to eq '42'
|
1807
|
+
end
|
1808
|
+
end
|
1809
|
+
|
1810
|
+
context 'when the file is not opened for writing' do
|
1811
|
+
it 'raises an exception' do
|
1812
|
+
expect { subject << 'Hello' }.to raise_error IOError
|
1813
|
+
end
|
1814
|
+
end
|
1815
|
+
end
|
1816
|
+
|
1817
|
+
describe '#advise' do
|
1818
|
+
it 'returns nil' do
|
1819
|
+
returned_value = subject.advise(:normal)
|
1820
|
+
expect(returned_value).to be_nil
|
1821
|
+
end
|
1822
|
+
|
1823
|
+
shared_examples 'advise working' do |advise_type|
|
1824
|
+
context "when the #{advise_type.inspect} advise type is given" do
|
1825
|
+
it 'does not raise an error ' do
|
1826
|
+
expect { subject.advise(advise_type) }.not_to raise_error
|
1617
1827
|
end
|
1618
1828
|
end
|
1619
1829
|
end
|
1620
1830
|
|
1621
|
-
|
1622
|
-
|
1623
|
-
|
1624
|
-
|
1831
|
+
it_behaves_like 'advise working', :normal
|
1832
|
+
it_behaves_like 'advise working', :sequential
|
1833
|
+
it_behaves_like 'advise working', :random
|
1834
|
+
it_behaves_like 'advise working', :willneed
|
1835
|
+
it_behaves_like 'advise working', :dontneed
|
1836
|
+
it_behaves_like 'advise working', :noreuse
|
1837
|
+
|
1838
|
+
context 'when a wrong advise type is given' do
|
1839
|
+
it 'raises an exception' do
|
1840
|
+
expect { subject.advise(:wrong) }.to raise_error NotImplementedError
|
1841
|
+
end
|
1842
|
+
end
|
1843
|
+
end
|
1844
|
+
|
1845
|
+
describe '#atime' do
|
1846
|
+
it 'returns a Time object' do
|
1847
|
+
expect(subject.atime).to be_a Time
|
1848
|
+
end
|
1849
|
+
end
|
1850
|
+
|
1851
|
+
describe '#autoclose=' do
|
1852
|
+
it 'sets the autoclose flag' do
|
1853
|
+
subject.autoclose = false
|
1854
|
+
|
1855
|
+
expect(subject.autoclose?).to be false
|
1856
|
+
end
|
1857
|
+
end
|
1858
|
+
|
1859
|
+
describe '#autoclose?' do
|
1860
|
+
it "returns true by default" do
|
1861
|
+
expect(subject.autoclose?).to be true
|
1862
|
+
end
|
1863
|
+
|
1864
|
+
context 'when the file will be automatically closed' do
|
1865
|
+
before { subject.autoclose = true }
|
1866
|
+
|
1867
|
+
it 'returns true' do
|
1868
|
+
expect(subject.autoclose?).to be true
|
1869
|
+
end
|
1870
|
+
end
|
1871
|
+
|
1872
|
+
context 'when the file will not be automatically closed' do
|
1873
|
+
before { subject.autoclose = false }
|
1874
|
+
|
1875
|
+
it 'returns false' do
|
1876
|
+
expect(subject.autoclose?).to be false
|
1877
|
+
end
|
1878
|
+
end
|
1879
|
+
end
|
1880
|
+
|
1881
|
+
describe '#binmode' do
|
1882
|
+
it 'returns the file itself' do
|
1883
|
+
returned_value = subject.binmode
|
1884
|
+
expect(returned_value).to be subject
|
1885
|
+
end
|
1886
|
+
|
1887
|
+
it 'sets the binmode flag for the file' do
|
1888
|
+
subject.binmode
|
1889
|
+
expect(subject.binmode?).to be true
|
1890
|
+
end
|
1891
|
+
|
1892
|
+
it "sets the file encoding to ASCII-8BIT" do
|
1893
|
+
subject.binmode
|
1894
|
+
|
1895
|
+
encoding = subject.external_encoding
|
1896
|
+
expect(encoding).to be Encoding::ASCII_8BIT
|
1897
|
+
end
|
1898
|
+
end
|
1899
|
+
|
1900
|
+
describe '#binmode?' do
|
1901
|
+
it "returns false by default" do
|
1902
|
+
expect(subject.binmode?).to be false
|
1903
|
+
end
|
1904
|
+
|
1905
|
+
context 'when the file is in binmode' do
|
1906
|
+
before { subject.binmode }
|
1907
|
+
|
1908
|
+
it 'returns true' do
|
1909
|
+
expect(subject.binmode?).to be true
|
1625
1910
|
end
|
1626
1911
|
end
|
1627
1912
|
end
|
1628
1913
|
|
1914
|
+
describe '#bytes' do
|
1915
|
+
it_behaves_like 'aliased method', :bytes, :each_byte
|
1916
|
+
end
|
1917
|
+
|
1918
|
+
describe '#chars' do
|
1919
|
+
it_behaves_like 'aliased method', :chars, :each_char
|
1920
|
+
end
|
1921
|
+
|
1629
1922
|
describe '#chmod' do
|
1630
|
-
it
|
1631
|
-
|
1632
|
-
|
1923
|
+
it 'changes permission bits on the file' do
|
1924
|
+
subject.chmod 0777
|
1925
|
+
|
1926
|
+
mode = subject.stat.mode
|
1927
|
+
expect(mode).to be 0100777
|
1633
1928
|
end
|
1634
1929
|
|
1635
|
-
it
|
1636
|
-
|
1930
|
+
it 'returns zero' do
|
1931
|
+
returned_value = subject.chmod(0777)
|
1932
|
+
expect(returned_value).to be_zero
|
1637
1933
|
end
|
1638
1934
|
end
|
1639
1935
|
|
1640
|
-
describe
|
1641
|
-
it
|
1642
|
-
|
1643
|
-
|
1936
|
+
describe '#chown' do
|
1937
|
+
it 'changes the owner of the named file to the given numeric owner id' do
|
1938
|
+
subject.chown 42, nil
|
1939
|
+
|
1940
|
+
uid = subject.stat.uid
|
1941
|
+
expect(uid).to be 42
|
1644
1942
|
end
|
1645
1943
|
|
1646
|
-
it
|
1647
|
-
|
1648
|
-
|
1944
|
+
it 'changes owner on the named files (in list)' do
|
1945
|
+
subject.chown 42
|
1946
|
+
|
1947
|
+
uid = subject.stat.uid
|
1948
|
+
expect(uid).to be(42)
|
1649
1949
|
end
|
1650
1950
|
|
1651
|
-
it
|
1652
|
-
|
1653
|
-
|
1951
|
+
it 'changes the group of the named file to the given numeric group id' do
|
1952
|
+
subject.chown nil, 42
|
1953
|
+
|
1954
|
+
gid = subject.stat.gid
|
1955
|
+
expect(gid).to be 42
|
1654
1956
|
end
|
1655
1957
|
|
1656
|
-
it
|
1657
|
-
|
1958
|
+
it 'returns zero' do
|
1959
|
+
returned_value = subject.chown(42, 42)
|
1960
|
+
expect(returned_value).to be_zero
|
1658
1961
|
end
|
1659
1962
|
|
1660
|
-
it
|
1963
|
+
it 'ignores nil user id' do
|
1661
1964
|
expect {
|
1662
|
-
|
1663
|
-
}.to_not change{
|
1965
|
+
subject.chown nil, 42
|
1966
|
+
}.to_not change { subject.stat.uid }
|
1664
1967
|
end
|
1665
1968
|
|
1666
|
-
it
|
1969
|
+
it 'ignores nil group id' do
|
1667
1970
|
expect {
|
1668
|
-
|
1669
|
-
}.to_not change{
|
1971
|
+
subject.chown 42, nil
|
1972
|
+
}.to_not change { subject.stat.gid }
|
1670
1973
|
end
|
1671
1974
|
|
1672
|
-
it
|
1975
|
+
it 'ignores -1 user id' do
|
1673
1976
|
expect {
|
1674
|
-
|
1675
|
-
}.to_not change{
|
1977
|
+
subject.chown -1, 42
|
1978
|
+
}.to_not change { subject.stat.uid }
|
1676
1979
|
end
|
1677
1980
|
|
1678
|
-
it
|
1981
|
+
it 'ignores -1 group id' do
|
1679
1982
|
expect {
|
1680
|
-
|
1681
|
-
}.to_not change{
|
1983
|
+
subject.chown 42, -1
|
1984
|
+
}.to_not change { subject.stat.gid }
|
1682
1985
|
end
|
1683
1986
|
|
1684
|
-
context
|
1685
|
-
let(:symlink) {
|
1987
|
+
context 'when the named entry is a symlink' do
|
1988
|
+
let(:symlink) { subject_class.new('/test-link') }
|
1989
|
+
|
1990
|
+
it 'changes the owner on the last target of the link chain' do
|
1991
|
+
symlink.chown 42, nil
|
1686
1992
|
|
1687
|
-
|
1688
|
-
|
1689
|
-
expect(file.stat.uid).to be(42)
|
1993
|
+
uid = subject.stat.uid
|
1994
|
+
expect(uid).to be 42
|
1690
1995
|
end
|
1691
1996
|
|
1692
|
-
it
|
1693
|
-
symlink.chown
|
1694
|
-
|
1997
|
+
it 'changes the group on the last target of the link chain' do
|
1998
|
+
symlink.chown nil, 42
|
1999
|
+
|
2000
|
+
gid = subject.stat.gid
|
2001
|
+
expect(gid).to be 42
|
1695
2002
|
end
|
1696
2003
|
|
1697
|
-
it
|
1698
|
-
symlink.chown
|
1699
|
-
|
2004
|
+
it 'does not change the owner of the symlink' do
|
2005
|
+
symlink.chown 42, nil
|
2006
|
+
|
2007
|
+
uid = symlink.lstat.uid
|
2008
|
+
expect(uid).not_to be 42
|
1700
2009
|
end
|
1701
2010
|
|
1702
|
-
it
|
1703
|
-
symlink.chown
|
1704
|
-
|
2011
|
+
it 'does not change the group of the symlink' do
|
2012
|
+
symlink.chown nil, 42
|
2013
|
+
|
2014
|
+
gid = symlink.lstat.gid
|
2015
|
+
expect(gid).not_to be 42
|
1705
2016
|
end
|
1706
2017
|
end
|
1707
2018
|
end
|
1708
2019
|
|
1709
|
-
describe
|
1710
|
-
it
|
1711
|
-
|
1712
|
-
|
1713
|
-
|
2020
|
+
describe '#close' do
|
2021
|
+
it 'closes the file stream' do
|
2022
|
+
subject.close
|
2023
|
+
expect(subject).to be_closed
|
2024
|
+
end
|
2025
|
+
end
|
2026
|
+
|
2027
|
+
describe '#closed?' do
|
2028
|
+
it 'returns true when the file is closed' do
|
2029
|
+
subject.close
|
2030
|
+
expect(subject.closed?).to be true
|
2031
|
+
end
|
2032
|
+
|
2033
|
+
it 'returns false when the file is open' do
|
2034
|
+
expect(subject.closed?).to be false
|
1714
2035
|
end
|
1715
2036
|
end
|
1716
2037
|
|
1717
|
-
describe
|
1718
|
-
it
|
1719
|
-
|
1720
|
-
|
1721
|
-
expect(
|
2038
|
+
describe '#close_on_exec=' do
|
2039
|
+
it 'sets the close-on-exec flag on the file' do
|
2040
|
+
subject.close_on_exec = false
|
2041
|
+
|
2042
|
+
expect(subject.close_on_exec?).to be false
|
2043
|
+
end
|
2044
|
+
end
|
2045
|
+
|
2046
|
+
describe '#close_on_exec?' do
|
2047
|
+
it 'returns true by default' do
|
2048
|
+
expect(subject.close_on_exec?).to be true
|
2049
|
+
end
|
2050
|
+
|
2051
|
+
context "when the close-on-exec flag is set to false" do
|
2052
|
+
before { subject.close_on_exec = false }
|
2053
|
+
|
2054
|
+
it 'returns false' do
|
2055
|
+
expect(subject.close_on_exec?).to be false
|
2056
|
+
end
|
1722
2057
|
end
|
2058
|
+
end
|
1723
2059
|
|
1724
|
-
|
1725
|
-
|
1726
|
-
expect(
|
1727
|
-
file.close
|
2060
|
+
describe '#ctime' do
|
2061
|
+
it 'returns a Time object' do
|
2062
|
+
expect(subject.ctime).to be_a Time
|
1728
2063
|
end
|
1729
2064
|
end
|
1730
2065
|
|
1731
2066
|
describe '#each' do
|
1732
|
-
let(:file) { subject.open('/test-file') }
|
1733
2067
|
let(:lines) do
|
1734
|
-
["Hello this is a file\n",
|
2068
|
+
["Hello this is a file\n",
|
2069
|
+
"with some lines\n",
|
2070
|
+
"for test purpose\n"]
|
1735
2071
|
end
|
1736
2072
|
|
1737
2073
|
before do
|
1738
|
-
|
2074
|
+
File.open('/test-file', 'w') do |f|
|
1739
2075
|
lines.each { |line| f.puts line }
|
1740
2076
|
end
|
1741
2077
|
end
|
1742
2078
|
|
1743
|
-
it '
|
1744
|
-
expect{ |blk|
|
1745
|
-
yield_successive_args(*lines)
|
2079
|
+
it 'calls the block for every line in the file' do
|
2080
|
+
expect { |blk| subject.each(&blk) }.to yield_successive_args(*lines)
|
1746
2081
|
end
|
1747
2082
|
|
1748
2083
|
it 'returns the file itself' do
|
1749
|
-
|
2084
|
+
returned_value = subject.each {}
|
2085
|
+
expect(returned_value).to be subject
|
1750
2086
|
end
|
1751
2087
|
|
1752
2088
|
context 'when a separator is given' do
|
1753
2089
|
it 'uses this separator to split lines' do
|
1754
2090
|
expected_lines = [
|
1755
|
-
|
2091
|
+
'Hello this is a f',
|
1756
2092
|
"ile\nwith some lines\nf",
|
1757
2093
|
"or test purpose\n"
|
1758
2094
|
]
|
1759
|
-
expect{ |blk|
|
2095
|
+
expect { |blk| subject.each('f', &blk) }.to \
|
1760
2096
|
yield_successive_args(*expected_lines)
|
1761
2097
|
end
|
1762
2098
|
end
|
1763
2099
|
|
1764
2100
|
context 'when the file is not open for reading' do
|
1765
|
-
|
2101
|
+
it 'raises an exception' do
|
2102
|
+
expect {
|
2103
|
+
write_subject.each { |l| puts l }
|
2104
|
+
}.to raise_error IOError
|
2105
|
+
end
|
2106
|
+
|
2107
|
+
context 'when no block is given' do
|
2108
|
+
it 'does not raise an exception' do
|
2109
|
+
expect { write_subject.each }.not_to raise_error
|
2110
|
+
end
|
2111
|
+
end
|
2112
|
+
end
|
2113
|
+
|
2114
|
+
context 'when no block is given' do
|
2115
|
+
it 'returns an enumerator' do
|
2116
|
+
expect(subject.each.next).to eq "Hello this is a file\n"
|
2117
|
+
end
|
2118
|
+
end
|
2119
|
+
end
|
2120
|
+
|
2121
|
+
describe '#each_byte' do
|
2122
|
+
before do
|
2123
|
+
subject_class.open('/test-file', 'w') { |f| f << 'test' }
|
2124
|
+
end
|
2125
|
+
|
2126
|
+
it 'calls the given block once for each byte of the file' do
|
2127
|
+
expect { |blk|
|
2128
|
+
subject.each_byte(&blk)
|
2129
|
+
}.to yield_successive_args 116, 101, 115, 116
|
2130
|
+
end
|
2131
|
+
|
2132
|
+
it 'returns the file itself' do
|
2133
|
+
returned_value = subject.each_byte {}
|
2134
|
+
expect(returned_value).to be subject
|
2135
|
+
end
|
2136
|
+
|
2137
|
+
context 'when the file is not open for reading' do
|
2138
|
+
it 'raises an exception' do
|
2139
|
+
expect {
|
2140
|
+
write_subject.each_byte { |b| }
|
2141
|
+
}.to raise_error IOError
|
2142
|
+
end
|
2143
|
+
|
2144
|
+
context 'when no block is given' do
|
2145
|
+
it 'does not raise an exception' do
|
2146
|
+
expect { write_subject.each_byte }.not_to raise_error
|
2147
|
+
end
|
2148
|
+
end
|
2149
|
+
end
|
2150
|
+
|
2151
|
+
context 'when no block is given' do
|
2152
|
+
it 'returns an enumerator' do
|
2153
|
+
expect(subject.each_byte.next).to eq 116
|
2154
|
+
end
|
2155
|
+
end
|
2156
|
+
end
|
2157
|
+
|
2158
|
+
describe '#each_char' do
|
2159
|
+
before do
|
2160
|
+
subject_class.open('/test-file', 'w') { |f| f << 'test' }
|
2161
|
+
end
|
1766
2162
|
|
2163
|
+
it 'calls the given block once for each byte of the file' do
|
2164
|
+
expect { |blk|
|
2165
|
+
subject.each_char(&blk)
|
2166
|
+
}.to yield_successive_args 't', 'e', 's', 't'
|
2167
|
+
end
|
2168
|
+
|
2169
|
+
it 'returns the file itself' do
|
2170
|
+
returned_value = subject.each_char {}
|
2171
|
+
expect(returned_value).to be subject
|
2172
|
+
end
|
2173
|
+
|
2174
|
+
context 'when the file is not open for reading' do
|
1767
2175
|
it 'raises an exception' do
|
1768
|
-
expect
|
2176
|
+
expect {
|
2177
|
+
write_subject.each_char { |b| }
|
2178
|
+
}.to raise_error IOError
|
1769
2179
|
end
|
1770
2180
|
|
1771
2181
|
context 'when no block is given' do
|
1772
2182
|
it 'does not raise an exception' do
|
1773
|
-
expect{
|
2183
|
+
expect { write_subject.each_char }.not_to raise_error
|
1774
2184
|
end
|
1775
2185
|
end
|
1776
2186
|
end
|
1777
2187
|
|
1778
2188
|
context 'when no block is given' do
|
1779
2189
|
it 'returns an enumerator' do
|
1780
|
-
expect(
|
2190
|
+
expect(subject.each_char.next).to eq 't'
|
2191
|
+
end
|
2192
|
+
end
|
2193
|
+
end
|
2194
|
+
|
2195
|
+
describe '#eof' do
|
2196
|
+
it_behaves_like 'aliased method', :eof, :eof?
|
2197
|
+
end
|
2198
|
+
|
2199
|
+
describe '#eof?' do
|
2200
|
+
context 'when the file is not empty' do
|
2201
|
+
before do
|
2202
|
+
File.open('/test-file', 'w') { |f| f.puts 'test' }
|
2203
|
+
end
|
2204
|
+
|
2205
|
+
context 'and the file is not yet read' do
|
2206
|
+
it 'returns false' do
|
2207
|
+
expect(subject.eof?).to be false
|
2208
|
+
end
|
2209
|
+
end
|
2210
|
+
|
2211
|
+
context 'and the file is partly read' do
|
2212
|
+
before { subject.read(2) }
|
2213
|
+
|
2214
|
+
it 'returns false' do
|
2215
|
+
expect(subject.eof?).to be false
|
2216
|
+
end
|
2217
|
+
end
|
2218
|
+
|
2219
|
+
context 'and the file is read' do
|
2220
|
+
before { subject.read }
|
2221
|
+
|
2222
|
+
it 'returns true' do
|
2223
|
+
expect(subject.eof?).to be true
|
2224
|
+
end
|
2225
|
+
end
|
2226
|
+
end
|
2227
|
+
|
2228
|
+
context 'when the file is not empty' do
|
2229
|
+
context 'and the file is not yet read' do
|
2230
|
+
it 'returns true' do
|
2231
|
+
expect(subject.eof?).to be true
|
2232
|
+
end
|
2233
|
+
end
|
2234
|
+
|
2235
|
+
context 'and the file is read' do
|
2236
|
+
before { subject.read }
|
2237
|
+
|
2238
|
+
it 'returns true' do
|
2239
|
+
expect(subject.eof?).to be true
|
2240
|
+
end
|
2241
|
+
end
|
2242
|
+
end
|
2243
|
+
end
|
2244
|
+
|
2245
|
+
describe '#external_encoding' do
|
2246
|
+
it 'returns the Encoding object representing the file encoding' do
|
2247
|
+
expect(subject.external_encoding).to be_an Encoding
|
2248
|
+
end
|
2249
|
+
|
2250
|
+
context 'when the file is open in write mode' do
|
2251
|
+
context 'and no encoding has been specified' do
|
2252
|
+
it 'returns nil' do
|
2253
|
+
expect(write_subject.external_encoding).to be_nil
|
2254
|
+
end
|
1781
2255
|
end
|
2256
|
+
|
2257
|
+
context 'and an encoding has been specified' do
|
2258
|
+
subject { File.open('/test-file', 'w', external_encoding: 'UTF-8') }
|
2259
|
+
|
2260
|
+
it 'returns the Encoding' do
|
2261
|
+
expect(subject.external_encoding).to be_an Encoding
|
2262
|
+
end
|
2263
|
+
end
|
2264
|
+
end
|
2265
|
+
end
|
2266
|
+
|
2267
|
+
describe '#flock' do
|
2268
|
+
it 'returns zero' do
|
2269
|
+
returned_value = subject.flock(File::LOCK_EX)
|
2270
|
+
expect(returned_value).to be_zero
|
1782
2271
|
end
|
1783
2272
|
end
|
1784
2273
|
|
1785
2274
|
describe '#lstat' do
|
1786
|
-
it
|
1787
|
-
expect(
|
2275
|
+
it 'returns the File::Stat object of the file' do
|
2276
|
+
expect(subject.lstat).to be_a File::Stat
|
1788
2277
|
end
|
1789
2278
|
|
1790
|
-
it
|
1791
|
-
file =
|
1792
|
-
|
2279
|
+
it 'does not follow the last symbolic link' do
|
2280
|
+
file = subject_class.new('/test-link')
|
2281
|
+
|
2282
|
+
is_symlink = file.lstat.symlink?
|
2283
|
+
expect(is_symlink).to be true
|
1793
2284
|
end
|
1794
2285
|
|
1795
|
-
context
|
1796
|
-
context
|
1797
|
-
it
|
1798
|
-
file =
|
1799
|
-
expect { file.lstat }.not_to raise_error
|
2286
|
+
context 'when the named file is a symlink' do
|
2287
|
+
context 'and its target does not exist' do
|
2288
|
+
it 'ignores errors' do
|
2289
|
+
file = subject_class.new('/no-link')
|
2290
|
+
expect { file.lstat }.not_to raise_error
|
1800
2291
|
end
|
1801
2292
|
end
|
1802
2293
|
end
|
1803
2294
|
end
|
1804
2295
|
|
1805
|
-
describe
|
1806
|
-
it
|
1807
|
-
|
1808
|
-
|
2296
|
+
describe '#mtime' do
|
2297
|
+
it 'returns a Time object' do
|
2298
|
+
expect(subject.mtime).to be_a Time
|
2299
|
+
end
|
2300
|
+
end
|
2301
|
+
|
2302
|
+
describe '#pos' do
|
2303
|
+
before do
|
2304
|
+
File.open('/test-file', 'w') { |f| f.puts 'test' }
|
2305
|
+
end
|
2306
|
+
|
2307
|
+
it 'returns zero when the file was just opened' do
|
2308
|
+
expect(subject.pos).to be_zero
|
2309
|
+
end
|
2310
|
+
|
2311
|
+
it 'returns the reading offset when some of the file has been read' do
|
2312
|
+
subject.read 2
|
2313
|
+
expect(subject.pos).to be 2
|
2314
|
+
end
|
2315
|
+
end
|
2316
|
+
|
2317
|
+
describe '#print' do
|
2318
|
+
it 'appends the given object to the file' do
|
2319
|
+
write_subject.print 'test '
|
2320
|
+
write_subject.print 'object'
|
2321
|
+
|
2322
|
+
content = write_subject.send(:content)
|
2323
|
+
expect(content).to eq 'test object'
|
2324
|
+
end
|
2325
|
+
|
2326
|
+
it 'converts any given object to string with to_s' do
|
2327
|
+
write_subject.print 42
|
2328
|
+
|
2329
|
+
content = write_subject.send(:content)
|
2330
|
+
expect(content).to eq '42'
|
2331
|
+
end
|
2332
|
+
|
2333
|
+
it 'returns nil' do
|
2334
|
+
return_value = write_subject.print('test')
|
2335
|
+
expect(return_value).to be nil
|
2336
|
+
end
|
2337
|
+
|
2338
|
+
context 'when multiple objects are given' do
|
2339
|
+
it 'appends the given objects to the file' do
|
2340
|
+
write_subject.print 'this ', 'is a', ' test'
|
2341
|
+
|
2342
|
+
content = write_subject.send(:content)
|
2343
|
+
expect(content).to eq 'this is a test'
|
2344
|
+
end
|
2345
|
+
end
|
2346
|
+
|
2347
|
+
context 'when the is not opened for writing' do
|
2348
|
+
it 'raises an exception' do
|
2349
|
+
expect { subject.print 'test' }.to raise_error IOError
|
2350
|
+
end
|
2351
|
+
end
|
2352
|
+
|
2353
|
+
context 'when the output field separator is nil' do
|
2354
|
+
around do |example|
|
2355
|
+
old_value = $,
|
2356
|
+
$, = nil
|
2357
|
+
example.run
|
2358
|
+
$, = old_value
|
2359
|
+
end
|
2360
|
+
|
2361
|
+
it 'inserts nothing between the objects' do
|
2362
|
+
write_subject.print 'a', 'b', 'c'
|
2363
|
+
|
2364
|
+
content = write_subject.send(:content)
|
2365
|
+
expect(content).to eq 'abc'
|
2366
|
+
end
|
2367
|
+
end
|
2368
|
+
|
2369
|
+
context 'when the output field separator is not nil' do
|
2370
|
+
around do |example|
|
2371
|
+
old_value = $,
|
2372
|
+
$, = '-'
|
2373
|
+
example.run
|
2374
|
+
$, = old_value
|
2375
|
+
end
|
2376
|
+
|
2377
|
+
it 'inserts it between the objects' do
|
2378
|
+
write_subject.print 'a', 'b', 'c'
|
2379
|
+
|
2380
|
+
content = write_subject.send(:content)
|
2381
|
+
expect(content).to eq 'a-b-c'
|
2382
|
+
end
|
2383
|
+
end
|
2384
|
+
|
2385
|
+
context 'when the output record separator is nil' do
|
2386
|
+
around do |example|
|
2387
|
+
old_value = $\
|
2388
|
+
$\ = nil
|
2389
|
+
example.run
|
2390
|
+
$\ = old_value
|
2391
|
+
end
|
2392
|
+
|
2393
|
+
it 'inserts nothing at the end of the output' do
|
2394
|
+
write_subject.print 'a', 'b', 'c'
|
2395
|
+
|
2396
|
+
content = write_subject.send(:content)
|
2397
|
+
expect(content).to eq 'abc'
|
2398
|
+
end
|
2399
|
+
end
|
2400
|
+
|
2401
|
+
context 'when the output record separator is not nil' do
|
2402
|
+
around do |example|
|
2403
|
+
old_value = $\
|
2404
|
+
$\ = '-'
|
2405
|
+
example.run
|
2406
|
+
$\ = old_value
|
2407
|
+
end
|
2408
|
+
|
2409
|
+
it 'inserts it at the end of the output' do
|
2410
|
+
write_subject.print 'a', 'b', 'c'
|
2411
|
+
|
2412
|
+
content = write_subject.send(:content)
|
2413
|
+
expect(content).to eq 'abc-'
|
2414
|
+
end
|
2415
|
+
end
|
2416
|
+
|
2417
|
+
context 'when no argument is given' do
|
2418
|
+
it 'prints $_' do
|
2419
|
+
skip "I don't know how to test with \$_"
|
2420
|
+
|
2421
|
+
$_ = 'test'
|
2422
|
+
write_subject.print
|
2423
|
+
|
2424
|
+
content = write_subject.send(:content)
|
2425
|
+
expect(content).to eq 'test'
|
2426
|
+
end
|
1809
2427
|
end
|
1810
2428
|
end
|
1811
2429
|
|
1812
|
-
describe
|
1813
|
-
|
1814
|
-
|
2430
|
+
describe '#printf' do
|
2431
|
+
it 'appends the string in the file' do
|
2432
|
+
write_subject.print 'test '
|
2433
|
+
write_subject.printf 'Hello'
|
2434
|
+
|
2435
|
+
content = write_subject.send(:content)
|
2436
|
+
expect(content).to eq 'test Hello'
|
1815
2437
|
end
|
1816
2438
|
|
1817
|
-
it
|
1818
|
-
|
2439
|
+
it 'converts parameters under control of the format string' do
|
2440
|
+
write_subject.printf 'Hello %d %05d', 42, 43
|
2441
|
+
|
2442
|
+
content = write_subject.send(:content)
|
2443
|
+
expect(content).to eq 'Hello 42 00043'
|
1819
2444
|
end
|
1820
2445
|
|
1821
|
-
it
|
1822
|
-
|
1823
|
-
expect(
|
2446
|
+
it 'returns nil' do
|
2447
|
+
returned_value = write_subject.printf('Hello')
|
2448
|
+
expect(returned_value).to be nil
|
1824
2449
|
end
|
1825
2450
|
end
|
1826
2451
|
|
1827
|
-
describe
|
1828
|
-
it
|
1829
|
-
|
1830
|
-
|
1831
|
-
|
1832
|
-
|
2452
|
+
describe '#puts' do
|
2453
|
+
it 'appends content to the file' do
|
2454
|
+
write_subject.puts 'test'
|
2455
|
+
write_subject.close
|
2456
|
+
|
2457
|
+
content = write_subject.send(:content)
|
2458
|
+
expect(content).to eq "test\n"
|
1833
2459
|
end
|
1834
2460
|
|
1835
2461
|
it "does not override the file's content" do
|
1836
|
-
|
1837
|
-
|
1838
|
-
|
1839
|
-
|
1840
|
-
|
2462
|
+
write_subject.puts 'test'
|
2463
|
+
write_subject.puts 'test'
|
2464
|
+
write_subject.close
|
2465
|
+
|
2466
|
+
content = write_subject.send(:content)
|
2467
|
+
expect(content).to eq "test\ntest\n"
|
1841
2468
|
end
|
1842
2469
|
|
1843
|
-
|
1844
|
-
|
1845
|
-
|
2470
|
+
context 'when the file is not writable' do
|
2471
|
+
it 'raises an exception' do
|
2472
|
+
expect { subject.puts 'test' }.to raise_error IOError
|
2473
|
+
end
|
2474
|
+
end
|
2475
|
+
end
|
2476
|
+
|
2477
|
+
describe '#path' do
|
2478
|
+
it 'returns the path of the file' do
|
2479
|
+
file = subject_class.new('/test-file')
|
2480
|
+
expect(file.path).to eq '/test-file'
|
1846
2481
|
end
|
1847
2482
|
end
|
1848
2483
|
|
1849
|
-
describe
|
1850
|
-
before
|
1851
|
-
|
2484
|
+
describe '#read' do
|
2485
|
+
before do
|
2486
|
+
MemFs::File.open('/test-file', 'w') { |f| f.puts random_string }
|
1852
2487
|
end
|
1853
2488
|
|
1854
|
-
context
|
1855
|
-
it
|
1856
|
-
expect(
|
2489
|
+
context 'when no length is given' do
|
2490
|
+
it 'returns the content of the named file' do
|
2491
|
+
expect(subject.read).to eq random_string + "\n"
|
1857
2492
|
end
|
1858
2493
|
|
1859
|
-
it
|
1860
|
-
|
1861
|
-
expect(
|
2494
|
+
it 'returns an empty string if called a second time' do
|
2495
|
+
subject.read
|
2496
|
+
expect(subject.read).to be_empty
|
1862
2497
|
end
|
1863
2498
|
end
|
1864
2499
|
|
1865
|
-
context
|
1866
|
-
it
|
1867
|
-
|
2500
|
+
context 'when a length is given' do
|
2501
|
+
it 'returns a string of the given length' do
|
2502
|
+
read = subject.read(2)
|
2503
|
+
expect(read).to eq random_string[0, 2]
|
1868
2504
|
end
|
1869
2505
|
|
1870
|
-
it
|
1871
|
-
|
1872
|
-
|
2506
|
+
it 'returns nil when there is nothing more to read' do
|
2507
|
+
subject.read 1000
|
2508
|
+
|
2509
|
+
second_read = subject.read(1000)
|
2510
|
+
expect(second_read).to be_nil
|
1873
2511
|
end
|
1874
2512
|
end
|
1875
2513
|
|
1876
|
-
context
|
1877
|
-
it
|
2514
|
+
context 'when a buffer is given' do
|
2515
|
+
it 'fills the buffer with the read content' do
|
1878
2516
|
buffer = String.new
|
1879
|
-
|
1880
|
-
|
2517
|
+
subject.read 2, buffer
|
2518
|
+
|
2519
|
+
expect(buffer).to eq random_string[0, 2]
|
1881
2520
|
end
|
1882
2521
|
end
|
1883
2522
|
end
|
1884
2523
|
|
1885
|
-
describe
|
1886
|
-
before
|
1887
|
-
|
2524
|
+
describe '#seek' do
|
2525
|
+
before do
|
2526
|
+
File.open('/test-file', 'w') { |f| f.puts 'test' }
|
1888
2527
|
end
|
1889
2528
|
|
1890
|
-
it
|
1891
|
-
|
2529
|
+
it 'returns zero' do
|
2530
|
+
returned_value = subject.seek(1)
|
2531
|
+
expect(returned_value).to be_zero
|
1892
2532
|
end
|
1893
2533
|
|
1894
|
-
context
|
1895
|
-
it
|
1896
|
-
|
1897
|
-
|
2534
|
+
context 'when +whence+ is not provided' do
|
2535
|
+
it 'seeks to the absolute location given by +amount+' do
|
2536
|
+
subject.seek 3
|
2537
|
+
|
2538
|
+
expect(subject.pos).to be 3
|
1898
2539
|
end
|
1899
2540
|
end
|
1900
2541
|
|
1901
|
-
context
|
1902
|
-
it
|
1903
|
-
|
1904
|
-
|
1905
|
-
|
2542
|
+
context 'when +whence+ is IO::SEEK_CUR' do
|
2543
|
+
it 'seeks to +amount+ plus current position' do
|
2544
|
+
subject.read 1
|
2545
|
+
subject.seek 1, ::IO::SEEK_CUR
|
2546
|
+
|
2547
|
+
expect(subject.pos).to be 2
|
1906
2548
|
end
|
1907
2549
|
end
|
1908
2550
|
|
1909
|
-
context
|
1910
|
-
it
|
1911
|
-
|
1912
|
-
|
2551
|
+
context 'when +whence+ is IO::SEEK_END' do
|
2552
|
+
it 'seeks to +amount+ plus end of stream' do
|
2553
|
+
subject.seek -1, ::IO::SEEK_END
|
2554
|
+
|
2555
|
+
expect(subject.pos).to be 4
|
1913
2556
|
end
|
1914
2557
|
end
|
1915
2558
|
|
1916
|
-
context
|
1917
|
-
it
|
1918
|
-
|
1919
|
-
|
2559
|
+
context 'when +whence+ is IO::SEEK_SET' do
|
2560
|
+
it 'seeks to the absolute location given by +amount+' do
|
2561
|
+
subject.seek 3, ::IO::SEEK_SET
|
2562
|
+
|
2563
|
+
expect(subject.pos).to be 3
|
1920
2564
|
end
|
1921
2565
|
end
|
1922
2566
|
|
1923
|
-
context
|
1924
|
-
it
|
1925
|
-
expect {
|
2567
|
+
context 'when +whence+ is invalid' do
|
2568
|
+
it 'raises an exception' do
|
2569
|
+
expect { subject.seek 0, 42 }.to raise_error Errno::EINVAL
|
1926
2570
|
end
|
1927
2571
|
end
|
1928
2572
|
|
1929
|
-
context
|
1930
|
-
it
|
1931
|
-
expect {
|
2573
|
+
context 'if the position ends up to be less than zero' do
|
2574
|
+
it 'raises an exception' do
|
2575
|
+
expect { subject.seek -1 }.to raise_error Errno::EINVAL
|
1932
2576
|
end
|
1933
2577
|
end
|
1934
2578
|
end
|
1935
2579
|
|
1936
|
-
describe
|
1937
|
-
it
|
1938
|
-
|
1939
|
-
|
2580
|
+
describe '#size' do
|
2581
|
+
it 'returns the size of the file' do
|
2582
|
+
subject_class.open('/test-file', 'w') { |f| f.puts random_string }
|
2583
|
+
|
2584
|
+
size = subject_class.new('/test-file').size
|
2585
|
+
expect(size).to eq random_string.size + 1
|
2586
|
+
end
|
2587
|
+
end
|
2588
|
+
|
2589
|
+
describe '#stat' do
|
2590
|
+
it 'returns the +Stat+ object of the file' do
|
2591
|
+
expect(subject.stat).to be_a(File::Stat)
|
1940
2592
|
end
|
1941
2593
|
end
|
1942
2594
|
|
1943
|
-
describe
|
1944
|
-
it
|
1945
|
-
|
1946
|
-
|
2595
|
+
describe '#truncate' do
|
2596
|
+
it 'truncates the given file to be at most integer bytes long' do
|
2597
|
+
subject_class.open('/test-file', 'w') do |f|
|
2598
|
+
f.puts 'this is a 24-char string'
|
2599
|
+
f.truncate 10
|
2600
|
+
f.close
|
2601
|
+
end
|
2602
|
+
|
2603
|
+
size = subject_class.size('/test-file')
|
2604
|
+
expect(size).to eq 10
|
2605
|
+
end
|
2606
|
+
|
2607
|
+
it 'returns zero' do
|
2608
|
+
subject_class.open('/test-file', 'w') do |f|
|
2609
|
+
returned_value = f.truncate(42)
|
2610
|
+
expect(returned_value).to be_zero
|
2611
|
+
end
|
1947
2612
|
end
|
1948
2613
|
end
|
1949
2614
|
|
1950
|
-
describe
|
1951
|
-
it
|
1952
|
-
|
1953
|
-
|
2615
|
+
describe '#write' do
|
2616
|
+
it 'writes the given string to file' do
|
2617
|
+
write_subject.write 'test'
|
2618
|
+
|
2619
|
+
content = File.read('/test-file')
|
2620
|
+
expect(content).to eq 'test'
|
1954
2621
|
end
|
1955
2622
|
|
1956
|
-
it
|
1957
|
-
|
1958
|
-
expect(
|
1959
|
-
file.close
|
2623
|
+
it 'returns the number of bytes written' do
|
2624
|
+
returned_value = write_subject.write('test')
|
2625
|
+
expect(returned_value).to be 4
|
1960
2626
|
end
|
1961
2627
|
|
1962
|
-
context
|
1963
|
-
it
|
1964
|
-
|
1965
|
-
expect { file.write('test') }.to raise_error(IOError)
|
1966
|
-
file.close
|
2628
|
+
context 'when the file is not opened for writing' do
|
2629
|
+
it 'raises an exception' do
|
2630
|
+
expect { subject.write 'test' }.to raise_error IOError
|
1967
2631
|
end
|
1968
2632
|
end
|
1969
2633
|
|
1970
|
-
context
|
1971
|
-
it
|
1972
|
-
|
1973
|
-
|
2634
|
+
context 'when the argument is not a string' do
|
2635
|
+
it 'will be converted to a string using to_s' do
|
2636
|
+
write_subject.write 42
|
2637
|
+
|
2638
|
+
content = File.read('/test-file')
|
2639
|
+
expect(content).to eq '42'
|
1974
2640
|
end
|
1975
2641
|
end
|
1976
2642
|
end
|