memfs 0.5.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,10 @@
1
+ # encoding: UTF-8
2
+
1
3
  require 'spec_helper'
2
4
  require 'pathname'
3
5
 
4
6
  module MemFs
5
- describe File do
7
+ ::RSpec.describe File do
6
8
  subject { described_class.new('/test-file') }
7
9
  let(:write_subject) { described_class.new('/test-file', 'w') }
8
10
 
@@ -15,7 +17,6 @@ module MemFs
15
17
  described_class.symlink '/no-file', '/no-link'
16
18
  end
17
19
 
18
-
19
20
  it 'implements Enumerable' do
20
21
  expect(described_class.ancestors).to include Enumerable
21
22
  end
@@ -25,6 +26,10 @@ module MemFs
25
26
  expect(MemFs::File::SEPARATOR).to eq '/'
26
27
  end
27
28
 
29
+ it 'exposes PATH_SEPARATOR' do
30
+ expect(MemFs::File::PATH_SEPARATOR).to eq '/'
31
+ end
32
+
28
33
  it 'expose ALT_SEPARATOR' do
29
34
  expect(MemFs::File::ALT_SEPARATOR).to be_nil
30
35
  end
@@ -35,20 +40,20 @@ module MemFs
35
40
 
36
41
  it 'converts a pathname to an absolute pathname' do
37
42
  path = described_class.absolute_path('./test-file')
38
- expect(path).to eq '/test-dir/test-file'
43
+ expect(path).to eq expected_path('/test-dir/test-file')
39
44
  end
40
45
 
41
46
  context 'when +dir_string+ is given' do
42
47
  it 'uses it as the starting point' do
43
48
  path = described_class.absolute_path('./test-file', '/no-dir')
44
- expect(path).to eq '/no-dir/test-file'
49
+ expect(path).to eq expected_path('/no-dir/test-file')
45
50
  end
46
51
  end
47
52
 
48
53
  context "when the given pathname starts with a '~'" do
49
54
  it 'does not expanded' do
50
55
  path = described_class.absolute_path('~/test-file')
51
- expect(path).to eq '/test-dir/~/test-file'
56
+ expect(path).to eq expected_path('/test-dir/~/test-file')
52
57
  end
53
58
  end
54
59
  end
@@ -74,6 +79,27 @@ module MemFs
74
79
  end
75
80
  end
76
81
 
82
+ describe '.birthtime' do
83
+ it 'returns creation time for the named file as a Time object' do
84
+ expect(described_class.birthtime('/test-file')).to be_a Time
85
+ end
86
+
87
+ it 'raises an error if the entry does not exist' do
88
+ expect { described_class.birthtime('/no-file') }.to raise_error Errno::ENOENT
89
+ end
90
+
91
+ context 'when the entry is a symlink' do
92
+ let(:time) { Time.now - 500_000 }
93
+
94
+ it 'returns the creation time of the last target of the link chain' do
95
+ _fs.find!('/test-file').birthtime = time
96
+ described_class.symlink '/test-link', '/test-link2'
97
+
98
+ expect(described_class.birthtime('/test-link2')).to eq time
99
+ end
100
+ end
101
+ end
102
+
77
103
  describe '.blockdev?' do
78
104
  context 'when the name file exists' do
79
105
  context 'and it is a block device' do
@@ -150,17 +176,17 @@ module MemFs
150
176
 
151
177
  describe '.chmod' do
152
178
  it 'changes permission bits on the named file' do
153
- described_class.chmod 0777, '/test-file'
179
+ described_class.chmod 0o777, '/test-file'
154
180
 
155
181
  mode = described_class.stat('/test-file').mode
156
- expect(mode).to be 0100777
182
+ expect(mode).to be 0o100777
157
183
  end
158
184
 
159
185
  it 'changes permission bits on the named files (in list)' do
160
- described_class.chmod 0777, '/test-file', '/test-file2'
186
+ described_class.chmod 0o777, '/test-file', '/test-file2'
161
187
 
162
188
  mode = described_class.stat('/test-file2').mode
163
- expect(mode).to be 0100777
189
+ expect(mode).to be 0o100777
164
190
  end
165
191
  end
166
192
 
@@ -205,7 +231,7 @@ module MemFs
205
231
 
206
232
  it 'ignores -1 user id' do
207
233
  expect {
208
- described_class.chown -1, 42, '/test-file'
234
+ described_class.chown(-1, 42, '/test-file')
209
235
  }.to_not change { described_class.stat('/test-file').uid }
210
236
  end
211
237
 
@@ -303,6 +329,12 @@ module MemFs
303
329
  end
304
330
  end
305
331
 
332
+ describe '.empty?' do
333
+ subject { described_class }
334
+
335
+ it_behaves_like 'aliased method', :empty?, :zero?
336
+ end
337
+
306
338
  describe '.executable?' do
307
339
  let(:access) { 0 }
308
340
  let(:gid) { 0 }
