memfs 0.5.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.hound.yml +2 -0
- data/.rspec +1 -1
- data/.rubocop.yml +1 -0
- data/.ruby-style.yml +335 -0
- data/.travis.yml +6 -3
- data/CHANGELOG.md +12 -1
- data/README.md +6 -5
- data/lib/memfs.rb +29 -2
- data/lib/memfs/dir.rb +6 -6
- data/lib/memfs/fake/directory.rb +5 -2
- data/lib/memfs/fake/file.rb +4 -5
- data/lib/memfs/fake/file/content.rb +0 -2
- data/lib/memfs/file.rb +27 -20
- data/lib/memfs/file/stat.rb +1 -1
- data/lib/memfs/file_system.rb +3 -3
- data/lib/memfs/io.rb +190 -154
- data/lib/memfs/version.rb +1 -1
- data/memfs.gemspec +4 -1
- data/spec/fileutils_spec.rb +23 -18
- data/spec/memfs/dir_spec.rb +20 -9
- data/spec/memfs/fake/directory_spec.rb +5 -1
- data/spec/memfs/fake/entry_spec.rb +5 -5
- data/spec/memfs/fake/file/content_spec.rb +1 -1
- data/spec/memfs/fake/file_spec.rb +1 -1
- data/spec/memfs/fake/symlink_spec.rb +2 -2
- data/spec/memfs/file/stat_spec.rb +6 -3
- data/spec/memfs/file_spec.rb +140 -57
- data/spec/memfs/file_system_spec.rb +5 -4
- data/spec/memfs_spec.rb +39 -2
- data/spec/spec_helper.rb +23 -21
- metadata +22 -6
- data/.rubocop.yml +0 -29
data/lib/memfs/version.rb
CHANGED
data/memfs.gemspec
CHANGED
@@ -21,11 +21,14 @@ Gem::Specification.new do |gem|
|
|
21
21
|
gem.require_paths = ['lib']
|
22
22
|
|
23
23
|
gem.add_development_dependency 'coveralls', '~> 0.6'
|
24
|
-
gem.add_development_dependency 'rake', '~>
|
24
|
+
gem.add_development_dependency 'rake', '~> 12.0'
|
25
25
|
gem.add_development_dependency 'rspec', '~> 3.0'
|
26
26
|
gem.add_development_dependency 'guard', '~> 2.6'
|
27
27
|
gem.add_development_dependency 'guard-rspec', '~> 4.3'
|
28
28
|
gem.add_development_dependency 'rb-inotify', '~> 0.8'
|
29
29
|
gem.add_development_dependency 'rb-fsevent', '~> 0.9'
|
30
30
|
gem.add_development_dependency 'rb-fchange', '~> 0.0'
|
31
|
+
|
32
|
+
listen_version = RUBY_VERSION >= '2.2.3' ? '~> 3.1' : '~> 3.0.7'
|
33
|
+
gem.add_development_dependency 'listen', listen_version
|
31
34
|
end
|
data/spec/fileutils_spec.rb
CHANGED
@@ -2,7 +2,7 @@ require 'date'
|
|
2
2
|
require 'fileutils'
|
3
3
|
require 'spec_helper'
|
4
4
|
|
5
|
-
describe FileUtils do
|
5
|
+
RSpec.describe FileUtils do
|
6
6
|
before :each do
|
7
7
|
MemFs::File.umask(0020)
|
8
8
|
MemFs.activate!
|
@@ -25,12 +25,12 @@ describe FileUtils do
|
|
25
25
|
end
|
26
26
|
|
27
27
|
it "raises an error when the given path doesn't exist" do
|
28
|
-
expect { described_class.cd('/nowhere') }.to
|
28
|
+
expect { described_class.cd('/nowhere') }.to raise_error(Errno::ENOENT)
|
29
29
|
end
|
30
30
|
|
31
31
|
it 'raises an error when the given path is not a directory' do
|
32
32
|
described_class.touch('/test-file')
|
33
|
-
expect { described_class.cd('/test-file') }.to
|
33
|
+
expect { described_class.cd('/test-file') }.to raise_error(Errno::ENOTDIR)
|
34
34
|
end
|
35
35
|
|
36
36
|
context 'when called with a block' do
|
@@ -57,7 +57,7 @@ describe FileUtils do
|
|
57
57
|
end
|
58
58
|
|
59
59
|
it "raises an error if the last target of the link chain doesn't exist" do
|
60
|
-
expect { described_class.cd('/nowhere-link') }.to
|
60
|
+
expect { described_class.cd('/nowhere-link') }.to raise_error(Errno::ENOENT)
|
61
61
|
end
|
62
62
|
end
|
63
63
|
end
|
@@ -82,7 +82,7 @@ describe FileUtils do
|
|
82
82
|
end
|
83
83
|
|
84
84
|
it 'raises an error if an entry does not exist' do
|
85
|
-
expect { described_class.chmod(0777, '/test-file') }.to
|
85
|
+
expect { described_class.chmod(0777, '/test-file') }.to raise_error(Errno::ENOENT)
|
86
86
|
end
|
87
87
|
|
88
88
|
context 'when the named file is a symlink' do
|
@@ -296,9 +296,12 @@ describe FileUtils do
|
|
296
296
|
|
297
297
|
context 'when +src+ does not exist' do
|
298
298
|
it 'raises an exception' do
|
299
|
+
expected_exception =
|
300
|
+
RUBY_VERSION >= '2.4.0' ? Errno::ENOENT : RuntimeError
|
301
|
+
|
299
302
|
expect {
|
300
303
|
described_class.copy_entry('/test-file', '/test-copy')
|
301
|
-
}.to
|
304
|
+
}.to raise_error(expected_exception)
|
302
305
|
end
|
303
306
|
end
|
304
307
|
|
@@ -382,7 +385,7 @@ describe FileUtils do
|
|
382
385
|
it 'raises an error' do
|
383
386
|
expect {
|
384
387
|
described_class.cp('/test-file', '/test-file')
|
385
|
-
}.to
|
388
|
+
}.to raise_error(ArgumentError)
|
386
389
|
end
|
387
390
|
end
|
388
391
|
|
@@ -400,7 +403,7 @@ describe FileUtils do
|
|
400
403
|
described_class.touch(['/dest', '/test-file2'])
|
401
404
|
expect {
|
402
405
|
described_class.cp(['/test-file', '/test-file2'], '/dest')
|
403
|
-
}.to
|
406
|
+
}.to raise_error(Errno::ENOTDIR)
|
404
407
|
end
|
405
408
|
end
|
406
409
|
end
|
@@ -474,7 +477,7 @@ describe FileUtils do
|
|
474
477
|
it 'raises an exception' do
|
475
478
|
expect {
|
476
479
|
described_class.install('/test-file', '/test-file')
|
477
|
-
}.to
|
480
|
+
}.to raise_error ArgumentError
|
478
481
|
end
|
479
482
|
end
|
480
483
|
|
@@ -527,7 +530,7 @@ describe FileUtils do
|
|
527
530
|
described_class.touch('/test-file2')
|
528
531
|
expect {
|
529
532
|
described_class.ln('/test-file', '/test-file2')
|
530
|
-
}.to
|
533
|
+
}.to raise_error(SystemCallError)
|
531
534
|
end
|
532
535
|
|
533
536
|
context 'and +:force+ is set' do
|
@@ -552,7 +555,7 @@ describe FileUtils do
|
|
552
555
|
described_class.touch(['/test-file2', '/not-a-dir'])
|
553
556
|
expect {
|
554
557
|
described_class.ln(['/test-file', '/test-file2'], '/not-a-dir')
|
555
|
-
}.to
|
558
|
+
}.to raise_error(Errno::ENOTDIR)
|
556
559
|
end
|
557
560
|
end
|
558
561
|
end
|
@@ -587,7 +590,7 @@ describe FileUtils do
|
|
587
590
|
it 'raises an exeption' do
|
588
591
|
expect {
|
589
592
|
described_class.ln_s('/test-file', '/not-a-dir')
|
590
|
-
}.to
|
593
|
+
}.to raise_error(Errno::EEXIST)
|
591
594
|
end
|
592
595
|
|
593
596
|
context 'and +:force+ is set' do
|
@@ -618,7 +621,7 @@ describe FileUtils do
|
|
618
621
|
it 'raises an error' do
|
619
622
|
expect {
|
620
623
|
described_class.ln_s(['/test-file', '/test-file2'], '/not-a-dir')
|
621
|
-
}.to
|
624
|
+
}.to raise_error(Errno::ENOTDIR)
|
622
625
|
end
|
623
626
|
end
|
624
627
|
end
|
@@ -667,8 +670,6 @@ describe FileUtils do
|
|
667
670
|
end
|
668
671
|
end
|
669
672
|
end
|
670
|
-
|
671
|
-
|
672
673
|
end
|
673
674
|
|
674
675
|
describe '.mkdir_p' do
|
@@ -867,7 +868,7 @@ describe FileUtils do
|
|
867
868
|
|
868
869
|
it 'cannot remove a directory' do
|
869
870
|
described_class.mkdir('/test-dir')
|
870
|
-
expect { described_class.rm('/test-dir') }.to
|
871
|
+
expect { described_class.rm('/test-dir') }.to raise_error(Errno::EPERM)
|
871
872
|
end
|
872
873
|
|
873
874
|
context 'when +:force+ is set' do
|
@@ -882,7 +883,9 @@ describe FileUtils do
|
|
882
883
|
|
883
884
|
describe '.rm_f' do
|
884
885
|
it 'calls rm with +:force+ set to true' do
|
885
|
-
expect(described_class)
|
886
|
+
expect(described_class)
|
887
|
+
.to receive(:rm).with('test', a_hash_including(force: true))
|
888
|
+
|
886
889
|
described_class.rm_f('test')
|
887
890
|
end
|
888
891
|
end
|
@@ -917,7 +920,9 @@ describe FileUtils do
|
|
917
920
|
|
918
921
|
describe '.rm_rf' do
|
919
922
|
it 'calls rm with +:force+ set to true' do
|
920
|
-
expect(described_class)
|
923
|
+
expect(described_class)
|
924
|
+
.to receive(:rm_r).with('test', a_hash_including(force: true))
|
925
|
+
|
921
926
|
described_class.rm_rf('test')
|
922
927
|
end
|
923
928
|
end
|
data/spec/memfs/dir_spec.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
require '
|
1
|
+
require 'pathname'
|
2
2
|
|
3
3
|
module MemFs
|
4
|
-
describe Dir do
|
4
|
+
::RSpec.describe Dir do
|
5
5
|
subject { described_class.new('/test') }
|
6
6
|
|
7
7
|
before { described_class.mkdir '/test' }
|
@@ -58,7 +58,6 @@ module MemFs
|
|
58
58
|
before { allow(Process).to receive_messages(uid: 0) }
|
59
59
|
|
60
60
|
it "changes the process's idea of the file system root" do
|
61
|
-
|
62
61
|
described_class.mkdir('/test/subdir')
|
63
62
|
described_class.chroot('/test')
|
64
63
|
|
@@ -146,7 +145,9 @@ module MemFs
|
|
146
145
|
|
147
146
|
context "and the directory doesn't exist" do
|
148
147
|
it 'raises an exception' do
|
149
|
-
expect {
|
148
|
+
expect {
|
149
|
+
described_class.foreach('/no-dir') {}
|
150
|
+
}.to raise_error Errno::ENOENT
|
150
151
|
end
|
151
152
|
end
|
152
153
|
|
@@ -154,7 +155,7 @@ module MemFs
|
|
154
155
|
it 'raises an exception' do
|
155
156
|
expect {
|
156
157
|
described_class.foreach('/test/test-file') {}
|
157
|
-
}.to raise_error
|
158
|
+
}.to raise_error Errno::ENOTDIR
|
158
159
|
end
|
159
160
|
end
|
160
161
|
end
|
@@ -215,6 +216,8 @@ module MemFs
|
|
215
216
|
it_behaves_like 'returning matching filenames', '/test?', %w[/test0 /test1 /test2]
|
216
217
|
it_behaves_like 'returning matching filenames', '/test[01]', %w[/test0 /test1]
|
217
218
|
it_behaves_like 'returning matching filenames', '/test[^2]', %w[/test0 /test1]
|
219
|
+
it_behaves_like 'returning matching filenames', Pathname.new('/'), %w[/]
|
220
|
+
it_behaves_like 'returning matching filenames', Pathname.new('/*'), %w[/tmp /test0 /test1 /test2]
|
218
221
|
|
219
222
|
if defined?(File::FNM_EXTGLOB)
|
220
223
|
it_behaves_like 'returning matching filenames', '/test{1,2}', %w[/test1 /test2]
|
@@ -308,7 +311,9 @@ module MemFs
|
|
308
311
|
|
309
312
|
context "when the given directory doesn't exist" do
|
310
313
|
it 'raises an exception' do
|
311
|
-
expect {
|
314
|
+
expect {
|
315
|
+
described_class.open('/no-dir')
|
316
|
+
}.to raise_error Errno::ENOENT
|
312
317
|
end
|
313
318
|
end
|
314
319
|
|
@@ -316,7 +321,9 @@ module MemFs
|
|
316
321
|
before { _fs.touch('/test/test-file') }
|
317
322
|
|
318
323
|
it 'raises an exception' do
|
319
|
-
expect {
|
324
|
+
expect {
|
325
|
+
described_class.open('/test/test-file')
|
326
|
+
}.to raise_error Errno::ENOTDIR
|
320
327
|
end
|
321
328
|
end
|
322
329
|
end
|
@@ -324,7 +331,9 @@ module MemFs
|
|
324
331
|
describe '.new' do
|
325
332
|
context "when the given directory doesn't exist" do
|
326
333
|
it 'raises an exception' do
|
327
|
-
expect {
|
334
|
+
expect {
|
335
|
+
described_class.new('/no-dir')
|
336
|
+
}.to raise_error Errno::ENOENT
|
328
337
|
end
|
329
338
|
end
|
330
339
|
|
@@ -332,7 +341,9 @@ module MemFs
|
|
332
341
|
before { _fs.touch('/test/test-file') }
|
333
342
|
|
334
343
|
it 'raises an exception' do
|
335
|
-
expect {
|
344
|
+
expect {
|
345
|
+
described_class.new('/test/test-file')
|
346
|
+
}.to raise_error Errno::ENOTDIR
|
336
347
|
end
|
337
348
|
end
|
338
349
|
end
|
@@ -2,7 +2,7 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
module MemFs
|
4
4
|
module Fake
|
5
|
-
describe Directory do
|
5
|
+
::RSpec.describe Directory do
|
6
6
|
subject(:directory) { described_class.new('test') }
|
7
7
|
|
8
8
|
describe '.new' do
|
@@ -70,6 +70,10 @@ module MemFs
|
|
70
70
|
it 'should remove any leading / in the path' do
|
71
71
|
expect(directory.find('/sub_dir/file')).to be(file)
|
72
72
|
end
|
73
|
+
|
74
|
+
it 'should remove any trailing / in the path' do
|
75
|
+
expect(directory.find('sub_dir/file/')).to be(file)
|
76
|
+
end
|
73
77
|
end
|
74
78
|
|
75
79
|
describe '#parent=' do
|
@@ -2,7 +2,7 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
module MemFs
|
4
4
|
module Fake
|
5
|
-
describe Entry do
|
5
|
+
::RSpec.describe Entry do
|
6
6
|
let(:entry) { described_class.new('test') }
|
7
7
|
let(:parent) { Directory.new('parent') }
|
8
8
|
let(:time) { Time.now - 5000 }
|
@@ -13,11 +13,11 @@ module MemFs
|
|
13
13
|
end
|
14
14
|
|
15
15
|
shared_examples 'it has accessors for' do |attribute|
|
16
|
-
let(:
|
16
|
+
let(:expected_value) { defined?(expected) ? expected : value }
|
17
17
|
|
18
18
|
it attribute do
|
19
19
|
entry.send(:"#{attribute}=", value)
|
20
|
-
expect(entry.public_send(attribute)).to eq(
|
20
|
+
expect(entry.public_send(attribute)).to eq(expected_value)
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
@@ -125,13 +125,13 @@ module MemFs
|
|
125
125
|
|
126
126
|
describe '#dev' do
|
127
127
|
it 'returns an integer representing the device on which the entry resides' do
|
128
|
-
expect(entry.dev).to be_a(
|
128
|
+
expect(entry.dev).to be_a(Integer)
|
129
129
|
end
|
130
130
|
end
|
131
131
|
|
132
132
|
describe '#ino' do
|
133
133
|
it 'Returns the inode number for the entry' do
|
134
|
-
expect(entry.ino).to be_a(
|
134
|
+
expect(entry.ino).to be_a(Integer)
|
135
135
|
end
|
136
136
|
end
|
137
137
|
|
@@ -2,7 +2,7 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
module MemFs
|
4
4
|
module Fake
|
5
|
-
describe Symlink do
|
5
|
+
::RSpec.describe Symlink do
|
6
6
|
describe '#content' do
|
7
7
|
it "returns the target's content" do
|
8
8
|
MemFs::File.open('/test-file', 'w') { |f| f.puts 'test' }
|
@@ -63,7 +63,7 @@ module MemFs
|
|
63
63
|
symlink = described_class.new('/test-link', '/no-file')
|
64
64
|
expect {
|
65
65
|
symlink.dereferenced_path
|
66
|
-
}.to
|
66
|
+
}.to raise_error Errno::ENOENT
|
67
67
|
end
|
68
68
|
end
|
69
69
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
module MemFs
|
4
|
-
describe File::Stat do
|
4
|
+
::RSpec.describe File::Stat do
|
5
5
|
let(:file_stat) { described_class.new('/test-file') }
|
6
6
|
let(:dereferenced_file_stat) { described_class.new('/test-file', true) }
|
7
7
|
|
@@ -131,7 +131,7 @@ module MemFs
|
|
131
131
|
|
132
132
|
describe '#dev' do
|
133
133
|
it 'returns an integer representing the device on which stat resides' do
|
134
|
-
expect(file_stat.dev).to be_a(
|
134
|
+
expect(file_stat.dev).to be_a(Integer)
|
135
135
|
end
|
136
136
|
end
|
137
137
|
|
@@ -449,7 +449,7 @@ module MemFs
|
|
449
449
|
|
450
450
|
describe '#ino' do
|
451
451
|
it 'returns the inode number for stat.' do
|
452
|
-
expect(file_stat.ino).to be_a(
|
452
|
+
expect(file_stat.ino).to be_a(Integer)
|
453
453
|
end
|
454
454
|
end
|
455
455
|
|
@@ -606,6 +606,7 @@ module MemFs
|
|
606
606
|
|
607
607
|
context 'when the file does not have the setgid bit set' do
|
608
608
|
it 'returns false' do
|
609
|
+
_fs.chmod(0644, '/test-file')
|
609
610
|
expect(file_stat.setgid?).to be false
|
610
611
|
end
|
611
612
|
end
|
@@ -621,6 +622,7 @@ module MemFs
|
|
621
622
|
|
622
623
|
context 'when the file does not have the setuid bit set' do
|
623
624
|
it 'returns false' do
|
625
|
+
_fs.chmod(0644, '/test-file')
|
624
626
|
expect(file_stat.setuid?).to be false
|
625
627
|
end
|
626
628
|
end
|
@@ -643,6 +645,7 @@ module MemFs
|
|
643
645
|
end
|
644
646
|
|
645
647
|
it "returns false if the named file hasn't' the sticky bit set" do
|
648
|
+
_fs.chmod(0666, '/test-file')
|
646
649
|
expect(file_stat.sticky?).to be false
|
647
650
|
end
|
648
651
|
end
|
data/spec/memfs/file_spec.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
require 'pathname'
|
3
5
|
|
4
6
|
module MemFs
|
5
|
-
describe File do
|
7
|
+
::RSpec.describe File do
|
6
8
|
subject { described_class.new('/test-file') }
|
7
9
|
let(:write_subject) { described_class.new('/test-file', 'w') }
|
8
10
|
|
@@ -15,7 +17,6 @@ module MemFs
|
|
15
17
|
described_class.symlink '/no-file', '/no-link'
|
16
18
|
end
|
17
19
|
|
18
|
-
|
19
20
|
it 'implements Enumerable' do
|
20
21
|
expect(described_class.ancestors).to include Enumerable
|
21
22
|
end
|
@@ -205,7 +206,7 @@ module MemFs
|
|
205
206
|
|
206
207
|
it 'ignores -1 user id' do
|
207
208
|
expect {
|
208
|
-
described_class.chown
|
209
|
+
described_class.chown(-1, 42, '/test-file')
|
209
210
|
}.to_not change { described_class.stat('/test-file').uid }
|
210
211
|
end
|
211
212
|
|
@@ -555,21 +556,21 @@ module MemFs
|
|
555
556
|
|
556
557
|
describe '.ftype' do
|
557
558
|
context 'when the named entry is a regular file' do
|
558
|
-
it
|
559
|
+
it 'returns "file"' do
|
559
560
|
ftype = described_class.ftype('/test-file')
|
560
561
|
expect(ftype).to eq 'file'
|
561
562
|
end
|
562
563
|
end
|
563
564
|
|
564
565
|
context 'when the named entry is a directory' do
|
565
|
-
it
|
566
|
+
it 'returns "directory"' do
|
566
567
|
ftype = described_class.ftype('/test-dir')
|
567
568
|
expect(ftype).to eq 'directory'
|
568
569
|
end
|
569
570
|
end
|
570
571
|
|
571
572
|
context 'when the named entry is a block device' do
|
572
|
-
it
|
573
|
+
it 'returns "blockSpecial"' do
|
573
574
|
_fs.touch '/block-file'
|
574
575
|
file = _fs.find('/block-file')
|
575
576
|
file.block_device = true
|
@@ -580,7 +581,7 @@ module MemFs
|
|
580
581
|
end
|
581
582
|
|
582
583
|
context 'when the named entry is a character device' do
|
583
|
-
it
|
584
|
+
it 'returns "characterSpecial"' do
|
584
585
|
_fs.touch '/character-file'
|
585
586
|
file = _fs.find('/character-file')
|
586
587
|
file.character_device = true
|
@@ -591,7 +592,7 @@ module MemFs
|
|
591
592
|
end
|
592
593
|
|
593
594
|
context 'when the named entry is a symlink' do
|
594
|
-
it
|
595
|
+
it 'returns "link"' do
|
595
596
|
ftype = described_class.ftype('/test-link')
|
596
597
|
expect(ftype).to eq 'link'
|
597
598
|
end
|
@@ -600,7 +601,7 @@ module MemFs
|
|
600
601
|
# fifo and socket not handled for now
|
601
602
|
|
602
603
|
context 'when the named entry has no specific type' do
|
603
|
-
it
|
604
|
+
it 'returns "unknown"' do
|
604
605
|
root = _fs.find '/'
|
605
606
|
root.add_entry Fake::Entry.new('test-entry')
|
606
607
|
|
@@ -727,7 +728,7 @@ module MemFs
|
|
727
728
|
expect(mode).to be 0100777
|
728
729
|
end
|
729
730
|
|
730
|
-
it
|
731
|
+
it 'does not change permission bits on the link’s target' do
|
731
732
|
old_mode = described_class.stat('/test-file').mode
|
732
733
|
described_class.lchmod 0777, '/test-link'
|
733
734
|
|
@@ -791,18 +792,45 @@ module MemFs
|
|
791
792
|
is_symlink = described_class.lstat('/test-link').symlink?
|
792
793
|
expect(is_symlink).to be true
|
793
794
|
end
|
795
|
+
end
|
796
|
+
end
|
794
797
|
|
795
|
-
|
796
|
-
|
798
|
+
describe '.new' do
|
799
|
+
it 'resets the file position to the beginning' do
|
800
|
+
described_class.open('/test-file', 'w') { |f| f.write 'hello' }
|
801
|
+
|
802
|
+
contents = described_class.open('/test-file', 'r', &:read)
|
803
|
+
expect(contents).to eq('hello')
|
804
|
+
|
805
|
+
contents = described_class.open('/test-file', 'r', &:read)
|
806
|
+
expect(contents).to eq('hello')
|
807
|
+
end
|
808
|
+
|
809
|
+
context 'when only the filename is provided' do
|
810
|
+
context 'and the file exists' do
|
811
|
+
it 'returns the open file' do
|
812
|
+
file = described_class.new('/test-file')
|
813
|
+
expect(file).to be_a(MemFs::File)
|
814
|
+
end
|
815
|
+
end
|
816
|
+
|
817
|
+
context 'and the file does not exist' do
|
818
|
+
it 'raises an exception' do
|
819
|
+
expect {
|
820
|
+
described_class.new('missing-file')
|
821
|
+
}.to raise_error(Errno::ENOENT)
|
822
|
+
end
|
823
|
+
end
|
824
|
+
|
825
|
+
context 'when the file is a symlink and its target does not exist' do
|
826
|
+
it 'raises an exception' do
|
797
827
|
expect {
|
798
|
-
described_class.
|
799
|
-
}.
|
828
|
+
described_class.new('/no-link')
|
829
|
+
}.to raise_error(Errno::ENOENT)
|
800
830
|
end
|
801
831
|
end
|
802
832
|
end
|
803
|
-
end
|
804
833
|
|
805
|
-
describe '.new' do
|
806
834
|
context 'when the mode is provided' do
|
807
835
|
context 'and it is an integer' do
|
808
836
|
subject { described_class.new('/test-file', File::RDWR) }
|
@@ -820,7 +848,8 @@ module MemFs
|
|
820
848
|
|
821
849
|
it 'sets the write+create+truncate mode for "w"' do
|
822
850
|
subject = described_class.new('/test-file', 'w')
|
823
|
-
expect(subject.send(:opening_mode)).to
|
851
|
+
expect(subject.send(:opening_mode)).to \
|
852
|
+
eq File::CREAT | File::TRUNC | File::WRONLY
|
824
853
|
end
|
825
854
|
|
826
855
|
it 'sets the read+write mode for "r+"' do
|
@@ -830,17 +859,20 @@ module MemFs
|
|
830
859
|
|
831
860
|
it 'sets the read+write+create+truncate mode for "w+"' do
|
832
861
|
subject = described_class.new('/test-file', 'w+')
|
833
|
-
expect(subject.send(:opening_mode)).to
|
862
|
+
expect(subject.send(:opening_mode)).to \
|
863
|
+
eq File::CREAT | File::TRUNC | File::RDWR
|
834
864
|
end
|
835
865
|
|
836
866
|
it 'sets the write+create+append mode for "a"' do
|
837
867
|
subject = described_class.new('/test-file', 'a')
|
838
|
-
expect(subject.send(:opening_mode)).to
|
868
|
+
expect(subject.send(:opening_mode)).to \
|
869
|
+
eq File::CREAT | File::APPEND | File::WRONLY
|
839
870
|
end
|
840
871
|
|
841
872
|
it 'sets the read+write+create+append mode for "a+"' do
|
842
873
|
subject = described_class.new('/test-file', 'a+')
|
843
|
-
expect(subject.send(:opening_mode)).to
|
874
|
+
expect(subject.send(:opening_mode)).to \
|
875
|
+
eq File::CREAT | File::APPEND | File::RDWR
|
844
876
|
end
|
845
877
|
|
846
878
|
it 'handles the :bom option' do
|
@@ -889,7 +921,9 @@ module MemFs
|
|
889
921
|
|
890
922
|
context 'when too many arguments are given' do
|
891
923
|
it 'raises an exception' do
|
892
|
-
expect {
|
924
|
+
expect {
|
925
|
+
described_class.new(1, 2, 3, 4)
|
926
|
+
}.to raise_error(ArgumentError)
|
893
927
|
end
|
894
928
|
end
|
895
929
|
end
|
@@ -989,18 +1023,18 @@ module MemFs
|
|
989
1023
|
|
990
1024
|
context 'when the last argument is a hash' do
|
991
1025
|
it 'passes the contained options to +open+' do
|
992
|
-
expect(described_class).to
|
993
|
-
|
994
|
-
|
1026
|
+
expect(described_class).to \
|
1027
|
+
receive(:open)
|
1028
|
+
.with('/test-file', File::RDONLY, encoding: 'UTF-8')
|
1029
|
+
.and_return(subject)
|
995
1030
|
|
996
1031
|
described_class.read '/test-file', encoding: 'UTF-8'
|
997
1032
|
end
|
998
1033
|
|
999
1034
|
context 'when it contains the +open_args+ key' do
|
1000
1035
|
it 'takes precedence over the other options' do
|
1001
|
-
expect(described_class).to
|
1002
|
-
|
1003
|
-
.and_return(subject)
|
1036
|
+
expect(described_class).to \
|
1037
|
+
receive(:open).with('/test-file', 'r').and_return(subject)
|
1004
1038
|
|
1005
1039
|
described_class.read '/test-file', mode: 'w', open_args: ['r']
|
1006
1040
|
end
|
@@ -1220,7 +1254,7 @@ module MemFs
|
|
1220
1254
|
it 'raises an exception' do
|
1221
1255
|
expect {
|
1222
1256
|
described_class.realdirpath '/no-dir/test-file'
|
1223
|
-
}.to raise_error
|
1257
|
+
}.to raise_error Errno::ENOENT
|
1224
1258
|
end
|
1225
1259
|
end
|
1226
1260
|
end
|
@@ -1284,7 +1318,7 @@ module MemFs
|
|
1284
1318
|
it 'raises an exception' do
|
1285
1319
|
expect {
|
1286
1320
|
described_class.realpath '/no-dir/test-file'
|
1287
|
-
}.to raise_error
|
1321
|
+
}.to raise_error Errno::ENOENT
|
1288
1322
|
end
|
1289
1323
|
end
|
1290
1324
|
end
|
@@ -1316,6 +1350,8 @@ module MemFs
|
|
1316
1350
|
|
1317
1351
|
context 'and the named file does not have the setgid bit set' do
|
1318
1352
|
it 'returns false' do
|
1353
|
+
_fs.chmod 0644, '/test-file'
|
1354
|
+
|
1319
1355
|
setgid = File.setgid?('/test-file')
|
1320
1356
|
expect(setgid).not_to be true
|
1321
1357
|
end
|
@@ -1343,6 +1379,8 @@ module MemFs
|
|
1343
1379
|
|
1344
1380
|
context 'and the named file does not have the setuid bit set' do
|
1345
1381
|
it 'returns false' do
|
1382
|
+
_fs.chmod 0644, '/test-file'
|
1383
|
+
|
1346
1384
|
setuid = File.setuid?('/test-file')
|
1347
1385
|
expect(setuid).not_to be true
|
1348
1386
|
end
|
@@ -1441,10 +1479,10 @@ module MemFs
|
|
1441
1479
|
end
|
1442
1480
|
|
1443
1481
|
it 'always returns a new object' do
|
1444
|
-
|
1445
|
-
|
1482
|
+
stat1 = described_class.stat('/test-file')
|
1483
|
+
stat2 = described_class.stat('/test-file')
|
1446
1484
|
|
1447
|
-
expect(
|
1485
|
+
expect(stat2).not_to be stat1
|
1448
1486
|
end
|
1449
1487
|
end
|
1450
1488
|
|
@@ -1458,8 +1496,9 @@ module MemFs
|
|
1458
1496
|
expect(sticky).to be true
|
1459
1497
|
end
|
1460
1498
|
|
1461
|
-
it
|
1499
|
+
it 'returns false if the named file hasn’t the sticky bit set' do
|
1462
1500
|
_fs.touch '/test-file'
|
1501
|
+
_fs.chmod 0666, '/test-file'
|
1463
1502
|
|
1464
1503
|
sticky = File.sticky?('/test-file')
|
1465
1504
|
expect(sticky).to be false
|
@@ -1540,13 +1579,17 @@ module MemFs
|
|
1540
1579
|
|
1541
1580
|
context 'when the named file does not exist' do
|
1542
1581
|
it 'raises an exception' do
|
1543
|
-
expect {
|
1582
|
+
expect {
|
1583
|
+
described_class.truncate '/no-file', 5
|
1584
|
+
}.to raise_error Errno::ENOENT
|
1544
1585
|
end
|
1545
1586
|
end
|
1546
1587
|
|
1547
1588
|
context 'when the given size is negative' do
|
1548
1589
|
it 'it raises an exception' do
|
1549
|
-
expect {
|
1590
|
+
expect {
|
1591
|
+
described_class.truncate '/test-file', -1
|
1592
|
+
}.to raise_error TypeError
|
1550
1593
|
end
|
1551
1594
|
end
|
1552
1595
|
end
|
@@ -1803,6 +1846,54 @@ module MemFs
|
|
1803
1846
|
end
|
1804
1847
|
end
|
1805
1848
|
|
1849
|
+
describe '.write' do
|
1850
|
+
it 'writes the string to the given file' do
|
1851
|
+
described_class.write('/test-file', 'test')
|
1852
|
+
read_content = described_class.read('/test-file')
|
1853
|
+
expect(read_content).to eq 'test'
|
1854
|
+
end
|
1855
|
+
|
1856
|
+
context 'when +offset+ is provided' do
|
1857
|
+
it 'writes the string to the given file when offset is 0' do
|
1858
|
+
described_class.write('/test-file', 'test', 0)
|
1859
|
+
read_content = described_class.read('/test-file')
|
1860
|
+
expect(read_content).to eq 'test'
|
1861
|
+
end
|
1862
|
+
|
1863
|
+
it 'writes the string to the given file when offset is nil' do
|
1864
|
+
described_class.write('/test-file', 'test', nil)
|
1865
|
+
read_content = described_class.read('/test-file')
|
1866
|
+
expect(read_content).to eq 'test'
|
1867
|
+
end
|
1868
|
+
|
1869
|
+
it 'starts writing from the offset' do
|
1870
|
+
skip('Offsets not yet implemented in IO.write')
|
1871
|
+
described_class.write('/test-file', 'test')
|
1872
|
+
described_class.write('/test-file', 'test', 2)
|
1873
|
+
read_content = described_class.read('/test-file')
|
1874
|
+
expect(read_content).to eq 'tetest'
|
1875
|
+
end
|
1876
|
+
|
1877
|
+
it 'raises an error if offset is negative' do
|
1878
|
+
expect {
|
1879
|
+
described_class.write('/test-file', 'foo', -1)
|
1880
|
+
}.to raise_error Errno::EINVAL
|
1881
|
+
end
|
1882
|
+
|
1883
|
+
it 'raises an error if offset is a boolean' do
|
1884
|
+
expect {
|
1885
|
+
described_class.write '/test-file', 'foo', false
|
1886
|
+
}.to raise_error TypeError
|
1887
|
+
end
|
1888
|
+
|
1889
|
+
it 'raises an error if offset is a string' do
|
1890
|
+
expect {
|
1891
|
+
described_class.write '/test-file', 'foo', 'offset'
|
1892
|
+
}.to raise_error TypeError
|
1893
|
+
end
|
1894
|
+
end
|
1895
|
+
end
|
1896
|
+
|
1806
1897
|
describe '.zero?' do
|
1807
1898
|
context 'when the named file exists' do
|
1808
1899
|
context 'and has a zero size' do
|
@@ -1906,7 +1997,7 @@ module MemFs
|
|
1906
1997
|
end
|
1907
1998
|
|
1908
1999
|
describe '#autoclose?' do
|
1909
|
-
it
|
2000
|
+
it 'returns true by default' do
|
1910
2001
|
expect(subject.autoclose?).to be true
|
1911
2002
|
end
|
1912
2003
|
|
@@ -1938,7 +2029,7 @@ module MemFs
|
|
1938
2029
|
expect(subject.binmode?).to be true
|
1939
2030
|
end
|
1940
2031
|
|
1941
|
-
it
|
2032
|
+
it 'sets the file encoding to ASCII-8BIT' do
|
1942
2033
|
subject.binmode
|
1943
2034
|
|
1944
2035
|
encoding = subject.external_encoding
|
@@ -1947,7 +2038,7 @@ module MemFs
|
|
1947
2038
|
end
|
1948
2039
|
|
1949
2040
|
describe '#binmode?' do
|
1950
|
-
it
|
2041
|
+
it 'returns false by default' do
|
1951
2042
|
expect(subject.binmode?).to be false
|
1952
2043
|
end
|
1953
2044
|
|
@@ -2023,7 +2114,7 @@ module MemFs
|
|
2023
2114
|
|
2024
2115
|
it 'ignores -1 user id' do
|
2025
2116
|
expect {
|
2026
|
-
subject.chown
|
2117
|
+
subject.chown(-1, 42)
|
2027
2118
|
}.to_not change { subject.stat.uid }
|
2028
2119
|
end
|
2029
2120
|
|
@@ -2097,7 +2188,7 @@ module MemFs
|
|
2097
2188
|
expect(subject.close_on_exec?).to be true
|
2098
2189
|
end
|
2099
2190
|
|
2100
|
-
context
|
2191
|
+
context 'when the close-on-exec flag is set to false' do
|
2101
2192
|
before { subject.close_on_exec = false }
|
2102
2193
|
|
2103
2194
|
it 'returns false' do
|
@@ -2186,7 +2277,7 @@ module MemFs
|
|
2186
2277
|
context 'when the file is not open for reading' do
|
2187
2278
|
it 'raises an exception' do
|
2188
2279
|
expect {
|
2189
|
-
write_subject.each_byte {
|
2280
|
+
write_subject.each_byte {}
|
2190
2281
|
}.to raise_error IOError
|
2191
2282
|
end
|
2192
2283
|
|
@@ -2223,7 +2314,7 @@ module MemFs
|
|
2223
2314
|
context 'when the file is not open for reading' do
|
2224
2315
|
it 'raises an exception' do
|
2225
2316
|
expect {
|
2226
|
-
write_subject.each_char {
|
2317
|
+
write_subject.each_char {}
|
2227
2318
|
}.to raise_error IOError
|
2228
2319
|
end
|
2229
2320
|
|
@@ -2325,19 +2416,11 @@ module MemFs
|
|
2325
2416
|
expect(subject.lstat).to be_a File::Stat
|
2326
2417
|
end
|
2327
2418
|
|
2328
|
-
|
2329
|
-
|
2419
|
+
context 'when the given file is a symlink' do
|
2420
|
+
subject { described_class.new('/test-link') }
|
2330
2421
|
|
2331
|
-
|
2332
|
-
|
2333
|
-
end
|
2334
|
-
|
2335
|
-
context 'when the named file is a symlink' do
|
2336
|
-
context 'and its target does not exist' do
|
2337
|
-
it 'ignores errors' do
|
2338
|
-
file = described_class.new('/no-link')
|
2339
|
-
expect { file.lstat }.not_to raise_error
|
2340
|
-
end
|
2422
|
+
it 'does not follow the last symbolic link' do
|
2423
|
+
expect(subject.lstat).to be_symlink
|
2341
2424
|
end
|
2342
2425
|
end
|
2343
2426
|
end
|
@@ -2507,7 +2590,7 @@ module MemFs
|
|
2507
2590
|
expect(content).to eq "test\n"
|
2508
2591
|
end
|
2509
2592
|
|
2510
|
-
it
|
2593
|
+
it 'does not override the file’s content' do
|
2511
2594
|
write_subject.puts 'test'
|
2512
2595
|
write_subject.puts 'test'
|
2513
2596
|
write_subject.close
|
@@ -2599,7 +2682,7 @@ module MemFs
|
|
2599
2682
|
|
2600
2683
|
context 'when +whence+ is IO::SEEK_END' do
|
2601
2684
|
it 'seeks to +amount+ plus end of stream' do
|
2602
|
-
subject.seek
|
2685
|
+
subject.seek(-1, ::IO::SEEK_END)
|
2603
2686
|
|
2604
2687
|
expect(subject.pos).to be 4
|
2605
2688
|
end
|
@@ -2621,7 +2704,7 @@ module MemFs
|
|
2621
2704
|
|
2622
2705
|
context 'if the position ends up to be less than zero' do
|
2623
2706
|
it 'raises an exception' do
|
2624
|
-
expect { subject.seek
|
2707
|
+
expect { subject.seek(-1) }.to raise_error Errno::EINVAL
|
2625
2708
|
end
|
2626
2709
|
end
|
2627
2710
|
end
|