rubypath 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,385 @@
1
+ require 'spec_helper'
2
+
3
+ describe Path do
4
+ describe 'File Operations' do
5
+ with_backends :mock, :sys do
6
+ let(:path) { Path('/path/to/file.txt') }
7
+
8
+ describe_method :name, aliases: [:basename] do
9
+ subject { path.send described_method }
10
+
11
+ it 'should return file name' do
12
+ should eq 'file.txt'
13
+ end
14
+ end
15
+
16
+ describe_method :touch do
17
+ let(:path) { Path '/rubypath' }
18
+ let(:args) { Array.new }
19
+ let(:expected_path) { path }
20
+ subject { path.touch(*args) }
21
+ before { expect(path).to_not be_existent }
22
+
23
+ shared_examples '#touch' do
24
+ it 'should create file' do
25
+ subject
26
+ expect(expected_path).to be_existent
27
+ end
28
+
29
+ it 'should update modification time' do
30
+ subject
31
+ expect(expected_path.mtime).to be_within(1).of(Time.now)
32
+ end
33
+ end
34
+
35
+ shared_examples '#touch with existing file' do
36
+ before do
37
+ expected_path.write 'ABC'
38
+ expected_path.mtime = Time.now - 3600
39
+ end
40
+ before { expect(expected_path.mtime).to be < (Time.now - 30) }
41
+
42
+ it_behaves_like '#touch'
43
+
44
+ it 'should not change content' do
45
+ subject
46
+ expect(expected_path.read).to eq 'ABC'
47
+ end
48
+ end
49
+
50
+ it_behaves_like '#touch with existing file'
51
+
52
+ context 'with args' do
53
+ let(:args) { '../file' }
54
+ let(:expected_path) { Path '/file' }
55
+
56
+ it_behaves_like '#touch with existing file'
57
+ end
58
+
59
+ context 'with existing file in path' do
60
+ let(:path) { super().join('file') }
61
+ before { path.parent.write 'ABC' }
62
+
63
+ it 'should raise ENOTDIR error' do
64
+ expect{ subject }.to raise_error(
65
+ Errno::ENOTDIR, 'Not a directory - /rubypath/file')
66
+ end
67
+ end
68
+
69
+ context 'with existing directory' do
70
+ before do
71
+ path.mkdir
72
+ path.mtime = Time.now - 3600
73
+ end
74
+ before { expect(path).to be_directory }
75
+ before { expect(path.mtime).to be < (Time.now - 30) }
76
+
77
+ it 'should should update modification time' do
78
+ subject
79
+ expect(path.mtime).to be_within(1).of(Time.now)
80
+ end
81
+ end
82
+
83
+ context 'with file in non-existent directory' do
84
+ let(:path) { Path '/dir/file' }
85
+
86
+ it 'should raise ENOENT error' do
87
+ expect{ subject }.to raise_error(
88
+ Errno::ENOENT, 'No such file or directory - /dir/file')
89
+ end
90
+ end
91
+ end
92
+
93
+ describe_method :mkfile do
94
+ let(:path) { Path '/path/to/file.txt' }
95
+ let(:args) { Array.new }
96
+ let(:expected_path) { path.dup }
97
+ subject { path.send described_method, *args }
98
+
99
+ shared_examples '#mkfile' do
100
+ it 'should create all missing directories' do
101
+ expect{ subject }.to change{ expected_path.parent.directory? }
102
+ .from(false).to(true)
103
+ end
104
+
105
+ it 'should create file' do
106
+ expect{ subject }.to change{ expected_path.file? }
107
+ .from(false).to(true)
108
+ end
109
+ end
110
+
111
+ it_behaves_like '#mkfile'
112
+
113
+ context 'with args' do
114
+ let(:args) { %w(sub file) }
115
+ let(:expected_path) { Path '/path/to/file.txt/sub/file' }
116
+
117
+ it_behaves_like '#mkfile'
118
+ end
119
+
120
+ context 'with existing directory' do
121
+ before { path.mkpath }
122
+
123
+ it 'should raise ENOENT error' do
124
+ expect{ subject }.to raise_error(
125
+ Errno::ENOENT, 'No such file or directory - /path/to/file.txt')
126
+ end
127
+ end
128
+
129
+ context 'with existing file in path' do
130
+ before do
131
+ path.parent.parent.mkpath
132
+ path.parent.touch
133
+ end
134
+
135
+ it 'should raise EISDIR error' do
136
+ expect{ subject }.to raise_error(
137
+ Errno::ENOTDIR, 'Not a directory - /path/to/file.txt')
138
+ end
139
+ end
140
+
141
+ context 'with absolute root dir as path' do
142
+ let(:path) { Path '/' }
143
+
144
+ it 'should raise EISDIR error' do
145
+ expect{ subject }.to raise_error(
146
+ Errno::ENOENT, 'No such file or directory - /')
147
+ end
148
+ end
149
+ end
150
+
151
+ describe_method :lookup do
152
+ let(:path) { Path('~') }
153
+ before do
154
+ Path.mock do |r|
155
+ path.mkpath 'a/b/c/d'
156
+ path.touch 'a/test.txt'
157
+ path.touch 'a/b/c/config.yaml'
158
+ path.touch 'a/b/.config.yml'
159
+ path.touch 'a/config.yml'
160
+ end
161
+ end
162
+ before { expect(path.join('a/b/c/d')).to be_directory }
163
+
164
+ context 'with filename' do
165
+ it 'should find file in current directory' do
166
+ expect(path.join('a').send(described_method, 'test.txt'))
167
+ .to eq path.join('a/test.txt').expand
168
+ end
169
+
170
+ it 'should find file in parent directory' do
171
+ expect(path.join(%w(a b)).send(described_method, 'test.txt'))
172
+ .to eq path.join('a/test.txt').expand
173
+ end
174
+
175
+ it 'should find file in ancestor directory' do
176
+ expect(path.join('a/b/c/d').send(described_method, 'test.txt'))
177
+ .to eq path.join('a/test.txt').expand
178
+ end
179
+
180
+ it 'should find first file in ancestor directory' do
181
+ expect(path.join('a/b/c/d').send(described_method, 'config.yaml'))
182
+ .to eq path.join('a/b/c/config.yaml').expand
183
+ end
184
+ end
185
+
186
+ context 'with glob' do
187
+ it 'should find file in current directory' do
188
+ expect(path.join('a').send(described_method, 'test.*'))
189
+ .to eq path.join('a/test.txt').expand
190
+ end
191
+
192
+ it 'should find file in parent directory' do
193
+ expect(path.join('a/b').send(described_method, 'test.*'))
194
+ .to eq path.join('a/test.txt').expand
195
+ end
196
+
197
+ it 'should find file in ancestor directory' do
198
+ expect(path.join('a/b/c/d').send(described_method, 'test.*'))
199
+ .to eq path.join('a/test.txt').expand
200
+ end
201
+
202
+ it 'should find first file in ancestor directory' do
203
+ expect(path.join('a/b/c/d').send(described_method, 'config.*'))
204
+ .to eq path.join('a/b/c/config.yaml').expand
205
+ end
206
+
207
+ it 'should find first file that match (I)' do
208
+ expect(path.join('a/b').send(described_method, '*.yml'))
209
+ .to eq path.join('a/config.yml').expand
210
+ end
211
+
212
+ it 'should find first file that match (II)' do
213
+ expect(path.join('a/b')
214
+ .send(described_method, 'config.{yml,yaml}'))
215
+ .to eq path.join('a/config.yml').expand
216
+ end
217
+
218
+ it 'should find first file that dotmatch' do
219
+ expect(path.join('a/b')
220
+ .send(described_method, '*.yml', ::File::FNM_DOTMATCH))
221
+ .to eq path.join('a/b/.config.yml').expand
222
+ end
223
+ end
224
+
225
+ context 'with regexp' do
226
+ it 'should find file in current directory' do
227
+ expect(path.join('a').send(described_method, /^test\.txt$/))
228
+ .to eq path.join('a/test.txt').expand
229
+ end
230
+
231
+ it 'should find file in parent directory' do
232
+ expect(path.join('a/b').send(described_method, /^test\.txt$/))
233
+ .to eq path.join('a/test.txt').expand
234
+ end
235
+
236
+ it 'should find file in ancestor directory' do
237
+ expect(path.join('a/b/c/d').send(described_method, /^test\.txt$/))
238
+ .to eq path.join('a/test.txt').expand
239
+ end
240
+
241
+ it 'should find first file in ancestor directory' do
242
+ expect(path.join('a/b/c/d')
243
+ .send(described_method, /^config\.yaml$/))
244
+ .to eq path.join('a/b/c/config.yaml').expand
245
+ end
246
+
247
+ it 'should find first file that match' do
248
+ expect(path.join('a/b').send(described_method, /^config\.ya?ml$/))
249
+ .to eq path.join('a/config.yml').expand
250
+ end
251
+ end
252
+ end
253
+
254
+ describe_method :mtime do
255
+ let(:path) { Path '/file.txt' }
256
+ before { path.touch }
257
+ subject { path.send described_method }
258
+
259
+ it 'should return file modification time' do
260
+ should be_within(0.1).of(Time.now)
261
+ end
262
+
263
+ context 'with modification time changed' do
264
+ before { path.mtime = Time.new(2175, 12, 24, 18, 00, 30) }
265
+
266
+ it 'should return file modification time' do
267
+ should eq Time.new(2175, 12, 24, 18, 00, 30)
268
+ end
269
+ end
270
+ end
271
+
272
+ describe_method :mtime= do
273
+ let(:path) { Path '/file.txt' }
274
+ before { path.touch }
275
+ subject { path.send described_method, Time.new(2175, 12, 24, 18, 00, 30) }
276
+
277
+ it 'should change file modification time' do
278
+ expect{ subject }.to change{ path.mtime }.to Time.new(2175, 12, 24, 18, 00, 30)
279
+ end
280
+ end
281
+
282
+ describe_method :atime do
283
+ let(:path) { Path '/file.txt' }
284
+ subject { path.send described_method }
285
+
286
+ context 'new create file' do
287
+ before { path.touch }
288
+
289
+ it { should be_within(0.1).of(Time.now) }
290
+ end
291
+
292
+ context 'with existing file' do
293
+ before { path.touch }
294
+
295
+ context 'older file' do
296
+ before { path.touch }
297
+ before { sleep 0.3 }
298
+
299
+ it { should be_within(0.1).of(Time.now - 0.3) }
300
+ end
301
+
302
+ context 'and changed access time' do
303
+ before { path.atime = Time.new(2175, 12, 24, 18, 00, 30) }
304
+
305
+ it 'should return file access time' do
306
+ should eq Time.new(2175, 12, 24, 18, 00, 30)
307
+ end
308
+ end
309
+ end
310
+ end
311
+
312
+ describe_method :atime= do
313
+ let(:path) { Path '/file.txt' }
314
+ before { path.touch }
315
+ subject { path.send described_method, Time.new(2175, 12, 24, 18, 00, 30) }
316
+
317
+ it 'should change file access time' do
318
+ expect{ subject }.to change{ path.atime }.to Time.new(2175, 12, 24, 18, 00, 30)
319
+ end
320
+ end
321
+
322
+ describe_method :mode do
323
+ let(:path) { Path '/file' }
324
+ subject { path.send described_method }
325
+
326
+ context 'with file' do
327
+ before { path.touch }
328
+ it { should eq 0666 - Path.umask }
329
+ end
330
+
331
+ context 'with directory' do
332
+ before { path.mkpath }
333
+ it { should eq 0777 - Path.umask }
334
+ end
335
+ end
336
+ end
337
+
338
+ describe 'umask' do
339
+ shared_examples 'umask setter' do
340
+ with_backend :sys do
341
+ it 'should set umask' do
342
+ subject
343
+ expect(File.umask).to eq 0077
344
+ end
345
+ end
346
+
347
+ with_backend :mock do
348
+ it 'should set umask' do
349
+ expect{ subject }.to change{ Path.umask }.from(0022).to(0077)
350
+ end
351
+ end
352
+ end
353
+
354
+ describe_method :umask do
355
+ let(:args) { Array.new }
356
+ subject { Path.send described_method, *args }
357
+
358
+ context 'as getter' do
359
+ with_backend :sys do
360
+ it 'should return umask' do
361
+ should eq File.umask
362
+ end
363
+ end
364
+
365
+ with_backend :mock do
366
+ it 'should return umask' do
367
+ should eq 0022
368
+ end
369
+ end
370
+ end
371
+
372
+ context 'as setter' do
373
+ let(:args) { [0077] }
374
+ it_behaves_like 'umask setter'
375
+ end
376
+ end
377
+
378
+ describe_method :umask= do
379
+ let(:args) { [0077] }
380
+ subject { Path.send described_method, *args }
381
+ it_behaves_like 'umask setter'
382
+ end
383
+ end
384
+ end
385
+ end
@@ -0,0 +1,66 @@
1
+ require 'spec_helper'
2
+
3
+ describe Path do
4
+ describe 'File Predicates' do
5
+ with_backends :mock, :sys do
6
+ describe_method :file? do
7
+ let(:path) { Path '/file.txt' }
8
+ before { expect(path).to_not be_existent }
9
+ subject { path.send(described_method) }
10
+
11
+ context 'with existing file' do
12
+ before { path.touch }
13
+ it { should eq true }
14
+ end
15
+
16
+ context 'with existing but wrong node (dir)' do
17
+ before { path.mkdir }
18
+ it { should eq false }
19
+ end
20
+
21
+ context 'with not existent file' do
22
+ it { should eq false }
23
+ end
24
+ end
25
+
26
+ describe_method :directory? do
27
+ let(:dir) { Path '/dir' }
28
+ before { expect(dir).to_not be_existent }
29
+ subject { dir.send(described_method) }
30
+
31
+ context 'with existing directory' do
32
+ before { dir.mkdir }
33
+ it { should eq true }
34
+ end
35
+
36
+ context 'with existing but wrong node (file)' do
37
+ before { dir.touch }
38
+ it { should eq false }
39
+ end
40
+
41
+ context 'with not existent directory' do
42
+ it { should eq false }
43
+ end
44
+ end
45
+
46
+ describe_method :exists?, aliases: [:exist?, :existent?] do
47
+ let(:path) { Path '/file' }
48
+ subject { path.send described_method }
49
+
50
+ context 'with existing directory' do
51
+ before { path.mkdir }
52
+ it { should eq true }
53
+ end
54
+
55
+ context 'with existing file' do
56
+ before { path.touch }
57
+ it { should eq true }
58
+ end
59
+
60
+ context 'with non-existing node' do
61
+ it { should eq false }
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe Path do
4
+ describe 'Identity' do
5
+ let(:str) { '/path/to/file' }
6
+ let(:args) { [str] }
7
+ let(:path) { described_class.new *args }
8
+ subject { path }
9
+
10
+ describe_method :path, aliases: [:to_path, :to_str, :to_s] do
11
+ subject { path.send described_method }
12
+
13
+ it { should eq str }
14
+
15
+ # Should not return same object as internal variable
16
+ # to avoid in-place modifications like
17
+ # `Path.new('/abc').path.delete!('abc')`
18
+ it { should_not equal path.send(:instance_variable_get, :@path) }
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,126 @@
1
+ require 'spec_helper'
2
+
3
+ describe Path do
4
+ describe 'IO Operations' do
5
+ with_backends :mock, :sys do
6
+
7
+ describe_method :read do
8
+ let(:path) { Path '/file' }
9
+ let(:args) { Array.new }
10
+ subject { path.send described_method, *args }
11
+
12
+ context 'with existing file' do
13
+ before { path.write 'CONTENT' }
14
+ before { path.mtime = Time.new(1970) }
15
+ before { path.atime = Time.new(1970) }
16
+
17
+ it { should eq 'CONTENT' }
18
+
19
+ it 'should update access time' do
20
+ subject
21
+ expect(path.atime).to be_within(0.1).of(Time.now)
22
+ end
23
+
24
+ context 'with read length and offset' do
25
+ let(:args) { [4, 2] }
26
+
27
+ it { should eq 'NTEN' }
28
+
29
+ context 'with oversized length' do
30
+ let(:args) { [10, 2] }
31
+ it { should eq 'NTENT' }
32
+ end
33
+
34
+ context 'with oversized offset' do
35
+ let(:args) { [10, 10] }
36
+ it { should eq nil }
37
+ end
38
+ end
39
+ end
40
+
41
+ context 'with existing directory' do
42
+ before { path.mkdir }
43
+
44
+ it 'should raise EISDIR error' do
45
+ expect { subject }.to raise_error(Errno::EISDIR, "Is a directory - /file")
46
+ end
47
+ end
48
+
49
+ context 'with non-existent file' do
50
+ before { expect(path).to_not be_existent }
51
+
52
+ it 'should raise ENOENT error' do
53
+ expect { subject }.to raise_error(Errno::ENOENT, "No such file or directory - /file")
54
+ end
55
+ end
56
+ end
57
+
58
+ describe_method :write do
59
+ let(:path) { Path '/file' }
60
+ let(:args) { Array.new }
61
+ subject { path.send described_method, 'CONTENT', *args }
62
+
63
+ shared_examples '#write' do
64
+ it 'should write content' do
65
+ subject
66
+ expect(path.read).to eq expected_content
67
+ end
68
+
69
+ it { should be_a Path }
70
+ it { expect(subject.path).to eq path.path}
71
+ end
72
+
73
+ context 'with existing file' do
74
+ before { path.touch }
75
+ before { path.mtime = Time.new(1970) }
76
+ before { path.atime = Time.new(1970) }
77
+ let(:expected_content) { 'CONTENT' }
78
+
79
+ it_behaves_like '#write'
80
+
81
+ it 'should update mtime' do
82
+ expect{ subject }.to change{ path.mtime }
83
+ expect(path.mtime).to be_within(0.1).of(Time.now)
84
+ end
85
+
86
+ it 'should not update atime' do
87
+ expect{ subject }.to_not change{ path.atime }
88
+ end
89
+
90
+ context 'with offset' do
91
+ before { path.write '12345678901234567890' }
92
+ let(:args) { [4] }
93
+ let(:expected_content) { '1234CONTENT234567890' }
94
+
95
+ it_behaves_like '#write'
96
+ end
97
+ end
98
+
99
+ context 'with existing directory' do
100
+ before { path.mkdir }
101
+
102
+ it 'should write content' do
103
+ expect{ subject }.to raise_error(Errno::EISDIR, "Is a directory - /file")
104
+ end
105
+ end
106
+
107
+ context 'with non-existing file' do
108
+ before { expect(path).to_not be_existent }
109
+ let(:expected_content) { 'CONTENT' }
110
+
111
+ it_behaves_like '#write'
112
+
113
+ it 'should set mtime' do
114
+ subject
115
+ expect(path.mtime).to be_within(0.1).of(Time.now)
116
+ end
117
+
118
+ it 'should set atime' do
119
+ subject
120
+ expect(path.atime).to be_within(0.1).of(Time.now)
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end