@@ -458,20 +490,20 @@ module MemFs
458
490
  _fs.chdir '/'
459
491
 
460
492
  expanded_path = described_class.expand_path('test-file')
461
- expect(expanded_path).to eq '/test-file'
493
+ expect(expanded_path).to eq expected_path('/test-file')
462
494
  end
463
495
 
464
496
  it 'references path from the current working directory' do
465
497
  _fs.chdir '/test-dir'
466
498
 
467
499
  expanded_path = described_class.expand_path('test-file')
468
- expect(expanded_path).to eq '/test-dir/test-file'
500
+ expect(expanded_path).to eq expected_path('/test-dir/test-file')
469
501
  end
470
502
 
471
503
  context 'when +dir_string+ is provided' do
472
504
  it 'uses +dir_string+ as the stating point' do
473
505
  expanded_path = described_class.expand_path('test-file', '/test')
474
- expect(expanded_path).to eq '/test/test-file'
506
+ expect(expanded_path).to eq expected_path('/test/test-file')
475
507
  end
476
508
  end
477
509
  end
@@ -499,9 +531,16 @@ module MemFs
499
531
  end
500
532
 
501
533
  context 'when the period is the last character in path' do
502
- it 'returns an empty string' do
503
- extname = described_class.extname('test-subject.')
504
- expect(extname).to eq ''
534
+ if MemFs.ruby_version_gte?('2.7') && !MemFs.windows?
535
+ it 'returns a period' do
536
+ extname = described_class.extname('test-subject.')
537
+ expect(extname).to eq '.'
538
+ end
539
+ else
540
+ it 'returns an empty string' do
541
+ extname = described_class.extname('test-subject.')
542
+ expect(extname).to eq ''
543
+ end
505
544
  end
506
545
  end
507
546
  end
@@ -555,21 +594,21 @@ module MemFs
555
594
 
556
595
  describe '.ftype' do
557
596
  context 'when the named entry is a regular file' do
558
- it "returns 'file'" do
597
+ it 'returns "file"' do
559
598
  ftype = described_class.ftype('/test-file')
560
599
  expect(ftype).to eq 'file'
561
600
  end
562
601
  end
563
602
 
564
603
  context 'when the named entry is a directory' do
565
- it "returns 'directory'" do
604
+ it 'returns "directory"' do
566
605
  ftype = described_class.ftype('/test-dir')
567
606
  expect(ftype).to eq 'directory'
568
607
  end
569
608
  end
570
609
 
571
610
  context 'when the named entry is a block device' do
572
- it "returns 'blockSpecial'" do
611
+ it 'returns "blockSpecial"' do
573
612
  _fs.touch '/block-file'
574
613
  file = _fs.find('/block-file')
575
614
  file.block_device = true
@@ -580,7 +619,7 @@ module MemFs
580
619
  end
581
620
 
582
621
  context 'when the named entry is a character device' do
583
- it "returns 'characterSpecial'" do
622
+ it 'returns "characterSpecial"' do
584
623
  _fs.touch '/character-file'
585
624
  file = _fs.find('/character-file')
586
625
  file.character_device = true
@@ -591,7 +630,7 @@ module MemFs
591
630
  end
592
631
 
593
632
  context 'when the named entry is a symlink' do
594
- it "returns 'link'" do
633
+ it 'returns "link"' do
595
634
  ftype = described_class.ftype('/test-link')
596
635
  expect(ftype).to eq 'link'
597
636
  end
@@ -600,7 +639,7 @@ module MemFs
600
639
  # fifo and socket not handled for now
601
640
 
602
641
  context 'when the named entry has no specific type' do
603
- it "returns 'unknown'" do
642
+ it 'returns "unknown"' do
604
643
  root = _fs.find '/'
605
644
  root.add_entry Fake::Entry.new('test-entry')
606
645
 
@@ -623,7 +662,7 @@ module MemFs
623
662
 
624
663
  context 'and the effective user group does not own of the file' do
625
664
  it 'returns false' do
626
- described_class.chown 0, 0, '/test-file'
665
+ described_class.chown 9999, 9999, '/test-file'
627
666
 
628
667
  grpowned = File.grpowned?('/test-file')
629
668
  expect(grpowned).to be false
@@ -700,6 +739,30 @@ module MemFs
700
739
  end
701
740
  end
702
741
  end
742
+
743
+ context 'when given a File object instead of a path' do
744
+ it 'returns true when the File object refers to the same file' do
745
+ file = described_class.new('/test-file', 'r')
746
+ expect(described_class.identical?(file, '/test-file')).to be true
747
+ file.close
748
+ end
749
+
750
+ it 'returns true when both arguments are File objects for the same file' do
751
+ file1 = described_class.new('/test-file', 'r')
752
+ file2 = described_class.new('/test-file', 'r')
753
+ expect(described_class.identical?(file1, file2)).to be true
754
+ file1.close
755
+ file2.close
756
+ end
757
+
758
+ it 'returns false when both File objects refer to different files' do
759
+ file1 = described_class.new('/test-file', 'r')
760
+ file2 = described_class.new('/test-file2', 'r')
761
+ expect(described_class.identical?(file1, file2)).to be false
762
+ file1.close
763
+ file2.close
764
+ end
765
+ end
703
766
  end
