memfs 0.0.1

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