memfs 0.0.1
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/.DS_Store +0 -0
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/Guardfile +8 -0
- data/LICENSE.txt +22 -0
- data/README.md +128 -0
- data/Rakefile +6 -0
- data/lib/fileutils.rb +1738 -0
- data/lib/memfs/dir.rb +33 -0
- data/lib/memfs/fake/directory.rb +51 -0
- data/lib/memfs/fake/entry.rb +73 -0
- data/lib/memfs/fake/file/content.rb +47 -0
- data/lib/memfs/fake/file.rb +36 -0
- data/lib/memfs/fake/symlink.rb +32 -0
- data/lib/memfs/file/stat.rb +48 -0
- data/lib/memfs/file.rb +284 -0
- data/lib/memfs/file_system.rb +157 -0
- data/lib/memfs/filesystem_access.rb +7 -0
- data/lib/memfs/version.rb +3 -0
- data/lib/memfs.rb +100 -0
- data/memfs.gemspec +29 -0
- data/spec/fileutils_spec.rb +961 -0
- data/spec/memfs/dir_spec.rb +104 -0
- data/spec/memfs/fake/directory_spec.rb +115 -0
- data/spec/memfs/fake/entry_spec.rb +140 -0
- data/spec/memfs/fake/file/content_spec.rb +154 -0
- data/spec/memfs/fake/file_spec.rb +37 -0
- data/spec/memfs/fake/symlink_spec.rb +43 -0
- data/spec/memfs/file/stat_spec.rb +209 -0
- data/spec/memfs/file_spec.rb +928 -0
- data/spec/memfs/file_system_spec.rb +393 -0
- data/spec/memfs_spec.rb +54 -0
- data/spec/spec_helper.rb +20 -0
- metadata +203 -0
@@ -0,0 +1,928 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module MemFs
|
4
|
+
describe File do
|
5
|
+
subject { MemFs::File }
|
6
|
+
|
7
|
+
let(:file) { subject.new('/test-file') }
|
8
|
+
let(:random_string) { ('a'..'z').to_a.sample(10).join }
|
9
|
+
|
10
|
+
before :each do
|
11
|
+
fs.mkdir '/test-dir'
|
12
|
+
fs.touch '/test-file', '/test-file2'
|
13
|
+
subject.symlink '/test-file', '/test-link'
|
14
|
+
subject.symlink '/no-file', '/no-link'
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '.atime' do
|
18
|
+
it "returns the last access time for the named file as a Time object" do
|
19
|
+
expect(subject.atime('/test-file')).to be_a(Time)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "raises an error if the entry does not exist" do
|
23
|
+
expect { subject.atime('/no-file') }.to raise_error(Errno::ENOENT)
|
24
|
+
end
|
25
|
+
|
26
|
+
context "when the entry is a symlink" do
|
27
|
+
let(:time) { Time.now - 500000 }
|
28
|
+
|
29
|
+
it "returns the last access time of the last target of the link chain" do
|
30
|
+
fs.find!('/test-file').atime = time
|
31
|
+
subject.symlink('/test-link', '/test-link2')
|
32
|
+
expect(subject.atime('/test-link2')).to eq(time)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe ".basename" do
|
38
|
+
it "returns the last component of the filename given in +file_name+" do
|
39
|
+
expect(subject.basename('/path/to/file.txt')).to eq('file.txt')
|
40
|
+
end
|
41
|
+
|
42
|
+
context "when +suffix+ is given" do
|
43
|
+
context "when it is present at the end of +file_name+" do
|
44
|
+
it "removes the +suffix+ from the filename basename" do
|
45
|
+
expect(subject.basename('/path/to/file.txt', '.txt')).to eq('file')
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe '.chmod' do
|
52
|
+
it "changes permission bits on the named file" do
|
53
|
+
subject.chmod(0777, '/test-file')
|
54
|
+
expect(subject.stat('/test-file').mode).to eq(0100777)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "changes permission bits on the named files (in list)" do
|
58
|
+
subject.chmod(0777, '/test-file', '/test-file2')
|
59
|
+
expect(subject.stat('/test-file2').mode).to eq(0100777)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe ".chown" do
|
64
|
+
it "changes the owner of the named file to the given numeric owner id" do
|
65
|
+
subject.chown(42, nil, '/test-file')
|
66
|
+
expect(subject.stat('/test-file').uid).to eq(42)
|
67
|
+
end
|
68
|
+
|
69
|
+
it "changes owner on the named files (in list)" do
|
70
|
+
subject.chown(42, nil, '/test-file', '/test-file2')
|
71
|
+
expect(subject.stat('/test-file2').uid).to eq(42)
|
72
|
+
end
|
73
|
+
|
74
|
+
it "changes the group of the named file to the given numeric group id" do
|
75
|
+
subject.chown(nil, 42, '/test-file')
|
76
|
+
expect(subject.stat('/test-file').gid).to eq(42)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "returns the number of files" do
|
80
|
+
expect(subject.chown(42, 42, '/test-file', '/test-file2')).to eq(2)
|
81
|
+
end
|
82
|
+
|
83
|
+
it "ignores nil user id" do
|
84
|
+
previous_uid = subject.stat('/test-file').uid
|
85
|
+
|
86
|
+
subject.chown(nil, 42, '/test-file')
|
87
|
+
expect(subject.stat('/test-file').uid).to eq(previous_uid)
|
88
|
+
end
|
89
|
+
|
90
|
+
it "ignores nil group id" do
|
91
|
+
previous_gid = subject.stat('/test-file').gid
|
92
|
+
|
93
|
+
subject.chown(42, nil, '/test-file')
|
94
|
+
expect(subject.stat('/test-file').gid).to eq(previous_gid)
|
95
|
+
end
|
96
|
+
|
97
|
+
it "ignores -1 user id" do
|
98
|
+
previous_uid = subject.stat('/test-file').uid
|
99
|
+
|
100
|
+
subject.chown(-1, 42, '/test-file')
|
101
|
+
expect(subject.stat('/test-file').uid).to eq(previous_uid)
|
102
|
+
end
|
103
|
+
|
104
|
+
it "ignores -1 group id" do
|
105
|
+
previous_gid = subject.stat('/test-file').gid
|
106
|
+
|
107
|
+
subject.chown(42, -1, '/test-file')
|
108
|
+
expect(subject.stat('/test-file').gid).to eq(previous_gid)
|
109
|
+
end
|
110
|
+
|
111
|
+
context "when the named entry is a symlink" do
|
112
|
+
it "changes the owner on the last target of the link chain" do
|
113
|
+
subject.chown(42, nil, '/test-link')
|
114
|
+
expect(subject.stat('/test-file').uid).to eq(42)
|
115
|
+
end
|
116
|
+
|
117
|
+
it "changes the group on the last target of the link chain" do
|
118
|
+
subject.chown(nil, 42, '/test-link')
|
119
|
+
expect(subject.stat('/test-file').gid).to eq(42)
|
120
|
+
end
|
121
|
+
|
122
|
+
it "does not change the owner of the symlink" do
|
123
|
+
subject.chown(42, nil, '/test-link')
|
124
|
+
expect(subject.lstat('/test-link').uid).not_to eq(42)
|
125
|
+
end
|
126
|
+
|
127
|
+
it "does not change the group of the symlink" do
|
128
|
+
subject.chown(nil, 42, '/test-link')
|
129
|
+
expect(subject.lstat('/test-link').gid).not_to eq(42)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
describe ".delete" do
|
135
|
+
it_behaves_like 'aliased method', :delete, :unlink
|
136
|
+
end
|
137
|
+
|
138
|
+
describe '.directory?' do
|
139
|
+
context "when the named entry is a directory" do
|
140
|
+
it "returns true" do
|
141
|
+
expect(subject.directory?('/test-dir')).to be_true
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
context "when the named entry is not a directory" do
|
146
|
+
it "returns false" do
|
147
|
+
expect(subject.directory?('/test-file')).to be_false
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
describe ".dirname" do
|
153
|
+
it "returns all components of the filename given in +file_name+ except the last one" do
|
154
|
+
expect(subject.dirname('/path/to/some/file.txt')).to eq('/path/to/some')
|
155
|
+
end
|
156
|
+
|
157
|
+
it "returns / if file_name is /" do
|
158
|
+
expect(subject.dirname('/')).to eq('/')
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
describe ".exists?" do
|
163
|
+
it "returns true if the file exists" do
|
164
|
+
expect(subject.exists?('/test-file')).to be_true
|
165
|
+
end
|
166
|
+
|
167
|
+
it "returns false if the file does not exist" do
|
168
|
+
expect(subject.exists?('/no-file')).to be_false
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
describe ".exist?" do
|
173
|
+
it_behaves_like 'aliased method', :exist?, :exists?
|
174
|
+
end
|
175
|
+
|
176
|
+
describe ".expand_path" do
|
177
|
+
it "converts a pathname to an absolute pathname" do
|
178
|
+
fs.chdir('/')
|
179
|
+
expect(subject.expand_path('test-file')).to eq("/test-file")
|
180
|
+
end
|
181
|
+
|
182
|
+
it "references path from the current working directory" do
|
183
|
+
fs.chdir('/test-dir')
|
184
|
+
expect(subject.expand_path('test-file')).to eq("/test-dir/test-file")
|
185
|
+
end
|
186
|
+
|
187
|
+
context "when +dir_string+ is provided" do
|
188
|
+
it "uses +dir_string+ as the stating point" do
|
189
|
+
expect(subject.expand_path('test-file', '/test')).to eq("/test/test-file")
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
describe ".file?" do
|
195
|
+
context "when the named file exists" do
|
196
|
+
context "and it is a regular file" do
|
197
|
+
it "returns true" do
|
198
|
+
expect(subject.file?('/test-file')).to be_true
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
context "and it is not a regular file" do
|
203
|
+
it "returns false" do
|
204
|
+
expect(subject.file?('/test-dir')).to be_false
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
context "when the named file does not exist" do
|
210
|
+
it "returns false" do
|
211
|
+
expect(subject.file?('/no-file')).to be_false
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
describe ".identical?" do
|
217
|
+
before :each do
|
218
|
+
subject.open('/test-file', 'w') { |f| f.puts 'test' }
|
219
|
+
subject.open('/test-file2', 'w') { |f| f.puts 'test' }
|
220
|
+
subject.symlink '/test-file', '/test-file-link'
|
221
|
+
subject.symlink '/test-file', '/test-file-link2'
|
222
|
+
subject.symlink '/test-file2', '/test-file2-link'
|
223
|
+
end
|
224
|
+
|
225
|
+
context "when two paths represent the same path" do
|
226
|
+
it "returns true" do
|
227
|
+
expect(subject.identical?('/test-file', '/test-file')).to be_true
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
context "when two paths do not represent the same file" do
|
232
|
+
it "returns false" do
|
233
|
+
expect(subject.identical?('/test-file', '/test-file2')).to be_false
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
context "when one of the paths does not exist" do
|
238
|
+
it "returns false" do
|
239
|
+
expect(subject.identical?('/test-file', '/no-file')).to be_false
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
context "when a path is a symlink" do
|
244
|
+
context "and the linked file is the same as the other path" do
|
245
|
+
it "returns true" do
|
246
|
+
expect(subject.identical?('/test-file', '/test-file-link')).to be_true
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
context "and the linked file is different from the other path" do
|
251
|
+
it "returns false" do
|
252
|
+
expect(subject.identical?('/test-file2', '/test-file-link')).to be_false
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
context "when the two paths are symlinks" do
|
258
|
+
context "and both links point to the same file" do
|
259
|
+
it "returns true" do
|
260
|
+
expect(subject.identical?('/test-file-link', '/test-file-link2')).to be_true
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
context "and both links do not point to the same file" do
|
265
|
+
it "returns false" do
|
266
|
+
expect(subject.identical?('/test-file-link', '/test-file2-link')).to be_false
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
describe ".join" do
|
273
|
+
it "Returns a new string formed by joining the strings using File::SEPARATOR" do
|
274
|
+
expect(subject.join('a', 'b', 'c')).to eq('a/b/c')
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
describe ".lchmod" do
|
279
|
+
context "when the named file is a regular file" do
|
280
|
+
it "acts like chmod" do
|
281
|
+
subject.lchmod(0777, '/test-file')
|
282
|
+
expect(subject.stat('/test-file').mode).to eq(0100777)
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
context "when the named file is a symlink" do
|
287
|
+
it "changes permission bits on the symlink" do
|
288
|
+
subject.lchmod(0777, '/test-link')
|
289
|
+
expect(subject.lstat('/test-link').mode).to eq(0100777)
|
290
|
+
end
|
291
|
+
|
292
|
+
it "does not change permission bits on the link's target" do
|
293
|
+
mode = subject.stat('/test-file').mode
|
294
|
+
subject.lchmod(0777, '/test-link')
|
295
|
+
expect(subject.stat('/test-file').mode).to eq(mode)
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
describe ".link" do
|
301
|
+
before :each do
|
302
|
+
subject.open('/test-file', 'w') { |f| f.puts 'test' }
|
303
|
+
end
|
304
|
+
|
305
|
+
it "creates a new name for an existing file using a hard link" do
|
306
|
+
subject.link('/test-file', '/new-file')
|
307
|
+
expect(subject.read('/new-file')).to eq(subject.read('/test-file'))
|
308
|
+
end
|
309
|
+
|
310
|
+
it "returns zero" do
|
311
|
+
expect(subject.link('/test-file', '/new-file')).to eq(0)
|
312
|
+
end
|
313
|
+
|
314
|
+
context "when +old_name+ does not exist" do
|
315
|
+
it "raises an exception" do
|
316
|
+
expect {
|
317
|
+
subject.link('/no-file', '/nowhere')
|
318
|
+
}.to raise_error(Errno::ENOENT)
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
context "when +new_name+ already exists" do
|
323
|
+
it "raises an exception" do
|
324
|
+
subject.open('/test-file2', 'w') { |f| f.puts 'test2' }
|
325
|
+
expect {
|
326
|
+
subject.link('/test-file', '/test-file2')
|
327
|
+
}.to raise_error(SystemCallError)
|
328
|
+
end
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
describe '.lstat' do
|
333
|
+
it "returns a File::Stat object for the named file" do
|
334
|
+
expect(subject.lstat('/test-file')).to be_a(File::Stat)
|
335
|
+
end
|
336
|
+
|
337
|
+
context "when the named file does not exist" do
|
338
|
+
it "raises an exception" do
|
339
|
+
expect { subject.lstat('/no-file') }.to raise_error(Errno::ENOENT)
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
context "when the named file is a symlink" do
|
344
|
+
it "does not follow the last symbolic link" do
|
345
|
+
expect(subject.lstat('/test-link').symlink?).to be_true
|
346
|
+
end
|
347
|
+
|
348
|
+
context "and its target does not exist" do
|
349
|
+
it "ignores errors" do
|
350
|
+
expect {
|
351
|
+
subject.lstat('/no-link')
|
352
|
+
}.not_to raise_error(Errno::ENOENT)
|
353
|
+
end
|
354
|
+
end
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
358
|
+
describe '.new' do
|
359
|
+
context "when the mode is provided" do
|
360
|
+
context "and it is an integer" do
|
361
|
+
it "sets the mode to the integer value" do
|
362
|
+
file = subject.new('/test-file', File::RDWR)
|
363
|
+
expect(file.opening_mode).to eq(File::RDWR)
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
context "and it is a string" do
|
368
|
+
it "sets the mode to the integer value" do
|
369
|
+
file = subject.new('/test-file', 'r+')
|
370
|
+
expect(file.opening_mode).to eq(File::RDWR)
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
context "and it specifies that the file must be created" do
|
375
|
+
context "and the file already exists" do
|
376
|
+
it "changes the mtime of the file" do
|
377
|
+
fs.should_receive(:touch).with('/test-file')
|
378
|
+
subject.new('/test-file', 'w')
|
379
|
+
end
|
380
|
+
end
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
context "when no argument is given" do
|
385
|
+
it "raises an exception" do
|
386
|
+
expect { subject.new }.to raise_error(ArgumentError)
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
390
|
+
context "when too many arguments are given" do
|
391
|
+
it "raises an exception" do
|
392
|
+
expect { subject.new(1,2,3,4) }.to raise_error(ArgumentError)
|
393
|
+
end
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
397
|
+
describe '.path' do
|
398
|
+
context "when the path is a string" do
|
399
|
+
let(:path) { '/some/path' }
|
400
|
+
|
401
|
+
it "returns the string representation of the path" do
|
402
|
+
expect(subject.path(path)).to eq('/some/path')
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
context "when the path is a Pathname" do
|
407
|
+
let(:path) { Pathname.new('/some/path') }
|
408
|
+
|
409
|
+
it "returns the string representation of the path" do
|
410
|
+
expect(subject.path(path)).to eq('/some/path')
|
411
|
+
end
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
415
|
+
describe ".read" do
|
416
|
+
before :each do
|
417
|
+
subject.open('/test-file', 'w') { |f| f.puts "test" }
|
418
|
+
end
|
419
|
+
|
420
|
+
it "reads the content of the given file" do
|
421
|
+
expect(subject.read('/test-file')).to eq("test\n")
|
422
|
+
end
|
423
|
+
|
424
|
+
context "when +lenght+ is provided" do
|
425
|
+
it "reads only +length+ characters" do
|
426
|
+
expect(subject.read('/test-file', 2)).to eq('te')
|
427
|
+
end
|
428
|
+
|
429
|
+
context "when +length+ is bigger than the file size" do
|
430
|
+
it "reads until the end of the file" do
|
431
|
+
expect(subject.read('/test-file', 1000)).to eq("test\n")
|
432
|
+
end
|
433
|
+
end
|
434
|
+
end
|
435
|
+
|
436
|
+
context "when +offset+ is provided" do
|
437
|
+
it "starts reading from the offset" do
|
438
|
+
expect(subject.read('/test-file', 2, 1)).to eq('es')
|
439
|
+
end
|
440
|
+
|
441
|
+
it "raises an error if offset is negative" do
|
442
|
+
expect {
|
443
|
+
subject.read('/test-file', 2, -1)
|
444
|
+
}.to raise_error(Errno::EINVAL)
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
context "when the last argument is a hash" do
|
449
|
+
it "passes the contained options to +open+" do
|
450
|
+
subject.should_receive(:open)
|
451
|
+
.with('/test-file', File::RDONLY, encoding: 'UTF-8')
|
452
|
+
.and_return(file)
|
453
|
+
subject.read('/test-file', encoding: 'UTF-8')
|
454
|
+
end
|
455
|
+
|
456
|
+
context "when it contains the +open_args+ key" do
|
457
|
+
it "takes precedence over the other options" do
|
458
|
+
subject.should_receive(:open)
|
459
|
+
.with('/test-file', 'r')
|
460
|
+
.and_return(file)
|
461
|
+
subject.read('/test-file', mode: 'w', open_args: ['r'])
|
462
|
+
end
|
463
|
+
end
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
467
|
+
describe ".readlink" do
|
468
|
+
it "returns the name of the file referenced by the given link" do
|
469
|
+
expect(subject.readlink('/test-link')).to eq('/test-file')
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
describe ".rename" do
|
474
|
+
it "renames the given file to the new name" do
|
475
|
+
subject.rename('/test-file', '/test-file2')
|
476
|
+
expect(subject.exists?('/test-file2')).to be_true
|
477
|
+
end
|
478
|
+
|
479
|
+
it "returns zero" do
|
480
|
+
expect(subject.rename('/test-file', '/test-file2')).to eq(0)
|
481
|
+
end
|
482
|
+
end
|
483
|
+
|
484
|
+
describe ".size" do
|
485
|
+
it "returns the size of the file" do
|
486
|
+
subject.open('/test-file', 'w') { |f| f.puts random_string }
|
487
|
+
expect(subject.size('/test-file')).to eq(random_string.size + 1)
|
488
|
+
end
|
489
|
+
end
|
490
|
+
|
491
|
+
describe '.stat' do
|
492
|
+
it "returns a File::Stat object for the named file" do
|
493
|
+
expect(subject.stat('/test-file')).to be_a(File::Stat)
|
494
|
+
end
|
495
|
+
|
496
|
+
it "follows the last symbolic link" do
|
497
|
+
expect(subject.stat('/test-link').symlink?).to be_false
|
498
|
+
end
|
499
|
+
|
500
|
+
context "when the named file does not exist" do
|
501
|
+
it "raises an exception" do
|
502
|
+
expect { subject.stat('/no-file') }.to raise_error(Errno::ENOENT)
|
503
|
+
end
|
504
|
+
end
|
505
|
+
|
506
|
+
context "when the named file is a symlink" do
|
507
|
+
context "and its target does not exist" do
|
508
|
+
it "raises an exception" do
|
509
|
+
expect { subject.stat('/no-link') }.to raise_error(Errno::ENOENT)
|
510
|
+
end
|
511
|
+
end
|
512
|
+
end
|
513
|
+
|
514
|
+
it "always returns a new object" do
|
515
|
+
stat = subject.stat('/test-file')
|
516
|
+
expect(subject.stat('/test-file')).not_to be(stat)
|
517
|
+
end
|
518
|
+
end
|
519
|
+
|
520
|
+
describe '.symlink' do
|
521
|
+
it "creates a symbolic link named new_name" do
|
522
|
+
expect(subject.symlink?('/test-link')).to be_true
|
523
|
+
end
|
524
|
+
|
525
|
+
it "creates a symbolic link that points to an entry named old_name" do
|
526
|
+
expect(fs.find!('/test-link').target).to eq('/test-file')
|
527
|
+
end
|
528
|
+
|
529
|
+
context "when the target does not exist" do
|
530
|
+
it "creates a symbolic link" do
|
531
|
+
expect(subject.symlink?('/no-link')).to be_true
|
532
|
+
end
|
533
|
+
end
|
534
|
+
|
535
|
+
it "returns 0" do
|
536
|
+
expect(subject.symlink('/test-file', '/new-link')).to eq(0)
|
537
|
+
end
|
538
|
+
end
|
539
|
+
|
540
|
+
describe '.symlink?' do
|
541
|
+
context "when the named entry is a symlink" do
|
542
|
+
it "returns true" do
|
543
|
+
expect(subject.symlink?('/test-link')).to be_true
|
544
|
+
end
|
545
|
+
end
|
546
|
+
|
547
|
+
context "when the named entry is not a symlink" do
|
548
|
+
it "returns false" do
|
549
|
+
expect(subject.symlink?('/test-file')).to be_false
|
550
|
+
end
|
551
|
+
end
|
552
|
+
|
553
|
+
context "when the named entry does not exist" do
|
554
|
+
it "returns false" do
|
555
|
+
expect(subject.symlink?('/no-file')).to be_false
|
556
|
+
end
|
557
|
+
end
|
558
|
+
end
|
559
|
+
|
560
|
+
describe '.umask' do
|
561
|
+
before :each do
|
562
|
+
subject.umask(0022)
|
563
|
+
end
|
564
|
+
|
565
|
+
it "returns the current umask value for this process" do
|
566
|
+
expect(subject.umask).to eq(0022)
|
567
|
+
end
|
568
|
+
|
569
|
+
context "when the optional argument is given" do
|
570
|
+
it "sets the umask to that value" do
|
571
|
+
subject.umask 0777
|
572
|
+
expect(subject.umask).to eq(0777)
|
573
|
+
end
|
574
|
+
|
575
|
+
it "return the previous value" do
|
576
|
+
expect(subject.umask(0777)).to eq(0022)
|
577
|
+
end
|
578
|
+
end
|
579
|
+
end
|
580
|
+
|
581
|
+
describe ".unlink" do
|
582
|
+
it "deletes the named file" do
|
583
|
+
subject.unlink('/test-file')
|
584
|
+
expect(subject.exists?('/test-file')).to be_false
|
585
|
+
end
|
586
|
+
|
587
|
+
it "returns the number of names passed as arguments" do
|
588
|
+
expect(subject.unlink('/test-file', '/test-file2')).to eq(2)
|
589
|
+
end
|
590
|
+
|
591
|
+
context "when multiple file names are given" do
|
592
|
+
it "deletes the named files" do
|
593
|
+
subject.unlink('/test-file', '/test-file2')
|
594
|
+
expect(subject.exists?('/test-file2')).to be_false
|
595
|
+
end
|
596
|
+
end
|
597
|
+
|
598
|
+
context "when the entry is a directory" do
|
599
|
+
it "raises an exception" do
|
600
|
+
expect { subject.unlink('/test-dir') }.to raise_error(Errno::EPERM)
|
601
|
+
end
|
602
|
+
end
|
603
|
+
end
|
604
|
+
|
605
|
+
describe '.utime' do
|
606
|
+
let(:time) { Time.now - 500000 }
|
607
|
+
|
608
|
+
it "sets the access time of each named file to the first argument" do
|
609
|
+
subject.utime(time, time, '/test-file')
|
610
|
+
expect(subject.atime('/test-file')).to eq(time)
|
611
|
+
end
|
612
|
+
|
613
|
+
it "sets the modification time of each named file to the second argument" do
|
614
|
+
subject.utime(time, time, '/test-file')
|
615
|
+
expect(subject.mtime('/test-file')).to eq(time)
|
616
|
+
end
|
617
|
+
|
618
|
+
it "returns the number of file names in the argument list" do
|
619
|
+
expect(subject.utime(time, time, '/test-file', '/test-file2')).to eq(2)
|
620
|
+
end
|
621
|
+
|
622
|
+
it "raises en error if the entry does not exist" do
|
623
|
+
expect {
|
624
|
+
subject.utime(time, time, '/no-file')
|
625
|
+
}.to raise_error(Errno::ENOENT)
|
626
|
+
end
|
627
|
+
end
|
628
|
+
|
629
|
+
describe '#chmod' do
|
630
|
+
it "changes permission bits on the file" do
|
631
|
+
file.chmod(0777)
|
632
|
+
expect(file.stat.mode).to eq(0100777)
|
633
|
+
end
|
634
|
+
|
635
|
+
it "returns zero" do
|
636
|
+
expect(file.chmod(0777)).to eq(0)
|
637
|
+
end
|
638
|
+
end
|
639
|
+
|
640
|
+
describe "#chown" do
|
641
|
+
it "changes the owner of the named file to the given numeric owner id" do
|
642
|
+
file.chown(42, nil)
|
643
|
+
expect(file.stat.uid).to be(42)
|
644
|
+
end
|
645
|
+
|
646
|
+
it "changes owner on the named files (in list)" do
|
647
|
+
file.chown(42)
|
648
|
+
expect(file.stat.uid).to be(42)
|
649
|
+
end
|
650
|
+
|
651
|
+
it "changes the group of the named file to the given numeric group id" do
|
652
|
+
file.chown(nil, 42)
|
653
|
+
expect(file.stat.gid).to be(42)
|
654
|
+
end
|
655
|
+
|
656
|
+
it "returns zero" do
|
657
|
+
expect(file.chown(42, 42)).to eq(0)
|
658
|
+
end
|
659
|
+
|
660
|
+
it "ignores nil user id" do
|
661
|
+
previous_uid = file.stat.uid
|
662
|
+
|
663
|
+
file.chown(nil, 42)
|
664
|
+
expect(file.stat.uid).to eq(previous_uid)
|
665
|
+
end
|
666
|
+
|
667
|
+
it "ignores nil group id" do
|
668
|
+
previous_gid = file.stat.gid
|
669
|
+
|
670
|
+
file.chown(42, nil)
|
671
|
+
expect(file.stat.gid).to eq(previous_gid)
|
672
|
+
end
|
673
|
+
|
674
|
+
it "ignores -1 user id" do
|
675
|
+
previous_uid = file.stat.uid
|
676
|
+
|
677
|
+
file.chown(-1, 42)
|
678
|
+
expect(file.stat.uid).to eq(previous_uid)
|
679
|
+
end
|
680
|
+
|
681
|
+
it "ignores -1 group id" do
|
682
|
+
previous_gid = file.stat.gid
|
683
|
+
|
684
|
+
file.chown(42, -1)
|
685
|
+
expect(file.stat.gid).to eq(previous_gid)
|
686
|
+
end
|
687
|
+
|
688
|
+
context "when the named entry is a symlink" do
|
689
|
+
let(:symlink) { subject.new('/test-link') }
|
690
|
+
|
691
|
+
it "changes the owner on the last target of the link chain" do
|
692
|
+
symlink.chown(42, nil)
|
693
|
+
expect(file.stat.uid).to be(42)
|
694
|
+
end
|
695
|
+
|
696
|
+
it "changes the group on the last target of the link chain" do
|
697
|
+
symlink.chown(nil, 42)
|
698
|
+
expect(file.stat.gid).to be(42)
|
699
|
+
end
|
700
|
+
|
701
|
+
it "does not change the owner of the symlink" do
|
702
|
+
symlink.chown(42, nil)
|
703
|
+
expect(symlink.lstat.uid).not_to be(42)
|
704
|
+
end
|
705
|
+
|
706
|
+
it "does not change the group of the symlink" do
|
707
|
+
symlink.chown(nil, 42)
|
708
|
+
expect(symlink.lstat.gid).not_to be(42)
|
709
|
+
end
|
710
|
+
end
|
711
|
+
end
|
712
|
+
|
713
|
+
describe "#close" do
|
714
|
+
it "closes the file stream" do
|
715
|
+
file = subject.open('/test-file')
|
716
|
+
file.close
|
717
|
+
expect(file).to be_closed
|
718
|
+
end
|
719
|
+
end
|
720
|
+
|
721
|
+
describe "#closed?" do
|
722
|
+
it "returns true when the file is closed" do
|
723
|
+
file = subject.open('/test-file')
|
724
|
+
file.close
|
725
|
+
expect(file.closed?).to be_true
|
726
|
+
end
|
727
|
+
|
728
|
+
it "returns false when the file is open" do
|
729
|
+
file = subject.open('/test-file')
|
730
|
+
expect(file.closed?).to be_false
|
731
|
+
file.close
|
732
|
+
end
|
733
|
+
end
|
734
|
+
|
735
|
+
describe '#lstat' do
|
736
|
+
it "returns the File::Stat object of the file" do
|
737
|
+
expect(file.lstat).to be_a(File::Stat)
|
738
|
+
end
|
739
|
+
|
740
|
+
it "does not follow the last symbolic link" do
|
741
|
+
file = subject.new('/test-link')
|
742
|
+
expect(file.lstat).to be_symlink
|
743
|
+
end
|
744
|
+
|
745
|
+
context "when the named file is a symlink" do
|
746
|
+
context "and its target does not exist" do
|
747
|
+
it "ignores errors" do
|
748
|
+
file = subject.new('/no-link')
|
749
|
+
expect { file.lstat }.not_to raise_error(Errno::ENOENT)
|
750
|
+
end
|
751
|
+
end
|
752
|
+
end
|
753
|
+
end
|
754
|
+
|
755
|
+
describe "#path" do
|
756
|
+
it "returns the path of the file" do
|
757
|
+
file = subject.new('/test-file')
|
758
|
+
expect(file.path).to eq('/test-file')
|
759
|
+
end
|
760
|
+
end
|
761
|
+
|
762
|
+
describe "#pos" do
|
763
|
+
before :each do
|
764
|
+
subject.open('/test-file', 'w') { |f| f.puts 'test' }
|
765
|
+
end
|
766
|
+
|
767
|
+
it "returns zero when the file was just opened" do
|
768
|
+
expect(file.pos).to be_zero
|
769
|
+
end
|
770
|
+
|
771
|
+
it "returns the reading offset when some of the file has been read" do
|
772
|
+
file.read(2)
|
773
|
+
expect(file.pos).to eq(2)
|
774
|
+
end
|
775
|
+
end
|
776
|
+
|
777
|
+
describe "#puts" do
|
778
|
+
it "appends content to the file" do
|
779
|
+
file = subject.new('/test-file', 'w')
|
780
|
+
file.puts "test"
|
781
|
+
file.close
|
782
|
+
expect(file.content.to_s).to eq("test\n")
|
783
|
+
end
|
784
|
+
|
785
|
+
it "does not override the file's content" do
|
786
|
+
file = subject.new('/test-file', 'w')
|
787
|
+
file.puts "test"
|
788
|
+
file.puts "test"
|
789
|
+
file.close
|
790
|
+
expect(file.content.to_s).to eq("test\ntest\n")
|
791
|
+
end
|
792
|
+
|
793
|
+
it "raises an exception if the file is not writable" do
|
794
|
+
file = subject.new('/test-file')
|
795
|
+
expect { file.puts "test" }.to raise_error(IOError)
|
796
|
+
end
|
797
|
+
end
|
798
|
+
|
799
|
+
describe "#read" do
|
800
|
+
before :each do
|
801
|
+
subject.open('/test-file', 'w') { |f| f.puts random_string }
|
802
|
+
end
|
803
|
+
|
804
|
+
context "when no length is given" do
|
805
|
+
it "returns the content of the named file" do
|
806
|
+
expect(file.read).to eq(random_string + "\n")
|
807
|
+
end
|
808
|
+
|
809
|
+
it "returns an empty string if called a second time" do
|
810
|
+
file.read
|
811
|
+
expect(file.read).to be_empty
|
812
|
+
end
|
813
|
+
end
|
814
|
+
|
815
|
+
context "when a length is given" do
|
816
|
+
it "returns a string of the given length" do
|
817
|
+
expect(file.read(2)).to eq(random_string[0, 2])
|
818
|
+
end
|
819
|
+
|
820
|
+
it "returns nil when there is nothing more to read" do
|
821
|
+
file.read(1000)
|
822
|
+
expect(file.read(1000)).to be_nil
|
823
|
+
end
|
824
|
+
end
|
825
|
+
|
826
|
+
context "when a buffer is given" do
|
827
|
+
it "fills the buffer with the read content" do
|
828
|
+
buffer = String.new
|
829
|
+
file.read(2, buffer)
|
830
|
+
expect(buffer).to eq(random_string[0, 2])
|
831
|
+
end
|
832
|
+
end
|
833
|
+
end
|
834
|
+
|
835
|
+
describe "#seek" do
|
836
|
+
before :each do
|
837
|
+
subject.open('/test-file', 'w') { |f| f.puts 'test' }
|
838
|
+
end
|
839
|
+
|
840
|
+
it "returns zero" do
|
841
|
+
expect(file.seek(1)).to eq(0)
|
842
|
+
end
|
843
|
+
|
844
|
+
context "when +whence+ is not provided" do
|
845
|
+
it "seeks to the absolute location given by +amount+" do
|
846
|
+
file.seek(3)
|
847
|
+
expect(file.pos).to eq(3)
|
848
|
+
end
|
849
|
+
end
|
850
|
+
|
851
|
+
context "when +whence+ is IO::SEEK_CUR" do
|
852
|
+
it "seeks to +amount+ plus current position" do
|
853
|
+
file.read(1)
|
854
|
+
file.seek(1, IO::SEEK_CUR)
|
855
|
+
expect(file.pos).to eq(2)
|
856
|
+
end
|
857
|
+
end
|
858
|
+
|
859
|
+
context "when +whence+ is IO::SEEK_END" do
|
860
|
+
it "seeks to +amount+ plus end of stream" do
|
861
|
+
file.seek(-1, IO::SEEK_END)
|
862
|
+
expect(file.pos).to eq(4)
|
863
|
+
end
|
864
|
+
end
|
865
|
+
|
866
|
+
context "when +whence+ is IO::SEEK_SET" do
|
867
|
+
it "seeks to the absolute location given by +amount+" do
|
868
|
+
file.seek(3, IO::SEEK_SET)
|
869
|
+
expect(file.pos).to eq(3)
|
870
|
+
end
|
871
|
+
end
|
872
|
+
|
873
|
+
context "when +whence+ is invalid" do
|
874
|
+
it "raises an exception" do
|
875
|
+
expect { file.seek(0, 42) }.to raise_error(Errno::EINVAL)
|
876
|
+
end
|
877
|
+
end
|
878
|
+
|
879
|
+
context "if the position ends up to be less than zero" do
|
880
|
+
it "raises an exception" do
|
881
|
+
expect { file.seek(-1) }.to raise_error(Errno::EINVAL)
|
882
|
+
end
|
883
|
+
end
|
884
|
+
end
|
885
|
+
|
886
|
+
describe "#size" do
|
887
|
+
it "returns the size of the file" do
|
888
|
+
subject.open('/test-file', 'w') { |f| f.puts random_string }
|
889
|
+
expect(subject.new('/test-file').size).to eq(random_string.size + 1)
|
890
|
+
end
|
891
|
+
end
|
892
|
+
|
893
|
+
describe "#stat" do
|
894
|
+
it "returns the +Stat+ object of the file" do
|
895
|
+
file = subject.new('/test-file')
|
896
|
+
file.stat == subject.stat('/test-file')
|
897
|
+
end
|
898
|
+
end
|
899
|
+
|
900
|
+
describe "#write" do
|
901
|
+
it "writes the given string to file" do
|
902
|
+
subject.open('/test-file', 'w') { |f| f.write "test" }
|
903
|
+
expect(subject.read('/test-file')).to eq("test")
|
904
|
+
end
|
905
|
+
|
906
|
+
it "returns the number of bytes written" do
|
907
|
+
file = subject.open('/test-file', 'w')
|
908
|
+
expect(file.write('test')).to eq(4)
|
909
|
+
file.close
|
910
|
+
end
|
911
|
+
|
912
|
+
context "when the file is not opened for writing" do
|
913
|
+
it "raises an exception" do
|
914
|
+
file = subject.open('/test-file')
|
915
|
+
expect { file.write('test') }.to raise_error(IOError)
|
916
|
+
file.close
|
917
|
+
end
|
918
|
+
end
|
919
|
+
|
920
|
+
context "when the argument is not a string" do
|
921
|
+
it "will be converted to a string using to_s" do
|
922
|
+
subject.open('/test-file', 'w') { |f| f.write 42 }
|
923
|
+
expect(subject.read('/test-file')).to eq('42')
|
924
|
+
end
|
925
|
+
end
|
926
|
+
end
|
927
|
+
end
|
928
|
+
end
|