704
767
 
705
768
  describe '.join' do
@@ -712,24 +775,24 @@ module MemFs
712
775
  describe '.lchmod' do
713
776
  context 'when the named file is a regular file' do
714
777
  it 'acts like chmod' do
715
- described_class.lchmod 0777, '/test-file'
778
+ described_class.lchmod 0o777, '/test-file'
716
779
 
717
780
  mode = described_class.stat('/test-file').mode
718
- expect(mode).to be 0100777
781
+ expect(mode).to be 0o100777
719
782
  end
720
783
  end
721
784
 
722
785
  context 'when the named file is a symlink' do
723
786
  it 'changes permission bits on the symlink' do
724
- described_class.lchmod 0777, '/test-link'
787
+ described_class.lchmod 0o777, '/test-link'
725
788
 
726
789
  mode = described_class.lstat('/test-link').mode
727
- expect(mode).to be 0100777
790
+ expect(mode).to be 0o100777
728
791
  end
729
792
 
730
- it "does not change permission bits on the link's target" do
793
+ it 'does not change permission bits on the links target' do
731
794
  old_mode = described_class.stat('/test-file').mode
732
- described_class.lchmod 0777, '/test-link'
795
+ described_class.lchmod 0o777, '/test-link'
733
796
 
734
797
  mode = described_class.stat('/test-file').mode
735
798
  expect(mode).to eq old_mode
@@ -791,18 +854,45 @@ module MemFs
791
854
  is_symlink = described_class.lstat('/test-link').symlink?
792
855
  expect(is_symlink).to be true
793
856
  end
857
+ end
858
+ end
794
859
 
795
- context 'and its target does not exist' do
796
- it 'ignores errors' do
860
+ describe '.new' do
861
+ it 'resets the file position to the beginning' do
862
+ described_class.open('/test-file', 'w') { |f| f.write 'hello' }
863
+
864
+ contents = described_class.open('/test-file', 'r', &:read)
865
+ expect(contents).to eq('hello')
866
+
867
+ contents = described_class.open('/test-file', 'r', &:read)
868
+ expect(contents).to eq('hello')
869
+ end
870
+
871
+ context 'when only the filename is provided' do
872
+ context 'and the file exists' do
873
+ it 'returns the open file' do
874
+ file = described_class.new('/test-file')
875
+ expect(file).to be_a(MemFs::File)
876
+ end
877
+ end
878
+
879
+ context 'and the file does not exist' do
880
+ it 'raises an exception' do
797
881
  expect {
798
- described_class.lstat('/no-link')
799
- }.not_to raise_error
882
+ described_class.new('missing-file')
883
+ }.to raise_error(Errno::ENOENT)
884
+ end
885
+ end
886
+
887
+ context 'when the file is a symlink and its target does not exist' do
888
+ it 'raises an exception' do
889
+ expect {
890
+ described_class.new('/no-link')
891
+ }.to raise_error(Errno::ENOENT)
800
892
  end
801
893
  end
802
894
  end
803
- end
804
895
 
805
- describe '.new' do
806
896
  context 'when the mode is provided' do
807
897
  context 'and it is an integer' do
808
898
  subject { described_class.new('/test-file', File::RDWR) }
@@ -820,7 +910,8 @@ module MemFs
820
910
 
821
911
  it 'sets the write+create+truncate mode for "w"' do
822
912
  subject = described_class.new('/test-file', 'w')
823
- expect(subject.send(:opening_mode)).to eq File::CREAT|File::TRUNC|File::WRONLY
913
+ expect(subject.send(:opening_mode)).to \
914
+ eq File::CREAT | File::TRUNC | File::WRONLY
824
915
  end
825
916
 
826
917
  it 'sets the read+write mode for "r+"' do
@@ -830,17 +921,20 @@ module MemFs
830
921
 
831
922
  it 'sets the read+write+create+truncate mode for "w+"' do
832
923
  subject = described_class.new('/test-file', 'w+')
833
- expect(subject.send(:opening_mode)).to eq File::CREAT|File::TRUNC|File::RDWR
924
+ expect(subject.send(:opening_mode)).to \
925
+ eq File::CREAT | File::TRUNC | File::RDWR
834
926
  end
835
927
 
836
928
  it 'sets the write+create+append mode for "a"' do
837
929
  subject = described_class.new('/test-file', 'a')
838
- expect(subject.send(:opening_mode)).to eq File::CREAT|File::APPEND|File::WRONLY
930
+ expect(subject.send(:opening_mode)).to \
931
+ eq File::CREAT | File::APPEND | File::WRONLY
839
932
  end
