rubypath 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.
@@ -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