memfs 0.0.2 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +4 -0
- data/README.md +7 -1
- data/lib/memfs/dir.rb +1 -1
- data/lib/memfs/fake/directory.rb +4 -0
- data/lib/memfs/fake/entry.rb +20 -3
- data/lib/memfs/fake/file.rb +8 -0
- data/lib/memfs/fake/file/content.rb +4 -0
- data/lib/memfs/fake/symlink.rb +24 -0
- data/lib/memfs/file.rb +89 -7
- data/lib/memfs/file/stat.rb +127 -4
- data/lib/memfs/file_system.rb +0 -8
- data/lib/memfs/version.rb +1 -1
- data/spec/memfs/fake/directory_spec.rb +6 -0
- data/spec/memfs/fake/entry_spec.rb +54 -24
- data/spec/memfs/fake/file/content_spec.rb +9 -0
- data/spec/memfs/fake/file_spec.rb +22 -0
- data/spec/memfs/fake/symlink_spec.rb +70 -0
- data/spec/memfs/file/stat_spec.rb +728 -84
- data/spec/memfs/file_spec.rb +1028 -39
- data/spec/memfs/file_system_spec.rb +63 -35
- metadata +3 -5
- data/.DS_Store +0 -0
- data/lib/fileutils.rb +0 -1738
data/lib/memfs/file_system.rb
CHANGED
@@ -43,10 +43,6 @@ module MemFs
|
|
43
43
|
entry.gid = gid if gid && gid != -1
|
44
44
|
end
|
45
45
|
|
46
|
-
def directory?(path)
|
47
|
-
find(path).is_a?(Fake::Directory)
|
48
|
-
end
|
49
|
-
|
50
46
|
def dirname(path)
|
51
47
|
File.dirname(path)
|
52
48
|
end
|
@@ -128,10 +124,6 @@ module MemFs
|
|
128
124
|
find_parent!(new_name).add_entry Fake::Symlink.new(new_name, old_name)
|
129
125
|
end
|
130
126
|
|
131
|
-
def symlink?(path)
|
132
|
-
find(path).is_a?(Fake::Symlink)
|
133
|
-
end
|
134
|
-
|
135
127
|
def touch(*paths)
|
136
128
|
paths.each do |path|
|
137
129
|
entry = find(path)
|
data/lib/memfs/version.rb
CHANGED
@@ -29,6 +29,18 @@ module MemFs
|
|
29
29
|
let(:value) { time }
|
30
30
|
end
|
31
31
|
|
32
|
+
it_behaves_like 'it has accessors for', :block_device do
|
33
|
+
let(:value) { true }
|
34
|
+
end
|
35
|
+
|
36
|
+
it_behaves_like 'it has accessors for', :character_device do
|
37
|
+
let(:value) { true }
|
38
|
+
end
|
39
|
+
|
40
|
+
it_behaves_like 'it has accessors for', :ctime do
|
41
|
+
let(:value) { time }
|
42
|
+
end
|
43
|
+
|
32
44
|
it_behaves_like 'it has accessors for', :mtime do
|
33
45
|
let(:value) { time }
|
34
46
|
end
|
@@ -50,32 +62,13 @@ module MemFs
|
|
50
62
|
let(:value) { parent }
|
51
63
|
end
|
52
64
|
|
53
|
-
describe ".delete" do
|
54
|
-
it "removes the entry from its parent" do
|
55
|
-
entry.delete
|
56
|
-
expect(parent.entries).not_to have_value(entry)
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
describe '.dereferenced' do
|
61
|
-
it "returns the entry itself" do
|
62
|
-
expect(entry.dereferenced).to be(entry)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
describe '.find' do
|
67
|
-
it "raises an error" do
|
68
|
-
expect { entry.find('test') }.to raise_error(Errno::ENOTDIR)
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
65
|
describe ".new" do
|
73
66
|
it "sets its default uid to the current user's uid" do
|
74
|
-
expect(entry.uid).to eq(
|
67
|
+
expect(entry.uid).to eq(Process.euid)
|
75
68
|
end
|
76
69
|
|
77
70
|
it "sets its default gid to the current user's gid" do
|
78
|
-
expect(entry.gid).to eq(
|
71
|
+
expect(entry.gid).to eq(Process.egid)
|
79
72
|
end
|
80
73
|
|
81
74
|
it "extract its name from the path passed as argument" do
|
@@ -99,9 +92,34 @@ module MemFs
|
|
99
92
|
end
|
100
93
|
end
|
101
94
|
|
102
|
-
describe "
|
103
|
-
it "
|
104
|
-
|
95
|
+
describe "#delete" do
|
96
|
+
it "removes the entry from its parent" do
|
97
|
+
entry.delete
|
98
|
+
expect(parent.entries).not_to have_value(entry)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe '#dereferenced' do
|
103
|
+
it "returns the entry itself" do
|
104
|
+
expect(entry.dereferenced).to be(entry)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe "#dereferenced_name" do
|
109
|
+
it "returns the entry name" do
|
110
|
+
expect(entry.dereferenced_name).to eq('test')
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
describe "#dereferenced_path" do
|
115
|
+
it "returns the entry path" do
|
116
|
+
expect(entry.dereferenced_path).to eq('/parent/test')
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
describe '#find' do
|
121
|
+
it "raises an error" do
|
122
|
+
expect { entry.find('test') }.to raise_error(Errno::ENOTDIR)
|
105
123
|
end
|
106
124
|
end
|
107
125
|
|
@@ -117,6 +135,12 @@ module MemFs
|
|
117
135
|
end
|
118
136
|
end
|
119
137
|
|
138
|
+
describe "#path" do
|
139
|
+
it "returns the complete path of the entry" do
|
140
|
+
expect(entry.path).to eq('/parent/test')
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
120
144
|
describe "#touch" do
|
121
145
|
let(:time) { Time.now - 5000 }
|
122
146
|
|
@@ -135,6 +159,12 @@ module MemFs
|
|
135
159
|
expect(entry.mtime).not_to eq(time)
|
136
160
|
end
|
137
161
|
end
|
162
|
+
|
163
|
+
describe "#type" do
|
164
|
+
it "returns 'unknown" do
|
165
|
+
expect(entry.type).to eq('unknown')
|
166
|
+
end
|
167
|
+
end
|
138
168
|
end
|
139
169
|
end
|
140
170
|
end
|
@@ -79,6 +79,15 @@ module MemFs
|
|
79
79
|
end
|
80
80
|
end
|
81
81
|
|
82
|
+
describe "#truncate" do
|
83
|
+
subject { File::Content.new('x'*50) }
|
84
|
+
|
85
|
+
it "truncates the content to length characters" do
|
86
|
+
subject.truncate(5)
|
87
|
+
expect(subject.length).to eq(5)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
82
91
|
describe "#write" do
|
83
92
|
it "writes the given string in content" do
|
84
93
|
subject.write 'test'
|
@@ -32,6 +32,28 @@ module MemFs
|
|
32
32
|
end
|
33
33
|
end
|
34
34
|
end
|
35
|
+
|
36
|
+
describe "#type" do
|
37
|
+
context "when the file is a regular file" do
|
38
|
+
it "returns 'file'" do
|
39
|
+
expect(file.type).to eq('file')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context "when the file is a block device" do
|
44
|
+
it "returns 'blockSpecial'" do
|
45
|
+
file.block_device = true
|
46
|
+
expect(file.type).to eq('blockSpecial')
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context "when the file is a character device" do
|
51
|
+
it "returns 'characterSpecial'" do
|
52
|
+
file.character_device = true
|
53
|
+
expect(file.type).to eq('characterSpecial')
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
35
57
|
end
|
36
58
|
end
|
37
59
|
end
|
@@ -32,12 +32,82 @@ module MemFs
|
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
+
describe "#dereferenced_name" do
|
36
|
+
context "when the symlink's target exists" do
|
37
|
+
it "returns its target name" do
|
38
|
+
fs.touch('/test-file')
|
39
|
+
symlink = Symlink.new('/test-link', '/test-file')
|
40
|
+
expect(symlink.dereferenced_name).to eq('test-file')
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context "when the symlink's target does not exist" do
|
45
|
+
it "returns its target name" do
|
46
|
+
symlink = Symlink.new('/test-link', '/no-file')
|
47
|
+
expect(symlink.dereferenced_name).to eq('no-file')
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "#dereferenced_path" do
|
53
|
+
context "when the symlink's target exists" do
|
54
|
+
it "returns its target path" do
|
55
|
+
fs.touch('/test-file')
|
56
|
+
symlink = Symlink.new('/test-link', '/test-file')
|
57
|
+
expect(symlink.dereferenced_path).to eq('/test-file')
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context "when the symlink's target does not exist" do
|
62
|
+
it "raises an exception" do
|
63
|
+
symlink = Symlink.new('/test-link', '/no-file')
|
64
|
+
expect {
|
65
|
+
symlink.dereferenced_path
|
66
|
+
}.to raise_exception
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe '#find' do
|
72
|
+
let(:file) { fs.find!('/test-dir/test-file') }
|
73
|
+
|
74
|
+
before :each do
|
75
|
+
fs.mkdir '/test-dir'
|
76
|
+
fs.touch '/test-dir/test-file'
|
77
|
+
end
|
78
|
+
|
79
|
+
context "when the symlink's target exists" do
|
80
|
+
subject { Symlink.new('/test-dir-link', '/test-dir') }
|
81
|
+
|
82
|
+
it "forwards the search to it" do
|
83
|
+
entry = subject.find('test-file')
|
84
|
+
expect(entry).to eq(file)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context "when the symlink's target does not exist" do
|
89
|
+
subject { Symlink.new('/test-no-link', '/no-dir') }
|
90
|
+
|
91
|
+
it "returns nil" do
|
92
|
+
entry = subject.find('test-file')
|
93
|
+
expect(entry).to be_nil
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
35
98
|
describe '#target' do
|
36
99
|
it "returns the target of the symlink" do
|
37
100
|
s = Symlink.new('/test-link', '/test-file')
|
38
101
|
expect(s.target).to eq('/test-file')
|
39
102
|
end
|
40
103
|
end
|
104
|
+
|
105
|
+
describe "#type" do
|
106
|
+
it "returns 'link'" do
|
107
|
+
s = Symlink.new('/test-link', '/test-file')
|
108
|
+
expect(s.type).to eq('link')
|
109
|
+
end
|
110
|
+
end
|
41
111
|
end
|
42
112
|
end
|
43
113
|
end
|
@@ -2,11 +2,36 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
module MemFs
|
4
4
|
describe File::Stat do
|
5
|
+
let(:file_stat) { File::Stat.new('/test-file') }
|
6
|
+
let(:dereferenced_file_stat) { File::Stat.new('/test-file', true) }
|
7
|
+
|
8
|
+
let(:dir_link_stat) { File::Stat.new('/test-dir-link') }
|
9
|
+
let(:dereferenced_dir_link_stat) { File::Stat.new('/test-dir-link', true) }
|
10
|
+
|
11
|
+
let(:link_stat) { File::Stat.new('/test-link') }
|
12
|
+
let(:dereferenced_link_stat) { File::Stat.new('/test-link', true) }
|
13
|
+
|
14
|
+
let(:dir_stat) { File::Stat.new('/test-dir') }
|
15
|
+
let(:dereferenced_dir_stat) { File::Stat.new('/test-dir', true) }
|
16
|
+
|
17
|
+
let(:entry) { fs.find!('/test-file') }
|
18
|
+
|
19
|
+
before :each do
|
20
|
+
fs.mkdir('/test-dir')
|
21
|
+
fs.touch('/test-file')
|
22
|
+
fs.symlink('/test-file', '/test-link')
|
23
|
+
fs.symlink('/test-dir', '/test-dir-link')
|
24
|
+
fs.symlink('/no-file', '/test-no-file-link')
|
25
|
+
end
|
26
|
+
|
5
27
|
describe '.new' do
|
6
|
-
context "when optional
|
7
|
-
|
8
|
-
|
9
|
-
|
28
|
+
context "when optional dereference argument is set to true" do
|
29
|
+
context "when the last target of the link chain does not exist" do
|
30
|
+
it "raises an exception" do
|
31
|
+
expect {
|
32
|
+
File::Stat.new('/test-no-file-link', true)
|
33
|
+
}.to raise_error(Errno::ENOENT)
|
34
|
+
end
|
10
35
|
end
|
11
36
|
end
|
12
37
|
end
|
@@ -15,30 +40,23 @@ module MemFs
|
|
15
40
|
let(:time) { Time.now - 500000 }
|
16
41
|
|
17
42
|
it "returns the access time of the entry" do
|
18
|
-
fs.touch('/test-file')
|
19
43
|
entry = fs.find!('/test-file')
|
20
44
|
entry.atime = time
|
21
|
-
expect(
|
45
|
+
expect(file_stat.atime).to eq(time)
|
22
46
|
end
|
23
47
|
|
24
48
|
context "when the entry is a symlink" do
|
25
|
-
context "and the optional
|
49
|
+
context "and the optional dereference argument is true" do
|
26
50
|
it "returns the access time of the last target of the link chain" do
|
27
|
-
fs.touch('/test-file')
|
28
|
-
entry = fs.find!('/test-file')
|
29
51
|
entry.atime = time
|
30
|
-
|
31
|
-
expect(File::Stat.new('/test-link', true).atime).to eq(time)
|
52
|
+
expect(dereferenced_link_stat.atime).to eq(time)
|
32
53
|
end
|
33
54
|
end
|
34
55
|
|
35
|
-
context "and the optional
|
56
|
+
context "and the optional dereference argument is false" do
|
36
57
|
it "returns the access time of the symlink itself" do
|
37
|
-
fs.touch('/test-file')
|
38
|
-
entry = fs.find!('/test-file')
|
39
58
|
entry.atime = time
|
40
|
-
|
41
|
-
expect(File::Stat.new('/test-link').atime).not_to eq(time)
|
59
|
+
expect(link_stat.atime).not_to eq(time)
|
42
60
|
end
|
43
61
|
end
|
44
62
|
end
|
@@ -46,52 +64,130 @@ module MemFs
|
|
46
64
|
|
47
65
|
describe "#blksize" do
|
48
66
|
it "returns the block size of the file" do
|
49
|
-
|
50
|
-
expect(File::Stat.new('/test-file').blksize).to be(4096)
|
67
|
+
expect(file_stat.blksize).to be(4096)
|
51
68
|
end
|
52
69
|
end
|
53
70
|
|
54
|
-
describe "#
|
55
|
-
|
56
|
-
|
57
|
-
|
71
|
+
describe "#blockdev?" do
|
72
|
+
context "when the file is a block device" do
|
73
|
+
it "returns true" do
|
74
|
+
fs.touch('/block-file')
|
75
|
+
file = fs.find('/block-file')
|
76
|
+
file.block_device = true
|
77
|
+
block_stat = File::Stat.new('/block-file')
|
78
|
+
expect(block_stat.blockdev?).to be_true
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context "when the file is not a block device" do
|
83
|
+
it "returns false" do
|
84
|
+
expect(file_stat.blockdev?).to be_false
|
85
|
+
end
|
58
86
|
end
|
59
87
|
end
|
60
88
|
|
61
|
-
describe
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
89
|
+
describe "#chardev?" do
|
90
|
+
context "when the file is a character device" do
|
91
|
+
it "returns true" do
|
92
|
+
fs.touch('/character-file')
|
93
|
+
file = fs.find('/character-file')
|
94
|
+
file.character_device = true
|
95
|
+
character_stat = File::Stat.new('/character-file')
|
96
|
+
expect(character_stat.chardev?).to be_true
|
97
|
+
end
|
67
98
|
end
|
68
99
|
|
69
|
-
|
70
|
-
|
100
|
+
context "when the file is not a character device" do
|
101
|
+
it "returns false" do
|
102
|
+
expect(file_stat.chardev?).to be_false
|
103
|
+
end
|
71
104
|
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe '#ctime' do
|
108
|
+
let(:time) { Time.now - 500000 }
|
72
109
|
|
73
|
-
it "returns
|
74
|
-
|
110
|
+
it "returns the access time of the entry" do
|
111
|
+
entry.ctime = time
|
112
|
+
expect(file_stat.ctime).to eq(time)
|
75
113
|
end
|
76
114
|
|
77
115
|
context "when the entry is a symlink" do
|
78
|
-
context "and the optional
|
79
|
-
it "returns
|
80
|
-
|
116
|
+
context "and the optional dereference argument is true" do
|
117
|
+
it "returns the access time of the last target of the link chain" do
|
118
|
+
entry.ctime = time
|
119
|
+
expect(dereferenced_link_stat.ctime).to eq(time)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
context "and the optional dereference argument is false" do
|
124
|
+
it "returns the access time of the symlink itself" do
|
125
|
+
entry.ctime = time
|
126
|
+
expect(link_stat.ctime).not_to eq(time)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
describe "#dev" do
|
133
|
+
it "returns an integer representing the device on which stat resides" do
|
134
|
+
expect(file_stat.dev).to be_a(Fixnum)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
describe '#directory?' do
|
139
|
+
context 'when dereference is true' do
|
140
|
+
context "when the entry is a directory" do
|
141
|
+
it "returns true" do
|
142
|
+
expect(dereferenced_dir_stat.directory?).to be_true
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
context "when the entry is not a directory" do
|
147
|
+
it "returns false" do
|
148
|
+
expect(dereferenced_file_stat.directory?).to be_false
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
context "when the entry is a symlink" do
|
153
|
+
context "and the last target of the link chain is a directory" do
|
154
|
+
it "returns true" do
|
155
|
+
expect(dereferenced_dir_link_stat.directory?).to be_true
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
context "and the last target of the link chain is not a directory" do
|
160
|
+
it "returns false" do
|
161
|
+
expect(dereferenced_link_stat.directory?).to be_false
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
context 'when dereference is false' do
|
168
|
+
context "when the entry is a directory" do
|
169
|
+
it "returns true" do
|
170
|
+
expect(dir_stat.directory?).to be_true
|
81
171
|
end
|
172
|
+
end
|
82
173
|
|
83
|
-
|
84
|
-
|
174
|
+
context "when the entry is not a directory" do
|
175
|
+
it "returns false" do
|
176
|
+
expect(file_stat.directory?).to be_false
|
85
177
|
end
|
86
178
|
end
|
87
179
|
|
88
|
-
context "
|
89
|
-
|
90
|
-
|
180
|
+
context "when the entry is a symlink" do
|
181
|
+
context "and the last target of the link chain is a directory" do
|
182
|
+
it "returns false" do
|
183
|
+
expect(dir_link_stat.directory?).to be_false
|
184
|
+
end
|
91
185
|
end
|
92
186
|
|
93
|
-
|
94
|
-
|
187
|
+
context "and the last target of the link chain is not a directory" do
|
188
|
+
it "returns false" do
|
189
|
+
expect(link_stat.directory?).to be_false
|
190
|
+
end
|
95
191
|
end
|
96
192
|
end
|
97
193
|
end
|
@@ -99,109 +195,657 @@ module MemFs
|
|
99
195
|
|
100
196
|
describe '#entry' do
|
101
197
|
it "returns the comcerned entry" do
|
102
|
-
entry
|
103
|
-
|
104
|
-
|
198
|
+
expect(file_stat.entry).to be_a(Fake::File)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
describe "#executable?" do
|
203
|
+
let(:access) { 0 }
|
204
|
+
let(:gid) { 0 }
|
205
|
+
let(:uid) { 0 }
|
206
|
+
|
207
|
+
before :each do
|
208
|
+
entry.mode = access
|
209
|
+
entry.uid = uid
|
210
|
+
entry.gid = gid
|
211
|
+
end
|
212
|
+
|
213
|
+
context "when the file is not executable by anyone" do
|
214
|
+
it "return false" do
|
215
|
+
expect(file_stat.executable?).to be_false
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
context "when the file is user executable" do
|
220
|
+
let(:access) { MemFs::Fake::Entry::UEXEC }
|
221
|
+
|
222
|
+
context "and the current user owns the file" do
|
223
|
+
let(:uid) { Process.euid }
|
224
|
+
|
225
|
+
it "returns true" do
|
226
|
+
expect(file_stat.executable?).to be_true
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
context "when the file is group executable" do
|
232
|
+
let(:access) { MemFs::Fake::Entry::GEXEC }
|
233
|
+
|
234
|
+
context "and the current user is part of the owner group" do
|
235
|
+
let(:gid) { Process.egid }
|
236
|
+
|
237
|
+
it "returns true" do
|
238
|
+
expect(file_stat.executable?).to be_true
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
context "when the file is executable by anyone" do
|
244
|
+
let(:access) { MemFs::Fake::Entry::OEXEC }
|
245
|
+
|
246
|
+
context "and the user has no specific right on it" do
|
247
|
+
it "returns true" do
|
248
|
+
expect(file_stat.executable?).to be_true
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
context "when the file does not exist" do
|
254
|
+
it "returns false" do
|
255
|
+
expect(file_stat.executable?).to be_false
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
describe "#executable_real?" do
|
261
|
+
let(:access) { 0 }
|
262
|
+
let(:gid) { 0 }
|
263
|
+
let(:uid) { 0 }
|
264
|
+
|
265
|
+
before :each do
|
266
|
+
entry.mode = access
|
267
|
+
entry.uid = uid
|
268
|
+
entry.gid = gid
|
269
|
+
end
|
270
|
+
|
271
|
+
context "when the file is not executable by anyone" do
|
272
|
+
it "return false" do
|
273
|
+
expect(file_stat.executable_real?).to be_false
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
context "when the file is user executable" do
|
278
|
+
let(:access) { MemFs::Fake::Entry::UEXEC }
|
279
|
+
|
280
|
+
context "and the current user owns the file" do
|
281
|
+
let(:uid) { Process.uid }
|
282
|
+
|
283
|
+
it "returns true" do
|
284
|
+
expect(file_stat.executable_real?).to be_true
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
context "when the file is group executable" do
|
290
|
+
let(:access) { MemFs::Fake::Entry::GEXEC }
|
291
|
+
|
292
|
+
context "and the current user is part of the owner group" do
|
293
|
+
let(:gid) { Process.gid }
|
294
|
+
|
295
|
+
it "returns true" do
|
296
|
+
expect(file_stat.executable_real?).to be_true
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
context "when the file is executable by anyone" do
|
302
|
+
let(:access) { MemFs::Fake::Entry::OEXEC }
|
303
|
+
|
304
|
+
context "and the user has no specific right on it" do
|
305
|
+
it "returns true" do
|
306
|
+
expect(file_stat.executable_real?).to be_true
|
307
|
+
end
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
context "when the file does not exist" do
|
312
|
+
it "returns false" do
|
313
|
+
expect(file_stat.executable_real?).to be_false
|
314
|
+
end
|
105
315
|
end
|
106
316
|
end
|
107
317
|
|
108
318
|
describe "#file?" do
|
109
|
-
|
110
|
-
|
111
|
-
|
319
|
+
context 'when dereference is true' do
|
320
|
+
context "when the entry is a regular file" do
|
321
|
+
it "returns true" do
|
322
|
+
expect(dereferenced_file_stat.file?).to be_true
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
context "when the entry is not a regular file" do
|
327
|
+
it "returns false" do
|
328
|
+
expect(dereferenced_dir_stat.file?).to be_false
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
context "when the entry is a symlink" do
|
333
|
+
context "and the last target of the link chain is a regular file" do
|
334
|
+
it "returns true" do
|
335
|
+
expect(dereferenced_link_stat.file?).to be_true
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
context "and the last target of the link chain is not a regular file" do
|
340
|
+
it "returns false" do
|
341
|
+
expect(dereferenced_dir_link_stat.file?).to be_false
|
342
|
+
end
|
343
|
+
end
|
344
|
+
end
|
112
345
|
end
|
113
346
|
|
114
|
-
|
115
|
-
|
116
|
-
|
347
|
+
context 'when dereference is false' do
|
348
|
+
context "when the entry is a regular file" do
|
349
|
+
it "returns true" do
|
350
|
+
expect(file_stat.file?).to be_true
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
context "when the entry is not a regular file" do
|
355
|
+
it "returns false" do
|
356
|
+
expect(dir_stat.file?).to be_false
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
context "when the entry is a symlink" do
|
361
|
+
context "and the last target of the link chain is a regular file" do
|
362
|
+
it "returns false" do
|
363
|
+
expect(link_stat.file?).to be_false
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
context "and the last target of the link chain is not a regular file" do
|
368
|
+
it "returns false" do
|
369
|
+
expect(dir_link_stat.file?).to be_false
|
370
|
+
end
|
371
|
+
end
|
372
|
+
end
|
373
|
+
end
|
374
|
+
end
|
375
|
+
|
376
|
+
describe "#ftype" do
|
377
|
+
context "when the entry is a regular file" do
|
378
|
+
it "returns 'file'" do
|
379
|
+
expect(file_stat.ftype).to eq('file')
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
context "when the entry is a directory" do
|
384
|
+
it "returns 'directory'" do
|
385
|
+
expect(dir_stat.ftype).to eq('directory')
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
context "when the entry is a block device" do
|
390
|
+
it "returns 'blockSpecial'" do
|
391
|
+
fs.touch('/block-file')
|
392
|
+
file = fs.find('/block-file')
|
393
|
+
file.block_device = true
|
394
|
+
block_stat = File::Stat.new('/block-file')
|
395
|
+
expect(block_stat.ftype).to eq('blockSpecial')
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
context "when the entry is a character device" do
|
400
|
+
it "returns 'characterSpecial'" do
|
401
|
+
fs.touch('/character-file')
|
402
|
+
file = fs.find('/character-file')
|
403
|
+
file.character_device = true
|
404
|
+
character_stat = File::Stat.new('/character-file')
|
405
|
+
expect(character_stat.ftype).to eq('characterSpecial')
|
406
|
+
end
|
117
407
|
end
|
118
408
|
|
119
409
|
context "when the entry is a symlink" do
|
120
|
-
it "returns
|
121
|
-
|
122
|
-
fs.symlink('/test-file', '/test-link')
|
123
|
-
expect(File.stat('/test-link')).to be_file
|
410
|
+
it "returns 'link'" do
|
411
|
+
expect(link_stat.ftype).to eq('link')
|
124
412
|
end
|
413
|
+
end
|
414
|
+
|
415
|
+
# fifo and socket not handled for now
|
125
416
|
|
126
|
-
|
127
|
-
|
128
|
-
fs.
|
129
|
-
|
417
|
+
context "when the entry has no specific type" do
|
418
|
+
it "returns 'unknown'" do
|
419
|
+
root = fs.find('/')
|
420
|
+
root.add_entry Fake::Entry.new('test-entry')
|
421
|
+
entry_stat = File::Stat.new('/test-entry')
|
422
|
+
expect(entry_stat.ftype).to eq('unknown')
|
130
423
|
end
|
131
424
|
end
|
132
425
|
end
|
133
426
|
|
134
427
|
describe "#gid" do
|
135
428
|
it "returns the group id of the named entry" do
|
136
|
-
fs.touch('/test-file')
|
137
429
|
fs.chown(nil, 42, '/test-file')
|
138
|
-
expect(
|
430
|
+
expect(file_stat.gid).to be(42)
|
431
|
+
end
|
432
|
+
end
|
433
|
+
|
434
|
+
describe "#grpowned?" do
|
435
|
+
context "when the effective user group owns of the file" do
|
436
|
+
it "returns true" do
|
437
|
+
fs.chown(0, Process.egid, '/test-file')
|
438
|
+
expect(file_stat.grpowned?).to be_true
|
439
|
+
end
|
440
|
+
end
|
441
|
+
|
442
|
+
context "when the effective user group does not own of the file" do
|
443
|
+
it "returns false" do
|
444
|
+
fs.chown(0, 0, '/test-file')
|
445
|
+
expect(file_stat.grpowned?).to be_false
|
446
|
+
end
|
139
447
|
end
|
140
448
|
end
|
141
449
|
|
142
450
|
describe "#ino" do
|
143
451
|
it "returns the inode number for stat." do
|
144
|
-
|
145
|
-
expect(File::Stat.new('/test-file').ino).to be_a(Fixnum)
|
452
|
+
expect(file_stat.ino).to be_a(Fixnum)
|
146
453
|
end
|
147
454
|
end
|
148
455
|
|
149
456
|
describe '#mode' do
|
150
457
|
it "returns an integer representing the permission bits of stat" do
|
151
|
-
fs.touch('/test-file')
|
152
458
|
fs.chmod(0777, '/test-file')
|
153
|
-
expect(
|
459
|
+
expect(file_stat.mode).to be(0100777)
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
463
|
+
describe "#owned?" do
|
464
|
+
context "when the effective user owns of the file" do
|
465
|
+
it "returns true" do
|
466
|
+
fs.chown(Process.euid, 0, '/test-file')
|
467
|
+
expect(file_stat.owned?).to be_true
|
468
|
+
end
|
469
|
+
end
|
470
|
+
|
471
|
+
context "when the effective user does not own of the file" do
|
472
|
+
it "returns false" do
|
473
|
+
fs.chown(0, 0, '/test-file')
|
474
|
+
expect(file_stat.owned?).to be_false
|
475
|
+
end
|
476
|
+
end
|
477
|
+
end
|
478
|
+
|
479
|
+
describe "#pipe?" do
|
480
|
+
# Pipes are not handled for now
|
481
|
+
|
482
|
+
context "when the file is not a pipe" do
|
483
|
+
it "returns false" do
|
484
|
+
expect(file_stat.pipe?).to be_false
|
485
|
+
end
|
486
|
+
end
|
487
|
+
end
|
488
|
+
|
489
|
+
describe "#readable?" do
|
490
|
+
let(:access) { 0 }
|
491
|
+
let(:gid) { 0 }
|
492
|
+
let(:uid) { 0 }
|
493
|
+
|
494
|
+
before :each do
|
495
|
+
entry.mode = access
|
496
|
+
entry.uid = uid
|
497
|
+
entry.gid = gid
|
498
|
+
end
|
499
|
+
|
500
|
+
context "when the file is not readable by anyone" do
|
501
|
+
it "return false" do
|
502
|
+
expect(file_stat.readable?).to be_false
|
503
|
+
end
|
504
|
+
end
|
505
|
+
|
506
|
+
context "when the file is user readable" do
|
507
|
+
let(:access) { MemFs::Fake::Entry::UREAD }
|
508
|
+
|
509
|
+
context "and the current user owns the file" do
|
510
|
+
let(:uid) { Process.euid }
|
511
|
+
|
512
|
+
it "returns true" do
|
513
|
+
expect(file_stat.readable?).to be_true
|
514
|
+
end
|
515
|
+
end
|
516
|
+
end
|
517
|
+
|
518
|
+
context "when the file is group readable" do
|
519
|
+
let(:access) { MemFs::Fake::Entry::GREAD }
|
520
|
+
|
521
|
+
context "and the current user is part of the owner group" do
|
522
|
+
let(:gid) { Process.egid }
|
523
|
+
|
524
|
+
it "returns true" do
|
525
|
+
expect(file_stat.readable?).to be_true
|
526
|
+
end
|
527
|
+
end
|
528
|
+
end
|
529
|
+
|
530
|
+
context "when the file is readable by anyone" do
|
531
|
+
let(:access) { MemFs::Fake::Entry::OREAD }
|
532
|
+
|
533
|
+
context "and the user has no specific right on it" do
|
534
|
+
it "returns true" do
|
535
|
+
expect(file_stat.readable?).to be_true
|
536
|
+
end
|
537
|
+
end
|
538
|
+
end
|
539
|
+
end
|
540
|
+
|
541
|
+
describe "#readable_real?" do
|
542
|
+
let(:access) { 0 }
|
543
|
+
let(:gid) { 0 }
|
544
|
+
let(:uid) { 0 }
|
545
|
+
|
546
|
+
before :each do
|
547
|
+
entry.mode = access
|
548
|
+
entry.uid = uid
|
549
|
+
entry.gid = gid
|
550
|
+
end
|
551
|
+
|
552
|
+
context "when the file is not readable by anyone" do
|
553
|
+
it "return false" do
|
554
|
+
expect(file_stat.readable_real?).to be_false
|
555
|
+
end
|
556
|
+
end
|
557
|
+
|
558
|
+
context "when the file is user readable" do
|
559
|
+
let(:access) { MemFs::Fake::Entry::UREAD }
|
560
|
+
|
561
|
+
context "and the current user owns the file" do
|
562
|
+
let(:uid) { Process.euid }
|
563
|
+
|
564
|
+
it "returns true" do
|
565
|
+
expect(file_stat.readable_real?).to be_true
|
566
|
+
end
|
567
|
+
end
|
568
|
+
end
|
569
|
+
|
570
|
+
context "when the file is group readable" do
|
571
|
+
let(:access) { MemFs::Fake::Entry::GREAD }
|
572
|
+
|
573
|
+
context "and the current user is part of the owner group" do
|
574
|
+
let(:gid) { Process.egid }
|
575
|
+
|
576
|
+
it "returns true" do
|
577
|
+
expect(file_stat.readable_real?).to be_true
|
578
|
+
end
|
579
|
+
end
|
580
|
+
end
|
581
|
+
|
582
|
+
context "when the file is readable by anyone" do
|
583
|
+
let(:access) { MemFs::Fake::Entry::OREAD }
|
584
|
+
|
585
|
+
context "and the user has no specific right on it" do
|
586
|
+
it "returns true" do
|
587
|
+
expect(file_stat.readable_real?).to be_true
|
588
|
+
end
|
589
|
+
end
|
590
|
+
end
|
591
|
+
|
592
|
+
context "when the file does not exist" do
|
593
|
+
it "returns false" do
|
594
|
+
expect(file_stat.readable_real?).to be_false
|
595
|
+
end
|
596
|
+
end
|
597
|
+
end
|
598
|
+
|
599
|
+
describe "#setgid?" do
|
600
|
+
context "when the file has the setgid bit set" do
|
601
|
+
it "returns true" do
|
602
|
+
fs.chmod(02000, '/test-file')
|
603
|
+
expect(file_stat.setgid?).to be_true
|
604
|
+
end
|
605
|
+
end
|
606
|
+
|
607
|
+
context "when the file does not have the setgid bit set" do
|
608
|
+
it "returns false" do
|
609
|
+
expect(file_stat.setgid?).to be_false
|
610
|
+
end
|
611
|
+
end
|
612
|
+
end
|
613
|
+
|
614
|
+
describe "#setuid?" do
|
615
|
+
context "when the file has the setuid bit set" do
|
616
|
+
it "returns true" do
|
617
|
+
fs.chmod(04000, '/test-file')
|
618
|
+
expect(file_stat.setuid?).to be_true
|
619
|
+
end
|
620
|
+
end
|
621
|
+
|
622
|
+
context "when the file does not have the setuid bit set" do
|
623
|
+
it "returns false" do
|
624
|
+
expect(file_stat.setuid?).to be_false
|
625
|
+
end
|
626
|
+
end
|
627
|
+
end
|
628
|
+
|
629
|
+
describe "#socket?" do
|
630
|
+
# Sockets are not handled for now
|
631
|
+
|
632
|
+
context "when the file is not a socket" do
|
633
|
+
it "returns false" do
|
634
|
+
expect(file_stat.socket?).to be_false
|
635
|
+
end
|
154
636
|
end
|
155
637
|
end
|
156
638
|
|
157
639
|
describe "#sticky?" do
|
158
640
|
it "returns true if the named file has the sticky bit set" do
|
159
|
-
fs.touch('/test-file')
|
160
641
|
fs.chmod(01777, '/test-file')
|
161
|
-
expect(
|
642
|
+
expect(file_stat.sticky?).to be_true
|
162
643
|
end
|
163
644
|
|
164
645
|
it "returns false if the named file hasn't' the sticky bit set" do
|
165
|
-
|
166
|
-
expect(File::Stat.new('/test-file')).not_to be_sticky
|
646
|
+
expect(file_stat.sticky?).to be_false
|
167
647
|
end
|
168
648
|
end
|
169
649
|
|
170
650
|
describe '#symlink?' do
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
651
|
+
context 'when dereference is true' do
|
652
|
+
context "when the entry is a symlink" do
|
653
|
+
it "returns false" do
|
654
|
+
expect(dereferenced_link_stat.symlink?).to be_false
|
655
|
+
end
|
656
|
+
end
|
657
|
+
|
658
|
+
context "when the entry is not a symlink" do
|
659
|
+
it "returns false" do
|
660
|
+
expect(dereferenced_file_stat.symlink?).to be_false
|
661
|
+
end
|
662
|
+
end
|
175
663
|
end
|
176
664
|
|
177
|
-
|
178
|
-
|
179
|
-
|
665
|
+
context 'when dereference is false' do
|
666
|
+
context "when the entry is a symlink" do
|
667
|
+
it "returns true" do
|
668
|
+
expect(link_stat.symlink?).to be_true
|
669
|
+
end
|
670
|
+
end
|
671
|
+
|
672
|
+
context "when the entry is not a symlink" do
|
673
|
+
it "returns false" do
|
674
|
+
expect(file_stat.symlink?).to be_false
|
675
|
+
end
|
676
|
+
end
|
180
677
|
end
|
181
678
|
end
|
182
679
|
|
183
680
|
describe "#uid" do
|
184
681
|
it "returns the user id of the named entry" do
|
185
|
-
fs.touch('/test-file')
|
186
682
|
fs.chown(42, nil, '/test-file')
|
187
|
-
expect(
|
683
|
+
expect(file_stat.uid).to be(42)
|
684
|
+
end
|
685
|
+
end
|
686
|
+
|
687
|
+
describe "#world_reable?" do
|
688
|
+
context "when +file_name+ is readable by others" do
|
689
|
+
it "returns an integer representing the file permission bits of +file_name+" do
|
690
|
+
fs.chmod(MemFs::Fake::Entry::OREAD, '/test-file')
|
691
|
+
expect(file_stat.world_readable?).to eq(MemFs::Fake::Entry::OREAD)
|
692
|
+
end
|
693
|
+
end
|
694
|
+
|
695
|
+
context "when +file_name+ is not readable by others" do
|
696
|
+
it "returns nil" do
|
697
|
+
fs.chmod(MemFs::Fake::Entry::UREAD, '/test-file')
|
698
|
+
expect(file_stat.world_readable?).to be_nil
|
699
|
+
end
|
188
700
|
end
|
189
701
|
end
|
190
702
|
|
191
703
|
describe "#world_writable?" do
|
192
704
|
context "when +file_name+ is writable by others" do
|
193
705
|
it "returns an integer representing the file permission bits of +file_name+" do
|
194
|
-
fs.
|
195
|
-
|
196
|
-
expect(File::Stat.new('/test-file')).to be_world_writable
|
706
|
+
fs.chmod(MemFs::Fake::Entry::OWRITE, '/test-file')
|
707
|
+
expect(file_stat.world_writable?).to eq(MemFs::Fake::Entry::OWRITE)
|
197
708
|
end
|
198
709
|
end
|
199
710
|
|
200
711
|
context "when +file_name+ is not writable by others" do
|
201
712
|
it "returns nil" do
|
202
|
-
fs.
|
203
|
-
|
204
|
-
|
713
|
+
fs.chmod(MemFs::Fake::Entry::UWRITE, '/test-file')
|
714
|
+
expect(file_stat.world_writable?).to be_nil
|
715
|
+
end
|
716
|
+
end
|
717
|
+
end
|
718
|
+
|
719
|
+
describe "#writable?" do
|
720
|
+
let(:access) { 0 }
|
721
|
+
let(:gid) { 0 }
|
722
|
+
let(:uid) { 0 }
|
723
|
+
|
724
|
+
before :each do
|
725
|
+
entry.mode = access
|
726
|
+
entry.uid = uid
|
727
|
+
entry.gid = gid
|
728
|
+
end
|
729
|
+
|
730
|
+
context "when the file is not executable by anyone" do
|
731
|
+
it "return false" do
|
732
|
+
expect(file_stat.writable?).to be_false
|
733
|
+
end
|
734
|
+
end
|
735
|
+
|
736
|
+
context "when the file is user executable" do
|
737
|
+
let(:access) { MemFs::Fake::Entry::UWRITE }
|
738
|
+
|
739
|
+
context "and the current user owns the file" do
|
740
|
+
let(:uid) { Process.euid }
|
741
|
+
|
742
|
+
it "returns true" do
|
743
|
+
expect(file_stat.writable?).to be_true
|
744
|
+
end
|
745
|
+
end
|
746
|
+
end
|
747
|
+
|
748
|
+
context "when the file is group executable" do
|
749
|
+
let(:access) { MemFs::Fake::Entry::GWRITE }
|
750
|
+
|
751
|
+
context "and the current user is part of the owner group" do
|
752
|
+
let(:gid) { Process.egid }
|
753
|
+
|
754
|
+
it "returns true" do
|
755
|
+
expect(file_stat.writable?).to be_true
|
756
|
+
end
|
757
|
+
end
|
758
|
+
end
|
759
|
+
|
760
|
+
context "when the file is executable by anyone" do
|
761
|
+
let(:access) { MemFs::Fake::Entry::OWRITE }
|
762
|
+
|
763
|
+
context "and the user has no specific right on it" do
|
764
|
+
it "returns true" do
|
765
|
+
expect(file_stat.writable?).to be_true
|
766
|
+
end
|
767
|
+
end
|
768
|
+
end
|
769
|
+
|
770
|
+
context "when the file does not exist" do
|
771
|
+
it "returns false" do
|
772
|
+
expect(file_stat.writable?).to be_false
|
773
|
+
end
|
774
|
+
end
|
775
|
+
end
|
776
|
+
|
777
|
+
describe "#writable_real?" do
|
778
|
+
let(:access) { 0 }
|
779
|
+
let(:gid) { 0 }
|
780
|
+
let(:uid) { 0 }
|
781
|
+
|
782
|
+
before :each do
|
783
|
+
entry.mode = access
|
784
|
+
entry.uid = uid
|
785
|
+
entry.gid = gid
|
786
|
+
end
|
787
|
+
|
788
|
+
context "when the file is not executable by anyone" do
|
789
|
+
it "return false" do
|
790
|
+
expect(file_stat.writable_real?).to be_false
|
791
|
+
end
|
792
|
+
end
|
793
|
+
|
794
|
+
context "when the file is user executable" do
|
795
|
+
let(:access) { MemFs::Fake::Entry::UWRITE }
|
796
|
+
|
797
|
+
context "and the current user owns the file" do
|
798
|
+
let(:uid) { Process.euid }
|
799
|
+
|
800
|
+
it "returns true" do
|
801
|
+
expect(file_stat.writable_real?).to be_true
|
802
|
+
end
|
803
|
+
end
|
804
|
+
end
|
805
|
+
|
806
|
+
context "when the file is group executable" do
|
807
|
+
let(:access) { MemFs::Fake::Entry::GWRITE }
|
808
|
+
|
809
|
+
context "and the current user is part of the owner group" do
|
810
|
+
let(:gid) { Process.egid }
|
811
|
+
|
812
|
+
it "returns true" do
|
813
|
+
expect(file_stat.writable_real?).to be_true
|
814
|
+
end
|
815
|
+
end
|
816
|
+
end
|
817
|
+
|
818
|
+
context "when the file is executable by anyone" do
|
819
|
+
let(:access) { MemFs::Fake::Entry::OWRITE }
|
820
|
+
|
821
|
+
context "and the user has no specific right on it" do
|
822
|
+
it "returns true" do
|
823
|
+
expect(file_stat.writable_real?).to be_true
|
824
|
+
end
|
825
|
+
end
|
826
|
+
end
|
827
|
+
|
828
|
+
context "when the file does not exist" do
|
829
|
+
it "returns false" do
|
830
|
+
expect(file_stat.writable_real?).to be_false
|
831
|
+
end
|
832
|
+
end
|
833
|
+
end
|
834
|
+
|
835
|
+
describe "#zero?" do
|
836
|
+
context "when the file has a zero size" do
|
837
|
+
it "returns true" do
|
838
|
+
expect(file_stat.zero?).to be_true
|
839
|
+
end
|
840
|
+
end
|
841
|
+
|
842
|
+
context "when the file does not have a zero size" do
|
843
|
+
before :each do
|
844
|
+
fs.find!('/test-file').content << 'test'
|
845
|
+
end
|
846
|
+
|
847
|
+
it "returns false" do
|
848
|
+
expect(file_stat.zero?).to be_false
|
205
849
|
end
|
206
850
|
end
|
207
851
|
end
|