840
933
 
841
934
  it 'sets the read+write+create+append mode for "a+"' do
842
935
  subject = described_class.new('/test-file', 'a+')
843
- expect(subject.send(:opening_mode)).to eq File::CREAT|File::APPEND|File::RDWR
936
+ expect(subject.send(:opening_mode)).to \
937
+ eq File::CREAT | File::APPEND | File::RDWR
844
938
  end
845
939
 
846
940
  it 'handles the :bom option' do
@@ -848,6 +942,16 @@ module MemFs
848
942
  expect(subject.send(:opening_mode)).to eq File::RDONLY
849
943
  end
850
944
 
945
+ it 'handles the :utf-8 option' do
946
+ subject = described_class.new('/test-file', 'r:utf-8')
947
+ expect(subject.send(:opening_mode)).to eq File::RDONLY
948
+ end
949
+
950
+ it 'handles the :UTF-8 option' do
951
+ subject = described_class.new('/test-file', 'r:UTF-8')
952
+ expect(subject.send(:opening_mode)).to eq File::RDONLY
953
+ end
954
+
851
955
  it 'handles the |utf-8 option' do
852
956
  subject = described_class.new('/test-file', 'r|utf-8')
853
957
  expect(subject.send(:opening_mode)).to eq File::RDONLY
@@ -889,7 +993,9 @@ module MemFs
889
993
 
890
994
  context 'when too many arguments are given' do
891
995
  it 'raises an exception' do
892
- expect { described_class.new(1, 2, 3, 4) }.to raise_error(ArgumentError)
996
+ expect {
997
+ described_class.new(1, 2, 3, 4)
998
+ }.to raise_error(ArgumentError)
893
999
  end
894
1000
  end
895
1001
  end
@@ -907,7 +1013,7 @@ module MemFs
907
1013
 
908
1014
  context 'and the effective user does not own of the file' do
909
1015
  it 'returns false' do
910
- described_class.chown 0, 0, '/test-file'
1016
+ described_class.chown 9999, 9999, '/test-file'
911
1017
 
912
1018
  owned = File.owned?('/test-file')
913
1019
  expect(owned).to be false
@@ -989,18 +1095,18 @@ module MemFs
989
1095
 
990
1096
  context 'when the last argument is a hash' do
991
1097
  it 'passes the contained options to +open+' do
992
- expect(described_class).to receive(:open)
993
- .with('/test-file', File::RDONLY, encoding: 'UTF-8')
994
- .and_return(subject)
1098
+ expect(described_class).to \
1099
+ receive(:open)
1100
+ .with('/test-file', File::RDONLY, { encoding: 'UTF-8' })
1101
+ .and_return(subject)
995
1102
 
996
1103
  described_class.read '/test-file', encoding: 'UTF-8'
997
1104
  end
998
1105
 
999
1106
  context 'when it contains the +open_args+ key' do
1000
1107
  it 'takes precedence over the other options' do
1001
- expect(described_class).to receive(:open)
1002
- .with('/test-file', 'r')
1003
- .and_return(subject)
1108
+ expect(described_class).to \
1109
+ receive(:open).with('/test-file', 'r').and_return(subject)
1004
1110
 
1005
1111
  described_class.read '/test-file', mode: 'w', open_args: ['r']
1006
1112
  end
@@ -1152,7 +1258,7 @@ module MemFs
1152
1258
  context 'when the path does not contain any symlink or useless dots' do
1153
1259
  it 'returns the path itself' do
1154
1260
  path = described_class.realdirpath('/test-file')
1155
- expect(path).to eq '/test-file'
1261
+ expect(path).to eq expected_path('/test-file')
1156
1262
  end
1157
1263
  end
1158
1264
 
@@ -1160,14 +1266,14 @@ module MemFs
1160
1266
  context 'and the symlink is a middle part' do
1161
1267
  it 'returns the path with the symlink dereferrenced' do
1162
1268
  path = described_class.realdirpath('/test-dir/sub-dir-link/test-file')
1163
- expect(path).to eq '/test-dir/sub-dir/test-file'
1269
+ expect(path).to eq expected_path('/test-dir/sub-dir/test-file')
1164
1270
  end
1165
1271
  end
1166
1272
 
1167
1273
  context 'and the symlink is the last part' do
1168
1274
  it 'returns the path with the symlink dereferrenced' do
1169
1275
  path = described_class.realdirpath('/test-dir/sub-dir-link')
1170
- expect(path).to eq '/test-dir/sub-dir'
1276
+ expect(path).to eq expected_path('/test-dir/sub-dir')
1171
1277
  end
1172
1278
  end
1173
1279
  end
@@ -1175,7 +1281,7 @@ module MemFs
1175
1281
  context 'when the path contains useless dots' do
1176
1282
  it 'returns the path with the useless dots interpolated' do
1177
1283
  path = described_class.realdirpath('/test-dir/../test-dir/./sub-dir/test-file')
1178
- expect(path).to eq '/test-dir/sub-dir/test-file'
1284
+ expect(path).to eq expected_path('/test-dir/sub-dir/test-file')
1179
1285
  end
1180
1286
  end
1181
1287
 
@@ -1184,14 +1290,14 @@ module MemFs
1184
1290
  it 'uses the current working directory has base directory' do
1185
1291
  _fs.chdir '/test-dir'
1186
1292
  path = described_class.realdirpath('../test-dir/./sub-dir/test-file')
1187
- expect(path).to eq '/test-dir/sub-dir/test-file'
1293
+ expect(path).to eq expected_path('/test-dir/sub-dir/test-file')
1188
1294
  end
1189
1295
  end
1190
1296
 
1191
1297
  context 'and +dir_string+ is provided' do
1192
1298
  it 'uses the given directory has base directory' do
1193
1299
  path = described_class.realdirpath('../test-dir/./sub-dir/test-file', '/test-dir')
1194
- expect(path).to eq '/test-dir/sub-dir/test-file'
1300
+ expect(path).to eq expected_path('/test-dir/sub-dir/test-file')
1195
1301
  end
1196
1302
  end
1197
1303
  end
@@ -1204,7 +1310,7 @@ module MemFs
1204
1310
 
1205
1311
  it 'uses the name of the target in the resulting path' do
1206
1312
  path = described_class.realdirpath('/test-dir/sub-dir/test-link')
1207
- expect(path).to eq '/test-dir/sub-dir/test'
1313
+ expect(path).to eq expected_path('/test-dir/sub-dir/test')
1208
1314
  end
1209
1315
  end
1210
1316
  end
@@ -1212,7 +1318,7 @@ module MemFs
1212
1318
  context 'when the last part of the given path does not exist' do
1213
1319
  it 'uses its name in the resulting path' do
1214
1320
  path = described_class.realdirpath('/test-dir/sub-dir/test')
1215
- expect(path).to eq '/test-dir/sub-dir/test'
1321
+ expect(path).to eq expected_path('/test-dir/sub-dir/test')
1216
1322
  end
1217
1323
  end
1218
1324
 
@@ -1220,7 +1326,7 @@ module MemFs
1220
1326
  it 'raises an exception' do
1221
1327
  expect {
1222
1328
  described_class.realdirpath '/no-dir/test-file'
1223
- }.to raise_error
1329
+ }.to raise_error Errno::ENOENT
1224
1330
  end
1225
1331
  end
1226
1332
  end
@@ -1235,7 +1341,7 @@ module MemFs
1235
1341
  context 'when the path does not contain any symlink or useless dots' do
1236
1342
  it 'returns the path itself' do
1237
1343
  path = described_class.realpath('/test-file')
1238
- expect(path).to eq '/test-file'
1344
+ expect(path).to eq expected_path('/test-file')
1239
1345
  end
1240
1346
  end
1241
1347
 
@@ -1243,14 +1349,14 @@ module MemFs
1243
1349
  context 'and the symlink is a middle part' do
1244
1350
  it 'returns the path with the symlink dereferrenced' do
1245
1351
  path = described_class.realpath('/test-dir/sub-dir-link/test-file')
1246
- expect(path).to eq '/test-dir/sub-dir/test-file'
1352
+ expect(path).to eq expected_path('/test-dir/sub-dir/test-file')
1247
1353
  end
1248
1354
  end
1249
1355
 
1250
1356
  context 'and the symlink is the last part' do
1251
1357
  it 'returns the path with the symlink dereferrenced' do
1252
1358
  path = described_class.realpath('/test-dir/sub-dir-link')
1253
- expect(path).to eq '/test-dir/sub-dir'
1359
+ expect(path).to eq expected_path('/test-dir/sub-dir')
1254
1360
  end
1255
1361
  end
1256
1362
  end
@@ -1258,7 +1364,7 @@ module MemFs
1258
1364
  context 'when the path contains useless dots' do
1259
1365
  it 'returns the path with the useless dots interpolated' do
1260
1366
  path = described_class.realpath('/test-dir/../test-dir/./sub-dir/test-file')
1261
- expect(path).to eq '/test-dir/sub-dir/test-file'
1367
+ expect(path).to eq expected_path('/test-dir/sub-dir/test-file')
1262
1368
  end
1263
1369
  end
1264
1370
 
@@ -1268,14 +1374,14 @@ module MemFs
1268
1374
  _fs.chdir '/test-dir'
1269
1375
 
1270
1376
  path = described_class.realpath('../test-dir/./sub-dir/test-file')
1271
- expect(path).to eq '/test-dir/sub-dir/test-file'
1377
+ expect(path).to eq expected_path('/test-dir/sub-dir/test-file')
1272
1378
  end
1273
1379
  end
1274
1380
 
1275
1381
  context 'and +dir_string+ is provided' do
1276
1382
  it 'uses the given directory has base directory' do
1277
1383
  path = described_class.realpath('../test-dir/./sub-dir/test-file', '/test-dir')
1278
- expect(path).to eq '/test-dir/sub-dir/test-file'
1384
+ expect(path).to eq expected_path('/test-dir/sub-dir/test-file')
1279
1385
  end
1280
1386
  end
1281
1387
  end
@@ -1284,7 +1390,7 @@ module MemFs
1284
1390
  it 'raises an exception' do
1285
1391
  expect {
1286
1392
  described_class.realpath '/no-dir/test-file'
1287
- }.to raise_error
1393
+ }.to raise_error Errno::ENOENT
1288
1394
  end
1289
1395
  end
1290
1396
  end
@@ -1307,7 +1413,7 @@ module MemFs
1307
1413
  context 'when the named file exists' do
1308
1414
  context 'and the named file has the setgid bit set' do
1309
1415
  it 'returns true' do
1310
- _fs.chmod 02000, '/test-file'
1416
+ _fs.chmod 0o2000, '/test-file'
1311
1417
 
1312
1418
  setgid = File.setgid?('/test-file')
1313
1419
  expect(setgid).to be true
@@ -1316,6 +1422,8 @@ module MemFs
1316
1422
 
1317
1423
  context 'and the named file does not have the setgid bit set' do
1318
1424
  it 'returns false' do
1425
+ _fs.chmod 0o644, '/test-file'
1426
+
1319
1427
  setgid = File.setgid?('/test-file')
1320
1428
  expect(setgid).not_to be true
1321
1429
  end
@@ -1334,7 +1442,7 @@ module MemFs
1334
1442
  context 'when the named file exists' do
1335
1443
  context 'and the named file has the setuid bit set' do
1336
1444
  it 'returns true' do
1337
- _fs.chmod 04000, '/test-file'
1445
+ _fs.chmod 0o4000, '/test-file'
1338
1446
 
1339
1447
  setuid = File.setuid?('/test-file')
1340
1448
  expect(setuid).to be true
@@ -1343,6 +1451,8 @@ module MemFs
1343
1451
 
1344
1452
  context 'and the named file does not have the setuid bit set' do
1345
1453
  it 'returns false' do
1454
+ _fs.chmod 0o644, '/test-file'
1455
+
1346
1456
  setuid = File.setuid?('/test-file')
1347
1457
  expect(setuid).not_to be true
1348
1458
  end
@@ -1441,10 +1551,10 @@ module MemFs
1441
1551
  end
1442
1552
 
1443
1553
  it 'always returns a new object' do
1444
- stat_1 = described_class.stat('/test-file')
1445
- stat_2 = described_class.stat('/test-file')
1554
+ stat1 = described_class.stat('/test-file')
1555
+ stat2 = described_class.stat('/test-file')
1446
1556
 
1447
- expect(stat_2).not_to be stat_1
1557
+ expect(stat2).not_to be stat1
1448
1558
  end
1449
1559
  end
1450
1560
 
@@ -1452,14 +1562,15 @@ module MemFs
1452
1562
  context 'when the named file exists' do
1453
1563
  it 'returns true if the named file has the sticky bit set' do
1454
1564
  _fs.touch '/test-file'
1455
- _fs.chmod 01777, '/test-file'
1565
+ _fs.chmod 0o1777, '/test-file'
1456
1566
 
1457
1567
  sticky = File.sticky?('/test-file')
1458
1568
  expect(sticky).to be true
1459
1569
  end
1460
1570
 
1461
- it "returns false if the named file hasn't' the sticky bit set" do
1571
+ it 'returns false if the named file hasnt the sticky bit set' do
1462
1572
  _fs.touch '/test-file'
1573
+ _fs.chmod 0o666, '/test-file'
1463
1574
 
1464
1575
  sticky = File.sticky?('/test-file')
1465
1576
  expect(sticky).to be false
@@ -1540,33 +1651,37 @@ module MemFs
1540
1651
 
1541
1652
  context 'when the named file does not exist' do
1542
1653
  it 'raises an exception' do
1543
- expect { described_class.truncate '/no-file', 5 }.to raise_error
1654
+ expect {
1655
+ described_class.truncate '/no-file', 5
1656
+ }.to raise_error Errno::ENOENT
1544
1657
  end
1545
1658
  end
1546
1659
 
1547
1660
  context 'when the given size is negative' do
1548
1661
  it 'it raises an exception' do
1549
- expect { described_class.truncate '/test-file', -1 }.to raise_error
1662
+ expect {
1663
+ described_class.truncate '/test-file', -1
1664
+ }.to raise_error TypeError
1550
1665
  end
1551
1666
  end
1552
1667
  end
1553
1668
 
1554
1669
  describe '.umask' do
1555
- before { described_class.umask 0022 }
1670
+ before { described_class.umask 0o022 }
1556
1671
 
1557
1672
  it 'returns the current umask value for this process' do
1558
- expect(described_class.umask).to be 0022
1673
+ expect(described_class.umask).to be 0o022
1559
1674
  end
1560
1675
 
1561
1676
  context 'when the optional argument is given' do
1562
1677
  it 'sets the umask to that value' do
1563
- described_class.umask 0777
1564
- expect(described_class.umask).to be 0777
1678
+ described_class.umask 0o777
1679
+ expect(described_class.umask).to be 0o777
1565
1680
  end
1566
1681
 
1567
1682
  it 'return the previous value' do
1568
- previous_umask = described_class.umask(0777)
1569
- expect(previous_umask).to be 0022
1683
+ previous_umask = described_class.umask(0o777)
1684
+ expect(previous_umask).to be 0o022
1570
1685
  end
1571
1686
  end
1572
1687
  end
@@ -1803,6 +1918,54 @@ module MemFs
1803
1918
  end
1804
1919
  end
1805
1920
 
1921
+ describe '.write' do
1922
+ it 'writes the string to the given file' do
1923
+ described_class.write('/test-file', 'test')
1924
+ read_content = described_class.read('/test-file')
1925
+ expect(read_content).to eq 'test'
1926
+ end
1927
+
1928
+ context 'when +offset+ is provided' do
1929
+ it 'writes the string to the given file when offset is 0' do
1930
+ described_class.write('/test-file', 'test', 0)
1931
+ read_content = described_class.read('/test-file')
1932
+ expect(read_content).to eq 'test'
1933
+ end
1934
+
1935
+ it 'writes the string to the given file when offset is nil' do
1936
+ described_class.write('/test-file', 'test', nil)
1937
+ read_content = described_class.read('/test-file')
1938
+ expect(read_content).to eq 'test'
1939
+ end
1940
+
1941
+ it 'starts writing from the offset' do
1942
+ skip('Offsets not yet implemented in IO.write')
1943
+ described_class.write('/test-file', 'test')
1944
+ described_class.write('/test-file', 'test', 2)
1945
+ read_content = described_class.read('/test-file')
1946
+ expect(read_content).to eq 'tetest'
1947
+ end
1948
+
1949
+ it 'raises an error if offset is negative' do
1950
+ expect {
1951
+ described_class.write('/test-file', 'foo', -1)
1952
+ }.to raise_error Errno::EINVAL
1953
+ end
1954
+
1955
+ it 'raises an error if offset is a boolean' do
1956
+ expect {
1957
+ described_class.write '/test-file', 'foo', false
1958
+ }.to raise_error TypeError
1959
+ end
1960
+
1961
+ it 'raises an error if offset is a string' do
1962
+ expect {
1963
+ described_class.write '/test-file', 'foo', 'offset'
1964
+ }.to raise_error TypeError
1965
+ end
1966
+ end
1967
+ end
1968
+
1806
1969
  describe '.zero?' do
1807
1970
  context 'when the named file exists' do
1808
1971
  context 'and has a zero size' do
@@ -1906,7 +2069,7 @@ module MemFs
1906
2069
  end
1907
2070
 
1908
2071
  describe '#autoclose?' do
1909
- it "returns true by default" do
2072
+ it 'returns true by default' do
1910
2073
  expect(subject.autoclose?).to be true
1911
2074
  end
1912
2075
 
@@ -1938,7 +2101,7 @@ module MemFs
1938
2101
  expect(subject.binmode?).to be true
1939
2102
  end
1940
2103
 
1941
- it "sets the file encoding to ASCII-8BIT" do
2104
+ it 'sets the file encoding to ASCII-8BIT' do
1942
2105
  subject.binmode
1943
2106
 
1944
2107
  encoding = subject.external_encoding
@@ -1947,7 +2110,7 @@ module MemFs
1947
2110
  end
1948
2111
 
1949
2112
  describe '#binmode?' do
1950
- it "returns false by default" do
2113
+ it 'returns false by default' do
1951
2114
  expect(subject.binmode?).to be false
1952
2115
  end
1953
2116
 
@@ -1960,6 +2123,12 @@ module MemFs
1960
2123
  end
1961
2124
  end
1962
2125
 
2126
+ describe '#birthtime' do
2127
+ it 'returns a Time object' do
2128
+ expect(subject.birthtime).to be_a Time
2129
+ end
2130
+ end
2131
+
1963
2132
  describe '#bytes' do
1964
2133
  it_behaves_like 'aliased method', :bytes, :each_byte
1965
2134
  end
@@ -1970,14 +2139,14 @@ module MemFs
1970
2139
 
1971
2140
  describe '#chmod' do
1972
2141
  it 'changes permission bits on the file' do
1973
- subject.chmod 0777
2142
+ subject.chmod 0o777
1974
2143
 
1975
2144
  mode = subject.stat.mode
1976
- expect(mode).to be 0100777
2145
+ expect(mode).to be 0o100777
1977
2146
  end
1978
2147
 
1979
2148
  it 'returns zero' do
1980
- returned_value = subject.chmod(0777)
2149
+ returned_value = subject.chmod(0o777)
1981
2150
  expect(returned_value).to be_zero
1982
2151
  end
1983
2152
  end
@@ -2023,7 +2192,7 @@ module MemFs
2023
2192
 
2024
2193
  it 'ignores -1 user id' do
2025
2194
  expect {
2026
- subject.chown -1, 42
2195
+ subject.chown(-1, 42)
2027
2196
  }.to_not change { subject.stat.uid }
2028
2197
  end
2029
2198
 
@@ -2097,7 +2266,7 @@ module MemFs
2097
2266
  expect(subject.close_on_exec?).to be true
2098
2267
  end
2099
2268
 
2100
- context "when the close-on-exec flag is set to false" do
2269
+ context 'when the close-on-exec flag is set to false' do
2101
2270
  before { subject.close_on_exec = false }
2102
2271
 
2103
2272
  it 'returns false' do
@@ -2186,7 +2355,7 @@ module MemFs
2186
2355
  context 'when the file is not open for reading' do
2187
2356
  it 'raises an exception' do
2188
2357
  expect {
2189
- write_subject.each_byte { |b| }
2358
+ write_subject.each_byte {}
2190
2359
  }.to raise_error IOError
2191
2360
  end
2192
2361
 
@@ -2223,7 +2392,7 @@ module MemFs
2223
2392
  context 'when the file is not open for reading' do
2224
2393
  it 'raises an exception' do
2225
2394
  expect {
2226
- write_subject.each_char { |b| }
2395
+ write_subject.each_char {}
2227
2396
  }.to raise_error IOError
2228
2397
  end
2229
2398
 
@@ -2313,6 +2482,12 @@ module MemFs
2313
2482
  end
2314
2483
  end
2315
2484
 
2485
+ describe '#fileno' do
2486
+ it 'raises an exception' do
2487
+ expect { subject.fileno }.to raise_exception(NotImplementedError)
2488
+ end
2489
+ end
2490
+
2316
2491
  describe '#flock' do
2317
2492
  it 'returns zero' do
2318
2493
  returned_value = subject.flock(File::LOCK_EX)
@@ -2325,19 +2500,11 @@ module MemFs
2325
2500
  expect(subject.lstat).to be_a File::Stat
2326
2501
  end
2327
2502
 
2328
- it 'does not follow the last symbolic link' do
2329
- file = described_class.new('/test-link')
2503
+ context 'when the given file is a symlink' do
2504
+ subject { described_class.new('/test-link') }
2330
2505
 
2331
- is_symlink = file.lstat.symlink?
2332
- expect(is_symlink).to be true
2333
- end
2334
-
2335
- context 'when the named file is a symlink' do
2336
- context 'and its target does not exist' do
2337
- it 'ignores errors' do
2338
- file = described_class.new('/no-link')
2339
- expect { file.lstat }.not_to raise_error
2340
- end
2506
+ it 'does not follow the last symbolic link' do
2507
+ expect(subject.lstat).to be_symlink
2341
2508
  end
2342
2509
  end
2343
2510
  end
@@ -2507,7 +2674,7 @@ module MemFs
2507
2674
  expect(content).to eq "test\n"
2508
2675
  end
2509
2676
 
2510
- it "does not override the file's content" do
2677
+ it 'does not override the files content' do
2511
2678
  write_subject.puts 'test'
2512
2679
  write_subject.puts 'test'
2513
2680
  write_subject.close
@@ -2562,7 +2729,7 @@ module MemFs
2562
2729
 
2563
2730
  context 'when a buffer is given' do
2564
2731
  it 'fills the buffer with the read content' do
2565
- buffer = String.new
2732
+ buffer = +''
2566
2733
  subject.read 2, buffer
2567
2734
 
2568
2735
  expect(buffer).to eq random_string[0, 2]
@@ -2599,7 +2766,7 @@ module MemFs
2599
2766
 
2600
2767
  context 'when +whence+ is IO::SEEK_END' do
2601
2768
  it 'seeks to +amount+ plus end of stream' do
2602
- subject.seek -1, ::IO::SEEK_END
2769
+ subject.seek(-1, ::IO::SEEK_END)
2603
2770
 
2604
2771
  expect(subject.pos).to be 4
2605
2772
  end
@@ -2621,7 +2788,7 @@ module MemFs
2621
2788
 
2622
2789
  context 'if the position ends up to be less than zero' do
2623
2790
  it 'raises an exception' do
2624
- expect { subject.seek -1 }.to raise_error Errno::EINVAL
2791
+ expect { subject.seek(-1) }.to raise_error Errno::EINVAL
2625
2792
  end
2626
2793
  end
2627
2794
  end