memfs - memfs_rw_test.go
1 package memfs
2
3 import (
4 "bytes"
5 "errors"
6 "io/fs"
7 "reflect"
8 "sync"
9 "testing"
10 "time"
11 )
12
13 func newFSRW(d dnode) FS {
14 return FS{
15 fsRO: fsRO{
16 de: &dnodeRW{
17 dnode: d,
18 },
19 },
20 }
21 }
22
23 func TestOpenRW(t *testing.T) {
24 for n, test := range [...]*struct {
25 FS FS
26 Path string
27 File fs.File
28 Err error
29 }{
30 { // 1
31 FS: newFSRW(dnode{}),
32 Path: "file",
33 Err: &fs.PathError{
34 Op: "open",
35 Path: "file",
36 Err: fs.ErrPermission,
37 },
38 },
39 { // 2
40 FS: newFSRW(dnode{
41 mode: fs.ModeDir | fs.ModePerm,
42 }),
43 Path: "file",
44 Err: &fs.PathError{
45 Op: "open",
46 Path: "file",
47 Err: fs.ErrNotExist,
48 },
49 },
50 { // 3
51 FS: newFSRW(dnode{
52 entries: []*dirEnt{
53 {
54 directoryEntry: &inodeRW{},
55 name: "file",
56 },
57 },
58 }),
59 Path: "file",
60 Err: &fs.PathError{
61 Op: "open",
62 Path: "file",
63 Err: fs.ErrPermission,
64 },
65 },
66 { // 4
67 FS: newFSRW(dnode{
68 entries: []*dirEnt{
69 {
70 directoryEntry: &inodeRW{
71 inode: inode{
72 mode: fs.ModeDir | fs.ModePerm,
73 },
74 },
75 name: "file",
76 },
77 },
78 mode: fs.ModeDir | fs.ModePerm,
79 }),
80 Path: "file",
81 File: &File{
82 mu: &sync.RWMutex{},
83 file: file{
84 name: "file",
85 inode: &inode{
86 mode: fs.ModeDir | fs.ModePerm,
87 },
88 opMode: opRead | opSeek,
89 },
90 },
91 },
92 { // 5
93 FS: newFSRW(dnode{
94 entries: []*dirEnt{
95 {
96 directoryEntry: &inodeRW{
97 inode: inode{
98 mode: fs.ModeDir | fs.ModePerm,
99 },
100 },
101 name: "otherFile",
102 },
103 },
104 mode: fs.ModeDir | fs.ModePerm,
105 }),
106 Path: "file",
107 Err: &fs.PathError{
108 Op: "open",
109 Path: "file",
110 Err: fs.ErrNotExist,
111 },
112 },
113 { // 6
114 FS: newFSRW(dnode{
115 entries: []*dirEnt{
116 {
117 directoryEntry: &dnodeRW{
118 dnode: dnode{
119 entries: []*dirEnt{
120 {
121 directoryEntry: &inodeRW{
122 inode: inode{
123 mode: fs.ModeDir | fs.ModePerm,
124 },
125 },
126 name: "deepFile",
127 },
128 },
129 mode: fs.ModeDir | fs.ModePerm,
130 },
131 },
132 name: "dir",
133 },
134 },
135 mode: fs.ModeDir | fs.ModePerm,
136 }),
137 Path: "dir/deepFile",
138 File: &File{
139 mu: &sync.RWMutex{},
140 file: file{
141 name: "deepFile",
142 inode: &inode{
143 mode: fs.ModeDir | fs.ModePerm,
144 },
145 opMode: opRead | opSeek,
146 },
147 },
148 },
149 } {
150 f, err := test.FS.Open(test.Path)
151 if !reflect.DeepEqual(err, test.Err) {
152 t.Errorf("test %d: expecting error %s, got %s", n+1, test.Err, err)
153 } else if !reflect.DeepEqual(f, test.File) {
154 t.Errorf("test %d: expected file %v, got %v", n+1, test.File, f)
155 }
156 }
157 }
158
159 func TestFSReadDirRW(t *testing.T) {
160 for n, test := range [...]*struct {
161 FS FS
162 Path string
163 Output []fs.DirEntry
164 Err error
165 }{
166 { // 1
167 FS: newFSRW(dnode{}),
168 Path: ".",
169 Err: &fs.PathError{
170 Op: "readdir",
171 Path: ".",
172 Err: fs.ErrPermission,
173 },
174 },
175 { // 2
176 FS: newFSRW(dnode{
177 mode: fs.ModeDir | fs.ModePerm,
178 }),
179 Path: ".",
180 Output: []fs.DirEntry{},
181 },
182 { // 3
183 FS: newFSRW(dnode{
184 entries: []*dirEnt{
185 {
186 directoryEntry: &inodeRW{
187 inode: inode{
188 modtime: time.Unix(1, 0),
189 mode: 2,
190 },
191 },
192 name: "test",
193 },
194 },
195 mode: fs.ModeDir | fs.ModePerm,
196 }),
197 Path: ".",
198 Output: []fs.DirEntry{
199 &dirEnt{
200 directoryEntry: &inodeRW{
201 inode: inode{
202 modtime: time.Unix(1, 0),
203 mode: 2,
204 },
205 },
206 name: "test",
207 },
208 },
209 },
210 { // 4
211 FS: newFSRW(dnode{
212 entries: []*dirEnt{
213 {
214 directoryEntry: &inodeRW{
215 inode: inode{
216 modtime: time.Unix(1, 0),
217 mode: 2,
218 },
219 },
220 name: "test",
221 },
222 {
223 directoryEntry: &inodeRW{
224 inode: inode{
225 modtime: time.Unix(3, 0),
226 mode: 4,
227 },
228 },
229 name: "test2",
230 },
231 },
232 mode: fs.ModeDir | fs.ModePerm,
233 }),
234 Path: ".",
235 Output: []fs.DirEntry{
236 &dirEnt{
237 directoryEntry: &inodeRW{
238 inode: inode{
239 modtime: time.Unix(1, 0),
240 mode: 2,
241 },
242 },
243 name: "test",
244 },
245 &dirEnt{
246 directoryEntry: &inodeRW{
247 inode: inode{
248 modtime: time.Unix(3, 0),
249 mode: 4,
250 },
251 },
252 name: "test2",
253 },
254 },
255 },
256 { // 5
257 FS: newFSRW(dnode{
258 entries: []*dirEnt{
259 {
260 directoryEntry: &inodeRW{
261 inode: inode{
262 modtime: time.Unix(1, 0),
263 mode: 2,
264 },
265 },
266 name: "test",
267 },
268 {
269 directoryEntry: &dnodeRW{
270 dnode: dnode{
271 entries: []*dirEnt{
272 {
273 directoryEntry: &inodeRW{
274 inode: inode{
275 modtime: time.Unix(3, 0),
276 mode: 4,
277 },
278 },
279 name: "test3",
280 },
281 },
282 modtime: time.Unix(5, 0),
283 mode: fs.ModeDir | fs.ModePerm,
284 },
285 },
286 name: "test2",
287 },
288 },
289 mode: fs.ModeDir | fs.ModePerm,
290 }),
291 Path: ".",
292 Output: []fs.DirEntry{
293 &dirEnt{
294 directoryEntry: &inodeRW{
295 inode: inode{
296 modtime: time.Unix(1, 0),
297 mode: 2,
298 },
299 },
300 name: "test",
301 },
302 &dirEnt{
303 directoryEntry: &dnodeRW{
304 dnode: dnode{
305 entries: []*dirEnt{
306 {
307 directoryEntry: &inodeRW{
308 inode: inode{
309 modtime: time.Unix(3, 0),
310 mode: 4,
311 },
312 },
313 name: "test3",
314 },
315 },
316 modtime: time.Unix(5, 0),
317 mode: fs.ModeDir | fs.ModePerm,
318 },
319 },
320 name: "test2",
321 },
322 },
323 },
324 { // 6
325 FS: newFSRW(dnode{
326 entries: []*dirEnt{
327 {
328 directoryEntry: &inodeRW{
329 inode: inode{
330 modtime: time.Unix(1, 0),
331 mode: 2,
332 },
333 },
334 name: "test",
335 },
336 {
337 directoryEntry: &dnodeRW{
338 dnode: dnode{
339 entries: []*dirEnt{
340 {
341 directoryEntry: &inodeRW{
342 inode: inode{
343 modtime: time.Unix(3, 0),
344 mode: 4,
345 },
346 },
347 name: "test3",
348 },
349 },
350 modtime: time.Unix(5, 0),
351 mode: fs.ModeDir | fs.ModePerm,
352 },
353 },
354 name: "test2",
355 },
356 },
357 mode: fs.ModeDir | fs.ModePerm,
358 }),
359 Path: "test2",
360 Output: []fs.DirEntry{
361 &dirEnt{
362 directoryEntry: &inodeRW{
363 inode: inode{
364 modtime: time.Unix(3, 0),
365 mode: 4,
366 },
367 },
368 name: "test3",
369 },
370 },
371 },
372 { // 7
373 FS: newFSRW(dnode{
374 entries: []*dirEnt{
375 {
376 directoryEntry: &inodeRW{
377 inode: inode{
378 modtime: time.Unix(1, 0),
379 mode: 2,
380 },
381 },
382 name: "test",
383 },
384 {
385 directoryEntry: &dnodeRW{
386 dnode: dnode{
387 entries: []*dirEnt{
388 {
389 directoryEntry: &inodeRW{
390 inode: inode{
391 modtime: time.Unix(3, 0),
392 mode: 4,
393 },
394 },
395 name: "test3",
396 },
397 },
398 modtime: time.Unix(5, 0),
399 mode: fs.ModeDir,
400 },
401 },
402 name: "test2",
403 },
404 },
405 mode: fs.ModeDir | fs.ModePerm,
406 }),
407 Path: "test2",
408 Err: &fs.PathError{
409 Op: "readdir",
410 Path: "test2",
411 Err: fs.ErrPermission,
412 },
413 },
414 } {
415 de, err := test.FS.ReadDir(test.Path)
416 if !reflect.DeepEqual(err, test.Err) {
417 t.Errorf("test %d: expecting error %s, got %s", n+1, test.Err, err)
418 } else if !reflect.DeepEqual(test.Output, de) {
419 t.Errorf("test %d: expecting to get %v, got %v", n+1, test.Output, de)
420 }
421 }
422 }
423
424 func TestReadFileRW(t *testing.T) {
425 for n, test := range [...]*struct {
426 FS FS
427 Path string
428 Output []byte
429 Err error
430 }{
431 { // 1
432 FS: newFSRW(dnode{}),
433 Path: ".",
434 Err: &fs.PathError{
435 Op: "readfile",
436 Path: ".",
437 Err: fs.ErrPermission,
438 },
439 },
440 { // 2
441 FS: newFSRW(dnode{
442 mode: fs.ModeDir | fs.ModePerm,
443 }),
444 Path: ".",
445 Err: &fs.PathError{
446 Op: "readfile",
447 Path: ".",
448 Err: fs.ErrInvalid,
449 },
450 },
451 { // 3
452 FS: newFSRW(dnode{
453 mode: fs.ModeDir | fs.ModePerm,
454 }),
455 Path: "file",
456 Err: &fs.PathError{
457 Op: "readfile",
458 Path: "file",
459 Err: fs.ErrNotExist,
460 },
461 },
462 { // 4
463 FS: newFSRW(dnode{
464 entries: []*dirEnt{
465 {
466 directoryEntry: &inodeRW{},
467 name: "notFile",
468 },
469 },
470 mode: fs.ModeDir | fs.ModePerm,
471 }),
472 Path: "file",
473 Err: &fs.PathError{
474 Op: "readfile",
475 Path: "file",
476 Err: fs.ErrNotExist,
477 },
478 },
479 { // 5
480 FS: newFSRW(dnode{
481 entries: []*dirEnt{
482 {
483 directoryEntry: &inodeRW{},
484 name: "file",
485 },
486 },
487 mode: fs.ModeDir | fs.ModePerm,
488 }),
489 Path: "file",
490 Err: &fs.PathError{
491 Op: "readfile",
492 Path: "file",
493 Err: fs.ErrPermission,
494 },
495 },
496 { // 6
497 FS: newFSRW(dnode{
498 entries: []*dirEnt{
499 {
500 directoryEntry: &inodeRW{
501 inode: inode{
502 data: []byte("DATA"),
503 mode: fs.ModePerm,
504 },
505 },
506 name: "file",
507 },
508 },
509 mode: fs.ModeDir | fs.ModePerm,
510 }),
511 Path: "file",
512 Output: []byte("DATA"),
513 },
514 { // 7
515 FS: newFSRW(dnode{
516 entries: []*dirEnt{
517 {
518 directoryEntry: &inodeRW{
519 inode: inode{
520 data: []byte("DATA"),
521 mode: fs.ModePerm,
522 },
523 },
524 name: "file",
525 },
526 {
527 directoryEntry: &dnodeRW{
528 dnode: dnode{
529 entries: []*dirEnt{
530 {
531 directoryEntry: &inodeRW{
532 inode: inode{
533 data: []byte("MORE DATA"),
534 mode: fs.ModePerm,
535 },
536 },
537 name: "file2",
538 },
539 },
540 mode: fs.ModeDir | fs.ModePerm,
541 },
542 },
543 name: "DIR",
544 },
545 },
546 mode: fs.ModeDir | fs.ModePerm,
547 }),
548 Path: "DIR/file2",
549 Output: []byte("MORE DATA"),
550 },
551 } {
552 data, err := test.FS.ReadFile(test.Path)
553 if !reflect.DeepEqual(err, test.Err) {
554 t.Errorf("test %d: expecting error %s, got %s", n+1, test.Err, err)
555 } else if !bytes.Equal(test.Output, data) {
556 t.Errorf("test %d: expecting to get %v, got %v", n+1, test.Output, data)
557 }
558 }
559 }
560
561 func TestStatRW(t *testing.T) {
562 for n, test := range [...]*struct {
563 FS FS
564 Path string
565 Output fs.FileInfo
566 Err error
567 }{
568 { // 1
569 FS: newFSRW(dnode{}),
570 Path: ".",
571 Err: &fs.PathError{
572 Op: "stat",
573 Path: ".",
574 Err: fs.ErrPermission,
575 },
576 },
577 { // 2
578 FS: newFSRW(dnode{
579 modtime: time.Unix(1, 2),
580 mode: fs.ModeDir | fs.ModePerm,
581 }),
582 Path: ".",
583 Output: &dirEnt{
584 directoryEntry: &dnodeRW{
585 dnode: dnode{
586 modtime: time.Unix(1, 2),
587 mode: fs.ModeDir | fs.ModePerm,
588 },
589 },
590 name: "/",
591 },
592 },
593 { // 3
594 FS: newFSRW(dnode{
595 mode: fs.ModeDir | fs.ModePerm,
596 }),
597 Path: "file",
598 Err: &fs.PathError{
599 Op: "stat",
600 Path: "file",
601 Err: fs.ErrNotExist,
602 },
603 },
604 { // 4
605 FS: newFSRW(dnode{
606 entries: []*dirEnt{
607 {
608 directoryEntry: &inodeRW{},
609 name: "notFile",
610 },
611 },
612 mode: fs.ModeDir | fs.ModePerm,
613 }),
614 Path: "file",
615 Err: &fs.PathError{
616 Op: "stat",
617 Path: "file",
618 Err: fs.ErrNotExist,
619 },
620 },
621 { // 5
622 FS: newFSRW(dnode{
623 entries: []*dirEnt{
624 {
625 directoryEntry: &inodeRW{
626 inode: inode{
627 modtime: time.Unix(1, 2),
628 mode: 3,
629 },
630 },
631 name: "file",
632 },
633 },
634 mode: fs.ModeDir | fs.ModePerm,
635 }),
636 Path: "file",
637 Output: &dirEnt{
638 directoryEntry: &inodeRW{
639 inode: inode{
640 modtime: time.Unix(1, 2),
641 mode: 3,
642 },
643 },
644 name: "file",
645 },
646 },
647 { // 6
648 FS: newFSRW(dnode{
649 entries: []*dirEnt{
650 {
651 directoryEntry: &inodeRW{
652 inode: inode{
653 modtime: time.Unix(1, 2),
654 mode: 3,
655 },
656 },
657 name: "file",
658 },
659 {
660 directoryEntry: &dnodeRW{
661 dnode: dnode{
662 modtime: time.Unix(4, 5),
663 mode: 6,
664 },
665 },
666 name: "dir",
667 },
668 },
669 mode: fs.ModeDir | fs.ModePerm,
670 }),
671 Path: "dir",
672 Output: &dirEnt{
673 directoryEntry: &dnodeRW{
674 dnode: dnode{
675 modtime: time.Unix(4, 5),
676 mode: 6,
677 },
678 },
679 name: "dir",
680 },
681 },
682 { // 7
683 FS: newFSRW(dnode{
684 entries: []*dirEnt{
685 {
686 directoryEntry: &inodeRW{
687 inode: inode{
688 modtime: time.Unix(1, 2),
689 mode: 3,
690 },
691 },
692 name: "file",
693 },
694 {
695 directoryEntry: &dnodeRW{
696 dnode: dnode{
697 entries: []*dirEnt{
698 {
699 directoryEntry: &inodeRW{
700 inode: inode{
701 modtime: time.Unix(4, 5),
702 mode: 6,
703 },
704 },
705 name: "anotherFile",
706 },
707 },
708 modtime: time.Unix(7, 8),
709 mode: 9,
710 },
711 },
712 name: "dir",
713 },
714 },
715 mode: fs.ModeDir | fs.ModePerm,
716 }),
717 Path: "dir/anotherFile",
718 Err: &fs.PathError{
719 Op: "stat",
720 Path: "dir/anotherFile",
721 Err: fs.ErrPermission,
722 },
723 },
724 { // 8
725 FS: newFSRW(dnode{
726 entries: []*dirEnt{
727 {
728 directoryEntry: &inodeRW{
729 inode: inode{
730 modtime: time.Unix(1, 2),
731 mode: 3,
732 },
733 },
734 name: "file",
735 },
736 {
737 directoryEntry: &dnodeRW{
738 dnode: dnode{
739 entries: []*dirEnt{
740 {
741 directoryEntry: &inodeRW{
742 inode: inode{
743 modtime: time.Unix(4, 5),
744 mode: 6,
745 },
746 },
747 name: "anotherFile",
748 },
749 },
750 modtime: time.Unix(7, 8),
751 mode: fs.ModeDir | fs.ModePerm,
752 },
753 },
754 name: "dir",
755 },
756 },
757 mode: fs.ModeDir | fs.ModePerm,
758 }),
759 Path: "dir/anotherFile",
760 Output: &dirEnt{
761 directoryEntry: &inodeRW{
762 inode: inode{
763 modtime: time.Unix(4, 5),
764 mode: 6,
765 },
766 },
767 name: "anotherFile",
768 },
769 },
770 } {
771 stat, err := test.FS.Stat(test.Path)
772 if !reflect.DeepEqual(err, test.Err) {
773 t.Errorf("test %d: expecting error %s, got %s", n+1, test.Err, err)
774 } else if !reflect.DeepEqual(test.Output, stat) {
775 t.Errorf("test %d: expecting to get %v, got %v", n+1, test.Output, stat)
776 }
777 }
778 }
779
780 func TestMkdir(t *testing.T) {
781 now := time.Now()
782 for n, test := range [...]*struct {
783 FS FS
784 Path string
785 PathPerms fs.FileMode
786 Output FS
787 Err error
788 }{
789 { // 1
790 FS: newFSRW(dnode{}),
791 Output: newFSRW(dnode{}),
792 Err: &fs.PathError{
793 Op: "mkdir",
794 Path: "",
795 Err: fs.ErrInvalid,
796 },
797 },
798 { // 2
799 FS: newFSRW(dnode{
800 modtime: now,
801 mode: fs.ModeDir | fs.ModePerm,
802 }),
803 Output: newFSRW(dnode{
804 modtime: now,
805 mode: fs.ModeDir | fs.ModePerm,
806 }),
807 Err: &fs.PathError{
808 Op: "mkdir",
809 Path: "",
810 Err: fs.ErrInvalid,
811 },
812 },
813 { // 3
814 FS: newFSRW(dnode{
815 modtime: now,
816 mode: fs.ModeDir | fs.ModePerm,
817 }),
818 Output: newFSRW(dnode{
819 modtime: now,
820 mode: fs.ModeDir | fs.ModePerm,
821 }),
822 Path: ".",
823 Err: &fs.PathError{
824 Op: "mkdir",
825 Path: ".",
826 Err: fs.ErrInvalid,
827 },
828 },
829 { // 4
830 FS: newFSRW(dnode{
831 modtime: now,
832 mode: fs.ModeDir | fs.ModePerm,
833 }),
834 Output: newFSRW(dnode{
835 modtime: now,
836 entries: []*dirEnt{
837 {
838 directoryEntry: &dnodeRW{
839 dnode: dnode{
840 modtime: now,
841 mode: fs.ModeDir,
842 },
843 },
844 name: "a",
845 },
846 },
847 mode: fs.ModeDir | fs.ModePerm,
848 }),
849 Path: "a",
850 },
851 { // 5
852 FS: newFSRW(dnode{
853 modtime: now,
854 mode: fs.ModeDir | fs.ModePerm,
855 }),
856 Output: newFSRW(dnode{
857 modtime: now,
858 mode: fs.ModeDir | fs.ModePerm,
859 }),
860 Path: "a/b",
861 Err: &fs.PathError{
862 Op: "mkdir",
863 Path: "a/b",
864 Err: fs.ErrNotExist,
865 },
866 },
867 { // 6
868 FS: newFSRW(dnode{
869 modtime: now,
870 entries: []*dirEnt{
871 {
872 directoryEntry: &dnodeRW{
873 dnode: dnode{
874 modtime: now,
875 mode: fs.ModeDir,
876 },
877 },
878 name: "a",
879 },
880 },
881 mode: fs.ModeDir | fs.ModePerm,
882 }),
883 Output: newFSRW(dnode{
884 modtime: now,
885 entries: []*dirEnt{
886 {
887 directoryEntry: &dnodeRW{
888 dnode: dnode{
889 modtime: now,
890 mode: fs.ModeDir,
891 },
892 },
893 name: "a",
894 },
895 },
896 mode: fs.ModeDir | fs.ModePerm,
897 }),
898 Path: "a/b",
899 Err: &fs.PathError{
900 Op: "mkdir",
901 Path: "a/b",
902 Err: fs.ErrPermission,
903 },
904 },
905 { // 7
906 FS: newFSRW(dnode{
907 modtime: now,
908 entries: []*dirEnt{
909 {
910 directoryEntry: &inodeRW{
911 inode: inode{
912 modtime: now,
913 mode: fs.ModePerm,
914 },
915 },
916 name: "a",
917 },
918 },
919 mode: fs.ModeDir | fs.ModePerm,
920 }),
921 Output: newFSRW(dnode{
922 modtime: now,
923 entries: []*dirEnt{
924 {
925 directoryEntry: &inodeRW{
926 inode: inode{
927 modtime: now,
928 mode: fs.ModePerm,
929 },
930 },
931 name: "a",
932 },
933 },
934 mode: fs.ModeDir | fs.ModePerm,
935 }),
936 Path: "a/b",
937 Err: &fs.PathError{
938 Op: "mkdir",
939 Path: "a/b",
940 Err: fs.ErrInvalid,
941 },
942 },
943 { // 8
944 FS: newFSRW(dnode{
945 modtime: now,
946 entries: []*dirEnt{
947 {
948 directoryEntry: &dnodeRW{
949 dnode: dnode{
950 modtime: now,
951 mode: fs.ModeDir | fs.ModePerm,
952 },
953 },
954 name: "a",
955 },
956 },
957 mode: fs.ModeDir | fs.ModePerm,
958 }),
959 Output: newFSRW(dnode{
960 modtime: now,
961 entries: []*dirEnt{
962 {
963 directoryEntry: &dnodeRW{
964 dnode: dnode{
965 entries: []*dirEnt{
966 {
967 directoryEntry: &dnodeRW{
968 dnode: dnode{
969 modtime: now,
970 mode: fs.ModeDir | 0o123,
971 },
972 },
973 name: "b",
974 },
975 },
976 modtime: now,
977 mode: fs.ModeDir | fs.ModePerm,
978 },
979 },
980 name: "a",
981 },
982 },
983 mode: fs.ModeDir | fs.ModePerm,
984 }),
985 Path: "a/b",
986 PathPerms: 0o123,
987 },
988 } {
989 if err := test.FS.Mkdir(test.Path, test.PathPerms); !reflect.DeepEqual(err, test.Err) {
990 t.Errorf("test %d: expecting error %s, got %s", n+1, test.Err, err)
991 } else {
992 fixTimes(test.FS.de.(*dnodeRW), now)
993
994 if !reflect.DeepEqual(&test.Output, &test.FS) {
995 t.Errorf("test %d: expecting to get %v, got %v", n+1, &test.Output, &test.FS)
996 }
997 }
998 }
999 }
1000
1001 func withinRange(dt time.Duration) bool {
1002 return dt > -10*time.Second && dt < 10*time.Second
1003 }
1004
1005 func fixTimes(d *dnodeRW, now time.Time) {
1006 if withinRange(d.modtime.Sub(now)) {
1007 d.modtime = now
1008 }
1009
1010 for _, e := range d.entries {
1011 if de, ok := e.directoryEntry.(*dnodeRW); ok {
1012 fixTimes(de, now)
1013 } else if f, ok := e.directoryEntry.(*inodeRW); ok && withinRange(f.modtime.Sub(now)) {
1014 f.modtime = now
1015 }
1016 }
1017 }
1018
1019 func TestMkdirAll(t *testing.T) {
1020 now := time.Now()
1021 for n, test := range [...]*struct {
1022 FS FS
1023 Path string
1024 PathPerms fs.FileMode
1025 Output FS
1026 Err error
1027 }{
1028 { // 1
1029 FS: newFSRW(dnode{}),
1030 Output: newFSRW(dnode{}),
1031 Err: &fs.PathError{
1032 Op: "mkdirall",
1033 Path: "",
1034 Err: fs.ErrInvalid,
1035 },
1036 },
1037 { // 2
1038 FS: newFSRW(dnode{
1039 modtime: now,
1040 mode: fs.ModeDir | fs.ModePerm,
1041 }),
1042 Output: newFSRW(dnode{
1043 modtime: now,
1044 mode: fs.ModeDir | fs.ModePerm,
1045 }),
1046 Err: &fs.PathError{
1047 Op: "mkdirall",
1048 Path: "",
1049 Err: fs.ErrInvalid,
1050 },
1051 },
1052 { // 3
1053 FS: newFSRW(dnode{
1054 modtime: now,
1055 mode: fs.ModeDir | fs.ModePerm,
1056 }),
1057 Output: newFSRW(dnode{
1058 modtime: now,
1059 mode: fs.ModeDir | fs.ModePerm,
1060 }),
1061 Path: ".",
1062 Err: &fs.PathError{
1063 Op: "mkdirall",
1064 Path: ".",
1065 Err: fs.ErrInvalid,
1066 },
1067 },
1068 { // 4
1069 FS: newFSRW(dnode{
1070 modtime: now,
1071 mode: fs.ModeDir | fs.ModePerm,
1072 }),
1073 Output: newFSRW(dnode{
1074 modtime: now,
1075 entries: []*dirEnt{
1076 {
1077 directoryEntry: &dnodeRW{
1078 dnode: dnode{
1079 modtime: now,
1080 mode: fs.ModeDir,
1081 },
1082 },
1083 name: "a",
1084 },
1085 },
1086 mode: fs.ModeDir | fs.ModePerm,
1087 }),
1088 Path: "a",
1089 },
1090 { // 5
1091 FS: newFSRW(dnode{
1092 modtime: now,
1093 mode: fs.ModeDir | fs.ModePerm,
1094 }),
1095 Output: newFSRW(dnode{
1096 modtime: now,
1097 entries: []*dirEnt{
1098 {
1099 directoryEntry: &dnodeRW{
1100 dnode: dnode{
1101 modtime: now,
1102 mode: fs.ModeDir,
1103 },
1104 },
1105 name: "a",
1106 },
1107 },
1108 mode: fs.ModeDir | fs.ModePerm,
1109 }),
1110 Path: "a/b",
1111 Err: &fs.PathError{
1112 Op: "mkdirall",
1113 Path: "a/b",
1114 Err: fs.ErrPermission,
1115 },
1116 },
1117 { // 6
1118 FS: newFSRW(dnode{
1119 modtime: now,
1120 entries: []*dirEnt{
1121 {
1122 directoryEntry: &dnodeRW{
1123 dnode: dnode{
1124 modtime: now,
1125 mode: fs.ModeDir,
1126 },
1127 },
1128 name: "a",
1129 },
1130 },
1131 mode: fs.ModeDir | fs.ModePerm,
1132 }),
1133 Output: newFSRW(dnode{
1134 modtime: now,
1135 entries: []*dirEnt{
1136 {
1137 directoryEntry: &dnodeRW{
1138 dnode: dnode{
1139 modtime: now,
1140 mode: fs.ModeDir,
1141 },
1142 },
1143 name: "a",
1144 },
1145 },
1146 mode: fs.ModeDir | fs.ModePerm,
1147 }),
1148 Path: "a/b",
1149 Err: &fs.PathError{
1150 Op: "mkdirall",
1151 Path: "a/b",
1152 Err: fs.ErrPermission,
1153 },
1154 },
1155 { // 7
1156 FS: newFSRW(dnode{
1157 modtime: now,
1158 entries: []*dirEnt{
1159 {
1160 directoryEntry: &inodeRW{
1161 inode: inode{
1162 modtime: now,
1163 mode: fs.ModePerm,
1164 },
1165 },
1166 name: "a",
1167 },
1168 },
1169 mode: fs.ModeDir | fs.ModePerm,
1170 }),
1171 Output: newFSRW(dnode{
1172 modtime: now,
1173 entries: []*dirEnt{
1174 {
1175 directoryEntry: &inodeRW{
1176 inode: inode{
1177 modtime: now,
1178 mode: fs.ModePerm,
1179 },
1180 },
1181 name: "a",
1182 },
1183 },
1184 mode: fs.ModeDir | fs.ModePerm,
1185 }),
1186 Path: "a/b",
1187 Err: &fs.PathError{
1188 Op: "mkdirall",
1189 Path: "a/b",
1190 Err: fs.ErrInvalid,
1191 },
1192 },
1193 { // 8
1194 FS: newFSRW(dnode{
1195 modtime: now,
1196 entries: []*dirEnt{
1197 {
1198 directoryEntry: &dnodeRW{
1199 dnode: dnode{
1200 modtime: now,
1201 mode: fs.ModeDir | fs.ModePerm,
1202 },
1203 },
1204 name: "a",
1205 },
1206 },
1207 mode: fs.ModeDir | fs.ModePerm,
1208 }),
1209 Output: newFSRW(dnode{
1210 modtime: now,
1211 entries: []*dirEnt{
1212 {
1213 directoryEntry: &dnodeRW{
1214 dnode: dnode{
1215 entries: []*dirEnt{
1216 {
1217 directoryEntry: &dnodeRW{
1218 dnode: dnode{
1219 modtime: now,
1220 mode: fs.ModeDir | 0o123,
1221 },
1222 },
1223 name: "b",
1224 },
1225 },
1226 modtime: now,
1227 mode: fs.ModeDir | fs.ModePerm,
1228 },
1229 },
1230 name: "a",
1231 },
1232 },
1233 mode: fs.ModeDir | fs.ModePerm,
1234 }),
1235 Path: "a/b",
1236 PathPerms: 0o123,
1237 },
1238 { // 9
1239 FS: newFSRW(dnode{
1240 modtime: now,
1241 mode: fs.ModeDir | fs.ModePerm,
1242 }),
1243 Output: newFSRW(dnode{
1244 modtime: now,
1245 entries: []*dirEnt{
1246 {
1247 directoryEntry: &dnodeRW{
1248 dnode: dnode{
1249 entries: []*dirEnt{
1250 {
1251 directoryEntry: &dnodeRW{
1252 dnode: dnode{
1253 modtime: now,
1254 mode: fs.ModeDir | 0o765,
1255 },
1256 },
1257 name: "b",
1258 },
1259 },
1260 modtime: now,
1261 mode: fs.ModeDir | 0o765,
1262 },
1263 },
1264 name: "a",
1265 },
1266 },
1267 mode: fs.ModeDir | fs.ModePerm,
1268 }),
1269 Path: "a/b",
1270 PathPerms: 0o765,
1271 },
1272 } {
1273 if err := test.FS.MkdirAll(test.Path, test.PathPerms); !reflect.DeepEqual(err, test.Err) {
1274 t.Errorf("test %d: expecting error %s, got %s", n+1, test.Err, err)
1275 } else {
1276 fixTimes(test.FS.de.(*dnodeRW), now)
1277
1278 if !reflect.DeepEqual(&test.Output, &test.FS) {
1279 t.Errorf("test %d: expecting to get %v, got %v", n+1, &test.Output, &test.FS)
1280 }
1281 }
1282 }
1283 }
1284
1285 func TestCreate(t *testing.T) {
1286 now := time.Now()
1287 for n, test := range [...]*struct {
1288 FS FS
1289 Path string
1290 PathPerms fs.FileMode
1291 OutputFS FS
1292 OutputFile *File
1293 Err error
1294 }{
1295 { // 1
1296 FS: newFSRW(dnode{}),
1297 OutputFS: newFSRW(dnode{}),
1298 Err: &fs.PathError{
1299 Op: "create",
1300 Path: "",
1301 Err: fs.ErrInvalid,
1302 },
1303 },
1304 { // 2
1305 FS: newFSRW(dnode{
1306 mode: fs.ModeDir | fs.ModePerm,
1307 }),
1308 OutputFS: newFSRW(dnode{
1309 mode: fs.ModeDir | fs.ModePerm,
1310 }),
1311 Err: &fs.PathError{
1312 Op: "create",
1313 Path: "",
1314 Err: fs.ErrInvalid,
1315 },
1316 },
1317 { // 3
1318 FS: newFSRW(dnode{}),
1319 OutputFS: newFSRW(dnode{}),
1320 Path: "a",
1321 Err: &fs.PathError{
1322 Op: "create",
1323 Path: "a",
1324 Err: fs.ErrPermission,
1325 },
1326 },
1327 { // 4
1328 FS: newFSRW(dnode{
1329 mode: fs.ModeDir | fs.ModePerm,
1330 }),
1331 OutputFS: newFSRW(dnode{
1332 mode: fs.ModeDir | fs.ModePerm,
1333 }),
1334 Path: ".",
1335 Err: &fs.PathError{
1336 Op: "create",
1337 Path: ".",
1338 Err: fs.ErrInvalid,
1339 },
1340 },
1341 { // 5
1342 FS: newFSRW(dnode{
1343 modtime: now.Add(-20 * time.Second),
1344 mode: fs.ModeDir | fs.ModePerm,
1345 }),
1346 OutputFS: newFSRW(dnode{
1347 entries: []*dirEnt{
1348 {
1349 directoryEntry: &inodeRW{
1350 inode: inode{
1351 modtime: now,
1352 mode: defaultPerms,
1353 },
1354 },
1355 name: "a",
1356 },
1357 },
1358 modtime: now,
1359 mode: fs.ModeDir | fs.ModePerm,
1360 }),
1361 OutputFile: &File{
1362 mu: &sync.RWMutex{},
1363 file: file{
1364 inode: &inode{
1365 modtime: now,
1366 mode: defaultPerms,
1367 },
1368 name: "a",
1369 opMode: opRead | opWrite | opSeek,
1370 },
1371 },
1372 Path: "a",
1373 },
1374 { // 6
1375 FS: newFSRW(dnode{
1376 entries: []*dirEnt{
1377 {
1378 directoryEntry: &inodeRW{
1379 inode: inode{
1380 data: []byte("Hello"),
1381 modtime: now.Add(-20 * time.Second),
1382 mode: fs.ModePerm,
1383 },
1384 },
1385 name: "a",
1386 },
1387 },
1388 modtime: now.Add(-20 * time.Second),
1389 mode: fs.ModeDir | fs.ModePerm,
1390 }),
1391 OutputFS: newFSRW(dnode{
1392 entries: []*dirEnt{
1393 {
1394 directoryEntry: &inodeRW{
1395 inode: inode{
1396 data: ([]byte("Hello"))[:0],
1397 modtime: now,
1398 mode: fs.ModePerm,
1399 },
1400 },
1401 name: "a",
1402 },
1403 },
1404 modtime: now.Add(-20 * time.Second),
1405 mode: fs.ModeDir | fs.ModePerm,
1406 }),
1407 OutputFile: &File{
1408 mu: &sync.RWMutex{},
1409 file: file{
1410 inode: &inode{
1411 data: ([]byte("Hello"))[:0],
1412 modtime: now,
1413 mode: fs.ModePerm,
1414 },
1415 name: "a",
1416 opMode: opRead | opWrite | opSeek,
1417 },
1418 },
1419 Path: "a",
1420 },
1421 { // 7
1422 FS: newFSRW(dnode{
1423 entries: []*dirEnt{
1424 {
1425 directoryEntry: &dnodeRW{
1426 dnode: dnode{
1427 modtime: now.Add(-20 * time.Second),
1428 mode: fs.ModeDir | fs.ModePerm,
1429 },
1430 },
1431 name: "a",
1432 },
1433 },
1434 modtime: now,
1435 mode: fs.ModeDir | fs.ModePerm,
1436 }),
1437 OutputFS: newFSRW(dnode{
1438 entries: []*dirEnt{
1439 {
1440 directoryEntry: &dnodeRW{
1441 dnode: dnode{
1442 entries: []*dirEnt{
1443 {
1444 directoryEntry: &inodeRW{
1445 inode: inode{
1446 modtime: now,
1447 mode: defaultPerms,
1448 },
1449 },
1450 name: "b",
1451 },
1452 },
1453 modtime: now,
1454 mode: fs.ModeDir | fs.ModePerm,
1455 },
1456 },
1457 name: "a",
1458 },
1459 },
1460 modtime: now,
1461 mode: fs.ModeDir | fs.ModePerm,
1462 }),
1463 OutputFile: &File{
1464 mu: &sync.RWMutex{},
1465 file: file{
1466 inode: &inode{
1467 modtime: now,
1468 mode: defaultPerms,
1469 },
1470 name: "b",
1471 opMode: opRead | opWrite | opSeek,
1472 },
1473 },
1474 Path: "a/b",
1475 },
1476 { // 8
1477 FS: newFSRW(dnode{
1478 entries: []*dirEnt{
1479 {
1480 directoryEntry: &dnodeRW{
1481 dnode: dnode{
1482 modtime: now.Add(-20 * time.Second),
1483 mode: fs.ModeDir | 0o444,
1484 },
1485 },
1486 name: "a",
1487 },
1488 },
1489 modtime: now,
1490 mode: fs.ModeDir | fs.ModePerm,
1491 }),
1492 OutputFS: newFSRW(dnode{
1493 entries: []*dirEnt{
1494 {
1495 directoryEntry: &dnodeRW{
1496 dnode: dnode{
1497 modtime: now.Add(-20 * time.Second),
1498 mode: fs.ModeDir | 0o444,
1499 },
1500 },
1501 name: "a",
1502 },
1503 },
1504 modtime: now,
1505 mode: fs.ModeDir | fs.ModePerm,
1506 }),
1507 Path: "a/b",
1508 Err: &fs.PathError{
1509 Op: "create",
1510 Path: "a/b",
1511 Err: fs.ErrPermission,
1512 },
1513 },
1514 } {
1515 if f, err := test.FS.Create(test.Path); !reflect.DeepEqual(err, test.Err) {
1516 t.Errorf("test %d: expecting error %s, got %s", n+1, test.Err, err)
1517 } else {
1518 fixTimes(test.FS.de.(*dnodeRW), now)
1519
1520 if !reflect.DeepEqual(test.OutputFile, f) {
1521 t.Errorf("test %d: expecting to get file %v, got %v", n+1, test.OutputFile, f)
1522 } else if !reflect.DeepEqual(&test.OutputFS, &test.FS) {
1523 t.Errorf("test %d: expecting to get FS %v, got %v", n+1, &test.OutputFS, &test.FS)
1524 }
1525 }
1526 }
1527 }
1528
1529 func TestLink(t *testing.T) {
1530 now := time.Now()
1531 for n, test := range [...]*struct {
1532 FS FS
1533 From, To string
1534 Output FS
1535 Err error
1536 }{
1537 { // 1
1538 FS: newFSRW(dnode{}),
1539 Output: newFSRW(dnode{}),
1540 From: ".",
1541 Err: &fs.PathError{
1542 Op: "link",
1543 Path: ".",
1544 Err: fs.ErrPermission,
1545 },
1546 },
1547 { // 2
1548 FS: newFSRW(dnode{
1549 mode: fs.ModeDir | fs.ModePerm,
1550 }),
1551 Output: newFSRW(dnode{
1552 mode: fs.ModeDir | fs.ModePerm,
1553 }),
1554 From: ".",
1555 Err: &fs.PathError{
1556 Op: "link",
1557 Path: ".",
1558 Err: fs.ErrInvalid,
1559 },
1560 },
1561 { // 3
1562 FS: newFSRW(dnode{
1563 mode: fs.ModeDir | fs.ModePerm,
1564 }),
1565 Output: newFSRW(dnode{
1566 mode: fs.ModeDir | fs.ModePerm,
1567 }),
1568 From: "a",
1569 Err: &fs.PathError{
1570 Op: "link",
1571 Path: "a",
1572 Err: fs.ErrNotExist,
1573 },
1574 },
1575 { // 4
1576 FS: newFSRW(dnode{
1577 entries: []*dirEnt{
1578 {
1579 name: "a",
1580 directoryEntry: &inodeRW{},
1581 },
1582 },
1583 mode: fs.ModeDir | fs.ModePerm,
1584 }),
1585 Output: newFSRW(dnode{
1586 entries: []*dirEnt{
1587 {
1588 name: "a",
1589 directoryEntry: &inodeRW{},
1590 },
1591 },
1592 mode: fs.ModeDir | fs.ModePerm,
1593 }),
1594 From: "a",
1595 Err: &fs.PathError{
1596 Op: "link",
1597 Path: "",
1598 Err: fs.ErrInvalid,
1599 },
1600 },
1601 { // 5
1602 FS: newFSRW(dnode{
1603 entries: []*dirEnt{
1604 {
1605 name: "a",
1606 directoryEntry: &inodeRW{},
1607 },
1608 },
1609 mode: fs.ModeDir | fs.ModePerm,
1610 }),
1611 Output: newFSRW(dnode{
1612 entries: []*dirEnt{
1613 {
1614 name: "a",
1615 directoryEntry: &inodeRW{},
1616 },
1617 },
1618 mode: fs.ModeDir | fs.ModePerm,
1619 }),
1620 From: "a",
1621 To: "a",
1622 Err: &fs.PathError{
1623 Op: "link",
1624 Path: "a",
1625 Err: fs.ErrExist,
1626 },
1627 },
1628 { // 6
1629 FS: newFSRW(dnode{
1630 entries: []*dirEnt{
1631 {
1632 name: "a",
1633 directoryEntry: &inodeRW{
1634 inode: inode{
1635 data: []byte("Hello"),
1636 },
1637 },
1638 },
1639 },
1640 mode: fs.ModeDir,
1641 }),
1642 Output: newFSRW(dnode{
1643 entries: []*dirEnt{
1644 {
1645 name: "a",
1646 directoryEntry: &inodeRW{
1647 inode: inode{
1648 data: []byte("Hello"),
1649 },
1650 },
1651 },
1652 },
1653 mode: fs.ModeDir,
1654 }),
1655 From: "a",
1656 To: "b",
1657 Err: &fs.PathError{
1658 Op: "link",
1659 Path: "a",
1660 Err: fs.ErrPermission,
1661 },
1662 },
1663 { // 7
1664 FS: newFSRW(dnode{
1665 entries: []*dirEnt{
1666 {
1667 name: "a",
1668 directoryEntry: &inodeRW{
1669 inode: inode{
1670 data: []byte("Hello"),
1671 },
1672 },
1673 },
1674 },
1675 mode: fs.ModeDir | fs.ModePerm,
1676 }),
1677 Output: newFSRW(dnode{
1678 entries: []*dirEnt{
1679 {
1680 name: "a",
1681 directoryEntry: &inodeRW{
1682 inode: inode{
1683 data: []byte("Hello"),
1684 },
1685 },
1686 },
1687 {
1688 name: "b",
1689 directoryEntry: &inodeRW{
1690 inode: inode{
1691 data: []byte("Hello"),
1692 },
1693 },
1694 },
1695 },
1696 mode: fs.ModeDir | fs.ModePerm,
1697 modtime: now,
1698 }),
1699 From: "a",
1700 To: "b",
1701 },
1702 { // 8
1703 FS: newFSRW(dnode{
1704 entries: []*dirEnt{
1705 {
1706 name: "a",
1707 directoryEntry: &dnodeRW{
1708 dnode: dnode{
1709 entries: []*dirEnt{
1710 {
1711 name: "b",
1712 directoryEntry: &inodeRW{
1713 inode: inode{
1714 data: []byte("Hello"),
1715 },
1716 },
1717 },
1718 },
1719 mode: fs.ModeDir | fs.ModePerm,
1720 },
1721 },
1722 },
1723 },
1724 mode: fs.ModeDir | fs.ModePerm,
1725 }),
1726 Output: newFSRW(dnode{
1727 entries: []*dirEnt{
1728 {
1729 name: "a",
1730 directoryEntry: &dnodeRW{
1731 dnode: dnode{
1732 entries: []*dirEnt{
1733 {
1734 name: "b",
1735 directoryEntry: &inodeRW{
1736 inode: inode{
1737 data: []byte("Hello"),
1738 },
1739 },
1740 },
1741 },
1742 mode: fs.ModeDir | fs.ModePerm,
1743 },
1744 },
1745 },
1746 {
1747 name: "c",
1748 directoryEntry: &inodeRW{
1749 inode: inode{
1750 data: []byte("Hello"),
1751 },
1752 },
1753 },
1754 },
1755 mode: fs.ModeDir | fs.ModePerm,
1756 modtime: now,
1757 }),
1758 From: "a/b",
1759 To: "c",
1760 },
1761 { // 9
1762 FS: newFSRW(dnode{
1763 entries: []*dirEnt{
1764 {
1765 name: "a",
1766 directoryEntry: &inodeRW{
1767 inode: inode{
1768 data: []byte("Hello"),
1769 },
1770 },
1771 },
1772 {
1773 name: "b",
1774 directoryEntry: &dnodeRW{
1775 dnode: dnode{
1776 mode: fs.ModeDir | fs.ModePerm,
1777 },
1778 },
1779 },
1780 },
1781 mode: fs.ModeDir | fs.ModePerm,
1782 }),
1783 Output: newFSRW(dnode{
1784 entries: []*dirEnt{
1785 {
1786 name: "a",
1787 directoryEntry: &inodeRW{
1788 inode: inode{
1789 data: []byte("Hello"),
1790 },
1791 },
1792 },
1793 {
1794 name: "b",
1795 directoryEntry: &dnodeRW{
1796 dnode: dnode{
1797 entries: []*dirEnt{
1798 {
1799 name: "c",
1800 directoryEntry: &inodeRW{
1801 inode: inode{
1802 data: []byte("Hello"),
1803 },
1804 },
1805 },
1806 },
1807 mode: fs.ModeDir | fs.ModePerm,
1808 modtime: now,
1809 },
1810 },
1811 },
1812 },
1813 mode: fs.ModeDir | fs.ModePerm,
1814 }),
1815 From: "a",
1816 To: "b/c",
1817 },
1818 } {
1819 if err := test.FS.Link(test.From, test.To); !reflect.DeepEqual(err, test.Err) {
1820 t.Errorf("test %d: expecting error %s, got %s", n+1, test.Err, err)
1821 } else {
1822 fixTimes(test.FS.de.(*dnodeRW), now)
1823
1824 if !reflect.DeepEqual(&test.Output, &test.FS) {
1825 t.Errorf("test %d: expecting to get FS %v, got %v", n+1, &test.Output, &test.FS)
1826 }
1827 }
1828 }
1829 }
1830
1831 func TestSymlink(t *testing.T) {
1832 now := time.Now()
1833 for n, test := range [...]*struct {
1834 FS FS
1835 From, To string
1836 Output FS
1837 Err error
1838 }{
1839 { // 1
1840 FS: newFSRW(dnode{}),
1841 Output: newFSRW(dnode{}),
1842 Err: &fs.PathError{
1843 Op: "symlink",
1844 Path: "",
1845 Err: fs.ErrInvalid,
1846 },
1847 },
1848 { // 2
1849 FS: newFSRW(dnode{
1850 mode: fs.ModeDir | fs.ModePerm,
1851 }),
1852 Output: newFSRW(dnode{
1853 mode: fs.ModeDir | fs.ModePerm,
1854 }),
1855 Err: &fs.PathError{
1856 Op: "symlink",
1857 Path: "",
1858 Err: fs.ErrInvalid,
1859 },
1860 },
1861 { // 3
1862 FS: newFSRW(dnode{
1863 mode: fs.ModeDir | fs.ModePerm,
1864 }),
1865 Output: newFSRW(dnode{
1866 mode: fs.ModeDir | fs.ModePerm,
1867 }),
1868 From: "/a",
1869 Err: &fs.PathError{
1870 Op: "symlink",
1871 Path: "",
1872 Err: fs.ErrInvalid,
1873 },
1874 },
1875 { // 4
1876 FS: newFSRW(dnode{
1877 entries: []*dirEnt{
1878 {
1879 name: "a",
1880 directoryEntry: &inodeRW{},
1881 },
1882 },
1883 mode: fs.ModeDir | fs.ModePerm,
1884 }),
1885 Output: newFSRW(dnode{
1886 entries: []*dirEnt{
1887 {
1888 name: "a",
1889 directoryEntry: &inodeRW{},
1890 },
1891 },
1892 mode: fs.ModeDir | fs.ModePerm,
1893 }),
1894 From: "/a",
1895 Err: &fs.PathError{
1896 Op: "symlink",
1897 Path: "",
1898 Err: fs.ErrInvalid,
1899 },
1900 },
1901 { // 5
1902 FS: newFSRW(dnode{
1903 entries: []*dirEnt{
1904 {
1905 name: "a",
1906 directoryEntry: &inodeRW{},
1907 },
1908 },
1909 mode: fs.ModeDir | fs.ModePerm,
1910 }),
1911 Output: newFSRW(dnode{
1912 entries: []*dirEnt{
1913 {
1914 name: "a",
1915 directoryEntry: &inodeRW{},
1916 },
1917 },
1918 mode: fs.ModeDir | fs.ModePerm,
1919 }),
1920 From: "/a",
1921 To: "a",
1922 Err: &fs.PathError{
1923 Op: "symlink",
1924 Path: "a",
1925 Err: fs.ErrExist,
1926 },
1927 },
1928 { // 6
1929 FS: newFSRW(dnode{
1930 entries: []*dirEnt{
1931 {
1932 name: "a",
1933 directoryEntry: &inodeRW{
1934 inode: inode{
1935 data: []byte("Hello"),
1936 },
1937 },
1938 },
1939 },
1940 mode: fs.ModeDir,
1941 }),
1942 Output: newFSRW(dnode{
1943 entries: []*dirEnt{
1944 {
1945 name: "a",
1946 directoryEntry: &inodeRW{
1947 inode: inode{
1948 data: []byte("Hello"),
1949 },
1950 },
1951 },
1952 },
1953 mode: fs.ModeDir,
1954 }),
1955 From: "/a",
1956 To: "b",
1957 Err: &fs.PathError{
1958 Op: "symlink",
1959 Path: "b",
1960 Err: fs.ErrPermission,
1961 },
1962 },
1963 { // 7
1964 FS: newFSRW(dnode{
1965 entries: []*dirEnt{
1966 {
1967 name: "a",
1968 directoryEntry: &inodeRW{
1969 inode: inode{
1970 data: []byte("Hello"),
1971 },
1972 },
1973 },
1974 },
1975 mode: fs.ModeDir | fs.ModePerm,
1976 }),
1977 Output: newFSRW(dnode{
1978 entries: []*dirEnt{
1979 {
1980 name: "a",
1981 directoryEntry: &inodeRW{
1982 inode: inode{
1983 data: []byte("Hello"),
1984 },
1985 },
1986 },
1987 {
1988 name: "b",
1989 directoryEntry: &inodeRW{
1990 inode: inode{
1991 data: []byte("/a"),
1992 modtime: now,
1993 mode: fs.ModeSymlink | fs.ModePerm,
1994 },
1995 },
1996 },
1997 },
1998 mode: fs.ModeDir | fs.ModePerm,
1999 modtime: now,
2000 }),
2001 From: "/a",
2002 To: "b",
2003 },
2004 { // 8
2005 FS: newFSRW(dnode{
2006 entries: []*dirEnt{
2007 {
2008 name: "a",
2009 directoryEntry: &dnodeRW{
2010 dnode: dnode{
2011 entries: []*dirEnt{
2012 {
2013 name: "b",
2014 directoryEntry: &inodeRW{
2015 inode: inode{
2016 data: []byte("Hello"),
2017 },
2018 },
2019 },
2020 },
2021 mode: fs.ModeDir | fs.ModePerm,
2022 },
2023 },
2024 },
2025 },
2026 mode: fs.ModeDir | fs.ModePerm,
2027 }),
2028 Output: newFSRW(dnode{
2029 entries: []*dirEnt{
2030 {
2031 name: "a",
2032 directoryEntry: &dnodeRW{
2033 dnode: dnode{
2034 entries: []*dirEnt{
2035 {
2036 name: "b",
2037 directoryEntry: &inodeRW{
2038 inode: inode{
2039 data: []byte("Hello"),
2040 },
2041 },
2042 },
2043 },
2044 mode: fs.ModeDir | fs.ModePerm,
2045 },
2046 },
2047 },
2048 {
2049 name: "c",
2050 directoryEntry: &inodeRW{
2051 inode: inode{
2052 data: []byte("/a/b"),
2053 mode: fs.ModeSymlink | fs.ModePerm,
2054 modtime: now,
2055 },
2056 },
2057 },
2058 },
2059 mode: fs.ModeDir | fs.ModePerm,
2060 modtime: now,
2061 }),
2062 From: "/a/b",
2063 To: "c",
2064 },
2065 { // 9
2066 FS: newFSRW(dnode{
2067 entries: []*dirEnt{
2068 {
2069 name: "a",
2070 directoryEntry: &inodeRW{
2071 inode: inode{
2072 data: []byte("Hello"),
2073 },
2074 },
2075 },
2076 {
2077 name: "b",
2078 directoryEntry: &dnodeRW{
2079 dnode: dnode{
2080 mode: fs.ModeDir | fs.ModePerm,
2081 },
2082 },
2083 },
2084 },
2085 mode: fs.ModeDir | fs.ModePerm,
2086 }),
2087 Output: newFSRW(dnode{
2088 entries: []*dirEnt{
2089 {
2090 name: "a",
2091 directoryEntry: &inodeRW{
2092 inode: inode{
2093 data: []byte("Hello"),
2094 },
2095 },
2096 },
2097 {
2098 name: "b",
2099 directoryEntry: &dnodeRW{
2100 dnode: dnode{
2101 entries: []*dirEnt{
2102 {
2103 name: "c",
2104 directoryEntry: &inodeRW{
2105 inode: inode{
2106 data: []byte("/a"),
2107 modtime: now,
2108 mode: fs.ModeSymlink | fs.ModePerm,
2109 },
2110 },
2111 },
2112 },
2113 mode: fs.ModeDir | fs.ModePerm,
2114 modtime: now,
2115 },
2116 },
2117 },
2118 },
2119 mode: fs.ModeDir | fs.ModePerm,
2120 }),
2121 From: "/a",
2122 To: "b/c",
2123 },
2124 } {
2125 if err := test.FS.Symlink(test.From, test.To); !reflect.DeepEqual(err, test.Err) {
2126 t.Errorf("test %d: expecting error %s, got %s", n+1, test.Err, err)
2127 } else {
2128 fixTimes(test.FS.de.(*dnodeRW), now)
2129
2130 if !reflect.DeepEqual(&test.Output, &test.FS) {
2131 t.Errorf("test %d: expecting to get FS %v, got %v", n+1, &test.Output, &test.FS)
2132 }
2133 }
2134 }
2135 }
2136
2137 func TestSymlinkResolveFileRW(t *testing.T) {
2138 for n, test := range [...]*struct {
2139 FS FS
2140 Path string
2141 Output []byte
2142 Err error
2143 }{
2144 { // 1
2145 FS: newFSRW(dnode{
2146 entries: []*dirEnt{
2147 {
2148 name: "a",
2149 directoryEntry: &inodeRW{
2150 inode: inode{
2151 data: []byte("Hello"),
2152 mode: fs.ModePerm,
2153 },
2154 },
2155 },
2156 {
2157 name: "b",
2158 directoryEntry: &inodeRW{
2159 inode: inode{
2160 data: []byte("/a"),
2161 mode: fs.ModeSymlink | fs.ModePerm,
2162 },
2163 },
2164 },
2165 },
2166 mode: fs.ModeDir | fs.ModePerm,
2167 }),
2168 Path: "b",
2169 Output: []byte("Hello"),
2170 },
2171 { // 2
2172 FS: newFSRW(dnode{
2173 entries: []*dirEnt{
2174 {
2175 name: "a",
2176 directoryEntry: &inodeRW{
2177 inode: inode{
2178 data: []byte("Hello"),
2179 mode: fs.ModePerm,
2180 },
2181 },
2182 },
2183 {
2184 name: "b",
2185 directoryEntry: &inodeRW{
2186 inode: inode{
2187 data: []byte("/c"),
2188 mode: fs.ModeSymlink | fs.ModePerm,
2189 },
2190 },
2191 },
2192 },
2193 mode: fs.ModeDir | fs.ModePerm,
2194 }),
2195 Path: "b",
2196 Err: fs.ErrNotExist,
2197 },
2198 { // 3
2199 FS: newFSRW(dnode{
2200 entries: []*dirEnt{
2201 {
2202 name: "a",
2203 directoryEntry: &inodeRW{
2204 inode: inode{
2205 data: []byte("Hello"),
2206 },
2207 },
2208 },
2209 {
2210 name: "b",
2211 directoryEntry: &inodeRW{
2212 inode: inode{
2213 data: []byte("/a"),
2214 mode: fs.ModeSymlink | fs.ModePerm,
2215 },
2216 },
2217 },
2218 },
2219 mode: fs.ModeDir | fs.ModePerm,
2220 }),
2221 Path: "b",
2222 Err: fs.ErrPermission,
2223 },
2224 { // 4
2225 FS: newFSRW(dnode{
2226 entries: []*dirEnt{
2227 {
2228 name: "a",
2229 directoryEntry: &dnodeRW{
2230 dnode: dnode{
2231 mode: fs.ModeDir | fs.ModePerm,
2232 entries: []*dirEnt{
2233 {
2234 name: "b",
2235 directoryEntry: &inodeRW{
2236 inode: inode{
2237 data: []byte("World"),
2238 mode: fs.ModePerm,
2239 },
2240 },
2241 },
2242 },
2243 },
2244 },
2245 },
2246 {
2247 name: "c",
2248 directoryEntry: &inodeRW{
2249 inode: inode{
2250 data: []byte("/a/b"),
2251 mode: fs.ModeSymlink | fs.ModePerm,
2252 },
2253 },
2254 },
2255 },
2256 mode: fs.ModeDir | fs.ModePerm,
2257 }),
2258 Path: "c",
2259 Output: []byte("World"),
2260 },
2261 { // 5
2262 FS: newFSRW(dnode{
2263 entries: []*dirEnt{
2264 {
2265 name: "a",
2266 directoryEntry: &dnodeRW{
2267 dnode: dnode{
2268 mode: fs.ModeDir | fs.ModePerm,
2269 entries: []*dirEnt{
2270 {
2271 name: "b",
2272 directoryEntry: &inodeRW{
2273 inode: inode{
2274 data: []byte("World"),
2275 mode: fs.ModePerm,
2276 },
2277 },
2278 },
2279 },
2280 },
2281 },
2282 },
2283 {
2284 name: "c",
2285 directoryEntry: &inodeRW{
2286 inode: inode{
2287 data: []byte("a/b"),
2288 mode: fs.ModeSymlink | fs.ModePerm,
2289 },
2290 },
2291 },
2292 },
2293 mode: fs.ModeDir | fs.ModePerm,
2294 }),
2295 Path: "c",
2296 Output: []byte("World"),
2297 },
2298 { // 6
2299 FS: newFSRW(dnode{
2300 entries: []*dirEnt{
2301 {
2302 name: "a",
2303 directoryEntry: &dnodeRW{
2304 dnode: dnode{
2305 mode: fs.ModeDir | fs.ModePerm,
2306 entries: []*dirEnt{
2307 {
2308 name: "b",
2309 directoryEntry: &inodeRW{
2310 inode: inode{
2311 data: []byte("/c"),
2312 mode: fs.ModeSymlink | fs.ModePerm,
2313 },
2314 },
2315 },
2316 },
2317 },
2318 },
2319 },
2320 {
2321 name: "c",
2322 directoryEntry: &inode{
2323 data: []byte("FooBar"),
2324 mode: fs.ModePerm,
2325 },
2326 },
2327 },
2328 mode: fs.ModeDir | fs.ModePerm,
2329 }),
2330 Path: "a/b",
2331 Output: []byte("FooBar"),
2332 },
2333 { // 7
2334 FS: newFSRW(dnode{
2335 entries: []*dirEnt{
2336 {
2337 name: "a",
2338 directoryEntry: &dnodeRW{
2339 dnode: dnode{
2340 mode: fs.ModeDir | fs.ModePerm,
2341 entries: []*dirEnt{
2342 {
2343 name: "b",
2344 directoryEntry: &inodeRW{
2345 inode: inode{
2346 data: []byte("../c"),
2347 mode: fs.ModeSymlink | fs.ModePerm,
2348 },
2349 },
2350 },
2351 },
2352 },
2353 },
2354 },
2355 {
2356 name: "c",
2357 directoryEntry: &inodeRW{
2358 inode: inode{
2359 data: []byte("FooBar"),
2360 mode: fs.ModePerm,
2361 },
2362 },
2363 },
2364 },
2365 mode: fs.ModeDir | fs.ModePerm,
2366 }),
2367 Path: "a/b",
2368 Output: []byte("FooBar"),
2369 },
2370 { // 8
2371 FS: newFSRW(dnode{
2372 entries: []*dirEnt{
2373 {
2374 name: "a",
2375 directoryEntry: &dnodeRW{
2376 dnode: dnode{
2377 mode: fs.ModeDir | fs.ModePerm,
2378 entries: []*dirEnt{
2379 {
2380 name: "b",
2381 directoryEntry: &inodeRW{
2382 inode: inode{
2383 data: []byte("../c"),
2384 mode: fs.ModeSymlink | fs.ModePerm,
2385 },
2386 },
2387 },
2388 },
2389 },
2390 },
2391 },
2392 {
2393 name: "c",
2394 directoryEntry: &inodeRW{
2395 inode: inode{
2396 data: []byte("d"),
2397 mode: fs.ModeSymlink | fs.ModePerm,
2398 },
2399 },
2400 },
2401 {
2402 name: "d",
2403 directoryEntry: &inodeRW{
2404 inode: inode{
2405 data: []byte("Baz"),
2406 mode: fs.ModePerm,
2407 },
2408 },
2409 },
2410 },
2411 mode: fs.ModeDir | fs.ModePerm,
2412 }),
2413 Path: "a/b",
2414 Output: []byte("Baz"),
2415 },
2416 } {
2417 if output, err := test.FS.ReadFile(test.Path); !errors.Is(err, test.Err) {
2418 t.Errorf("test %d: expecting error %s, got %s", n+1, test.Err, err)
2419 } else if !bytes.Equal(test.Output, output) {
2420 t.Errorf("test %d: expecting to get %v, got %v", n+1, test.Output, output)
2421 }
2422 }
2423 }
2424
2425 func TestSymlinkResolveDirRW(t *testing.T) {
2426 for n, test := range [...]*struct {
2427 FS FS
2428 Path string
2429 Output []fs.DirEntry
2430 Err error
2431 }{
2432 { // 1
2433 FS: newFSRW(dnode{
2434 entries: []*dirEnt{
2435 {
2436 name: "a",
2437 directoryEntry: &dnodeRW{
2438 dnode: dnode{
2439 entries: []*dirEnt{
2440 {
2441 name: "b",
2442 directoryEntry: &inodeRW{
2443 inode: inode{
2444 data: []byte("Foo"),
2445 mode: fs.ModePerm,
2446 },
2447 },
2448 },
2449 {
2450 name: "c",
2451 directoryEntry: &inodeRW{
2452 inode: inode{
2453 data: []byte("Bar"),
2454 mode: fs.ModePerm,
2455 },
2456 },
2457 },
2458 {
2459 name: "d",
2460 directoryEntry: &inodeRW{
2461 inode: inode{
2462 data: []byte("Baz"),
2463 mode: fs.ModePerm,
2464 },
2465 },
2466 },
2467 },
2468 mode: fs.ModeDir | fs.ModePerm,
2469 },
2470 },
2471 },
2472 {
2473 name: "e",
2474 directoryEntry: &inodeRW{
2475 inode: inode{
2476 data: []byte("/f"),
2477 mode: fs.ModeSymlink | fs.ModePerm,
2478 },
2479 },
2480 },
2481 },
2482 mode: fs.ModeDir | fs.ModePerm,
2483 }),
2484 Path: "e",
2485 Err: fs.ErrNotExist,
2486 },
2487 { // 2
2488 FS: newFSRW(dnode{
2489 entries: []*dirEnt{
2490 {
2491 name: "a",
2492 directoryEntry: &dnodeRW{
2493 dnode: dnode{
2494 entries: []*dirEnt{
2495 {
2496 name: "b",
2497 directoryEntry: &inodeRW{
2498 inode: inode{
2499 data: []byte("Foo"),
2500 mode: fs.ModePerm,
2501 },
2502 },
2503 },
2504 {
2505 name: "c",
2506 directoryEntry: &inodeRW{
2507 inode: inode{
2508 data: []byte("Bar"),
2509 mode: fs.ModePerm,
2510 },
2511 },
2512 },
2513 {
2514 name: "d",
2515 directoryEntry: &inodeRW{
2516 inode: inode{
2517 data: []byte("Baz"),
2518 mode: fs.ModePerm,
2519 },
2520 },
2521 },
2522 },
2523 mode: fs.ModeDir | fs.ModePerm,
2524 },
2525 },
2526 },
2527 {
2528 name: "e",
2529 directoryEntry: &inodeRW{
2530 inode: inode{
2531 data: []byte("/a"),
2532 mode: fs.ModeSymlink | fs.ModePerm,
2533 },
2534 },
2535 },
2536 },
2537 mode: fs.ModeDir | fs.ModePerm,
2538 }),
2539 Path: "e",
2540 Output: []fs.DirEntry{
2541 &dirEnt{
2542 name: "b",
2543 directoryEntry: &inodeRW{
2544 inode: inode{
2545 data: []byte("Foo"),
2546 mode: fs.ModePerm,
2547 },
2548 },
2549 },
2550 &dirEnt{
2551 name: "c",
2552 directoryEntry: &inodeRW{
2553 inode: inode{
2554 data: []byte("Bar"),
2555 mode: fs.ModePerm,
2556 },
2557 },
2558 },
2559 &dirEnt{
2560 name: "d",
2561 directoryEntry: &inodeRW{
2562 inode: inode{
2563 data: []byte("Baz"),
2564 mode: fs.ModePerm,
2565 },
2566 },
2567 },
2568 },
2569 },
2570 } {
2571 de, err := test.FS.ReadDir(test.Path)
2572 if !errors.Is(err, test.Err) {
2573 t.Errorf("test %d: expecting error %s, got %s", n+1, test.Err, err)
2574 } else if !reflect.DeepEqual(test.Output, de) {
2575 t.Errorf("test %d: expecting to get %v, got %v", n+1, test.Output, de)
2576 }
2577 }
2578 }
2579
2580 func TestRemove(t *testing.T) {
2581 now := time.Now()
2582 for n, test := range [...]*struct {
2583 FS FS
2584 Path string
2585 Output FS
2586 Err error
2587 }{
2588 { // 1
2589 FS: newFSRW(dnode{}),
2590 Path: ".",
2591 Output: newFSRW(dnode{}),
2592 Err: &fs.PathError{
2593 Op: "remove",
2594 Path: ".",
2595 Err: fs.ErrInvalid,
2596 },
2597 },
2598 { // 2
2599 FS: newFSRW(dnode{
2600 mode: fs.ModeDir | fs.ModePerm,
2601 }),
2602 Path: "file",
2603 Output: newFSRW(dnode{
2604 mode: fs.ModeDir | fs.ModePerm,
2605 }),
2606 Err: &fs.PathError{
2607 Op: "remove",
2608 Path: "file",
2609 Err: fs.ErrNotExist,
2610 },
2611 },
2612 { // 3
2613 FS: newFSRW(dnode{
2614 entries: []*dirEnt{
2615 {
2616 directoryEntry: &inodeRW{},
2617 name: "file",
2618 },
2619 },
2620 mode: fs.ModeDir | fs.ModePerm,
2621 }),
2622 Path: "file",
2623 Output: newFSRW(dnode{
2624 entries: []*dirEnt{},
2625 modtime: now,
2626 mode: fs.ModeDir | fs.ModePerm,
2627 }),
2628 },
2629 { // 4
2630 FS: newFSRW(dnode{
2631 entries: []*dirEnt{
2632 {
2633 directoryEntry: &dnodeRW{
2634 dnode: dnode{
2635 mode: fs.ModeDir | fs.ModePerm,
2636 },
2637 },
2638 name: "dir",
2639 },
2640 },
2641 mode: fs.ModeDir | fs.ModePerm,
2642 }),
2643 Path: "dir",
2644 Output: newFSRW(dnode{
2645 entries: []*dirEnt{},
2646 modtime: now,
2647 mode: fs.ModeDir | fs.ModePerm,
2648 }),
2649 },
2650 { // 5
2651 FS: newFSRW(dnode{
2652 entries: []*dirEnt{
2653 {
2654 directoryEntry: &dnodeRW{
2655 dnode: dnode{
2656 entries: []*dirEnt{
2657 {
2658 directoryEntry: &inodeRW{},
2659 name: "file",
2660 },
2661 },
2662 mode: fs.ModeDir | fs.ModePerm,
2663 },
2664 },
2665 name: "dir",
2666 },
2667 },
2668 mode: fs.ModeDir | fs.ModePerm,
2669 }),
2670 Path: "dir",
2671 Output: newFSRW(dnode{
2672 entries: []*dirEnt{
2673 {
2674 directoryEntry: &dnodeRW{
2675 dnode: dnode{
2676 entries: []*dirEnt{
2677 {
2678 directoryEntry: &inodeRW{},
2679 name: "file",
2680 },
2681 },
2682 mode: fs.ModeDir | fs.ModePerm,
2683 },
2684 },
2685 name: "dir",
2686 },
2687 },
2688 mode: fs.ModeDir | fs.ModePerm,
2689 }),
2690 Err: &fs.PathError{
2691 Op: "remove",
2692 Path: "dir",
2693 Err: fs.ErrInvalid,
2694 },
2695 },
2696 } {
2697 if err := test.FS.Remove(test.Path); !reflect.DeepEqual(err, test.Err) {
2698 t.Errorf("test %d: expecting error %s, got %s", n+1, test.Err, err)
2699 } else {
2700 fixTimes(test.FS.de.(*dnodeRW), now)
2701
2702 if !reflect.DeepEqual(&test.Output, &test.FS) {
2703 t.Errorf("test %d: expecting to get FS %v, got %v", n+1, &test.Output, &test.FS)
2704 }
2705 }
2706 }
2707 }
2708
2709 func TestRemoveAll(t *testing.T) {
2710 now := time.Now()
2711 for n, test := range [...]*struct {
2712 FS FS
2713 Path string
2714 Output FS
2715 Err error
2716 }{
2717 { // 1
2718 FS: newFSRW(dnode{}),
2719 Path: ".",
2720 Output: newFSRW(dnode{}),
2721 Err: &fs.PathError{
2722 Op: "removeall",
2723 Path: ".",
2724 Err: fs.ErrPermission,
2725 },
2726 },
2727 { // 2
2728 FS: newFSRW(dnode{
2729 mode: fs.ModeDir | fs.ModePerm,
2730 }),
2731 Path: "file",
2732 Output: newFSRW(dnode{
2733 mode: fs.ModeDir | fs.ModePerm,
2734 }),
2735 Err: &fs.PathError{
2736 Op: "removeall",
2737 Path: "file",
2738 Err: fs.ErrNotExist,
2739 },
2740 },
2741 { // 3
2742 FS: newFSRW(dnode{
2743 entries: []*dirEnt{
2744 {
2745 directoryEntry: &inodeRW{},
2746 name: "file",
2747 },
2748 },
2749 mode: fs.ModeDir | fs.ModePerm,
2750 }),
2751 Path: "file",
2752 Output: newFSRW(dnode{
2753 entries: []*dirEnt{},
2754 modtime: now,
2755 mode: fs.ModeDir | fs.ModePerm,
2756 }),
2757 },
2758 { // 4
2759 FS: newFSRW(dnode{
2760 entries: []*dirEnt{
2761 {
2762 directoryEntry: &dnodeRW{
2763 dnode: dnode{
2764 mode: fs.ModeDir | fs.ModePerm,
2765 },
2766 },
2767 name: "dir",
2768 },
2769 },
2770 mode: fs.ModeDir | fs.ModePerm,
2771 }),
2772 Path: "dir",
2773 Output: newFSRW(dnode{
2774 entries: []*dirEnt{},
2775 modtime: now,
2776 mode: fs.ModeDir | fs.ModePerm,
2777 }),
2778 },
2779 { // 5
2780 FS: newFSRW(dnode{
2781 entries: []*dirEnt{
2782 {
2783 directoryEntry: &dnodeRW{
2784 dnode: dnode{
2785 entries: []*dirEnt{
2786 {
2787 directoryEntry: &inodeRW{},
2788 name: "file",
2789 },
2790 },
2791 mode: fs.ModeDir | fs.ModePerm,
2792 },
2793 },
2794 name: "dir",
2795 },
2796 },
2797 mode: fs.ModeDir | fs.ModePerm,
2798 }),
2799 Path: "dir",
2800 Output: newFSRW(dnode{
2801 entries: []*dirEnt{},
2802 modtime: now,
2803 mode: fs.ModeDir | fs.ModePerm,
2804 }),
2805 },
2806 } {
2807 if err := test.FS.RemoveAll(test.Path); !reflect.DeepEqual(err, test.Err) {
2808 t.Errorf("test %d: expecting error %s, got %s", n+1, test.Err, err)
2809 } else {
2810 fixTimes(test.FS.de.(*dnodeRW), now)
2811
2812 if !reflect.DeepEqual(&test.Output, &test.FS) {
2813 t.Errorf("test %d: expecting to get FS %v, got %v", n+1, &test.Output, &test.FS)
2814 }
2815 }
2816 }
2817 }
2818
2819 func TestLStatRW(t *testing.T) {
2820 for n, test := range [...]*struct {
2821 FS FS
2822 Path string
2823 Output fs.FileInfo
2824 Err error
2825 }{
2826 { // 1
2827 FS: newFSRW(dnode{}),
2828 Path: ".",
2829 Err: &fs.PathError{
2830 Op: "lstat",
2831 Path: ".",
2832 Err: fs.ErrPermission,
2833 },
2834 },
2835 { // 2
2836 FS: newFSRW(dnode{
2837 modtime: time.Unix(1, 2),
2838 mode: fs.ModeDir | fs.ModePerm,
2839 }),
2840 Path: ".",
2841 Output: &dirEnt{
2842 directoryEntry: &dnodeRW{
2843 dnode: dnode{
2844 modtime: time.Unix(1, 2),
2845 mode: fs.ModeDir | fs.ModePerm,
2846 },
2847 },
2848 name: "/",
2849 },
2850 },
2851 { // 3
2852 FS: newFSRW(dnode{
2853 mode: fs.ModeDir | fs.ModePerm,
2854 }),
2855 Path: "file",
2856 Err: &fs.PathError{
2857 Op: "lstat",
2858 Path: "file",
2859 Err: fs.ErrNotExist,
2860 },
2861 },
2862 { // 4
2863 FS: newFSRW(dnode{
2864 entries: []*dirEnt{
2865 {
2866 directoryEntry: &inodeRW{},
2867 name: "notFile",
2868 },
2869 },
2870 mode: fs.ModeDir | fs.ModePerm,
2871 }),
2872 Path: "file",
2873 Err: &fs.PathError{
2874 Op: "lstat",
2875 Path: "file",
2876 Err: fs.ErrNotExist,
2877 },
2878 },
2879 { // 5
2880 FS: newFSRW(dnode{
2881 entries: []*dirEnt{
2882 {
2883 directoryEntry: &inodeRW{
2884 inode: inode{
2885 modtime: time.Unix(1, 2),
2886 mode: fs.ModeSymlink | 3,
2887 },
2888 },
2889 name: "file",
2890 },
2891 },
2892 mode: fs.ModeDir | fs.ModePerm,
2893 }),
2894 Path: "file",
2895 Output: &dirEnt{
2896 directoryEntry: &inodeRW{
2897 inode: inode{
2898 modtime: time.Unix(1, 2),
2899 mode: fs.ModeSymlink | 3,
2900 },
2901 },
2902 name: "file",
2903 },
2904 },
2905 { // 6
2906 FS: newFSRW(dnode{
2907 entries: []*dirEnt{
2908 {
2909 directoryEntry: &inodeRW{
2910 inode: inode{
2911 modtime: time.Unix(1, 2),
2912 mode: 3,
2913 },
2914 },
2915 name: "file",
2916 },
2917 {
2918 directoryEntry: &dnodeRW{
2919 dnode: dnode{
2920 modtime: time.Unix(4, 5),
2921 mode: 6,
2922 },
2923 },
2924 name: "dir",
2925 },
2926 },
2927 mode: fs.ModeDir | fs.ModePerm,
2928 }),
2929 Path: "dir",
2930 Output: &dirEnt{
2931 directoryEntry: &dnodeRW{
2932 dnode: dnode{
2933 modtime: time.Unix(4, 5),
2934 mode: 6,
2935 },
2936 },
2937 name: "dir",
2938 },
2939 },
2940 { // 7
2941 FS: newFSRW(dnode{
2942 entries: []*dirEnt{
2943 {
2944 directoryEntry: &inodeRW{
2945 inode: inode{
2946 modtime: time.Unix(1, 2),
2947 mode: 3,
2948 },
2949 },
2950 name: "file",
2951 },
2952 {
2953 directoryEntry: &dnodeRW{
2954 dnode: dnode{
2955 entries: []*dirEnt{
2956 {
2957 directoryEntry: &inodeRW{
2958 inode: inode{
2959 modtime: time.Unix(4, 5),
2960 mode: 6,
2961 },
2962 },
2963 name: "anotherFile",
2964 },
2965 },
2966 modtime: time.Unix(7, 8),
2967 mode: 9,
2968 },
2969 },
2970 name: "dir",
2971 },
2972 },
2973 mode: fs.ModeDir | fs.ModePerm,
2974 }),
2975 Path: "dir/anotherFile",
2976 Err: &fs.PathError{
2977 Op: "lstat",
2978 Path: "dir/anotherFile",
2979 Err: fs.ErrPermission,
2980 },
2981 },
2982 { // 8
2983 FS: newFSRW(dnode{
2984 entries: []*dirEnt{
2985 {
2986 directoryEntry: &inodeRW{
2987 inode: inode{
2988 modtime: time.Unix(1, 2),
2989 mode: 3,
2990 },
2991 },
2992 name: "file",
2993 },
2994 {
2995 directoryEntry: &dnodeRW{
2996 dnode: dnode{
2997 entries: []*dirEnt{
2998 {
2999 directoryEntry: &inodeRW{
3000 inode: inode{
3001 modtime: time.Unix(4, 5),
3002 mode: fs.ModeSymlink | 6,
3003 },
3004 },
3005 name: "anotherFile",
3006 },
3007 },
3008 modtime: time.Unix(7, 8),
3009 mode: fs.ModeDir | fs.ModePerm,
3010 },
3011 },
3012 name: "dir",
3013 },
3014 },
3015 mode: fs.ModeDir | fs.ModePerm,
3016 }),
3017 Path: "dir/anotherFile",
3018 Output: &dirEnt{
3019 directoryEntry: &inodeRW{
3020 inode: inode{
3021 modtime: time.Unix(4, 5),
3022 mode: fs.ModeSymlink | 6,
3023 },
3024 },
3025 name: "anotherFile",
3026 },
3027 },
3028 } {
3029 if f, err := test.FS.LStat(test.Path); !reflect.DeepEqual(err, test.Err) {
3030 t.Errorf("test %d: expecting error %s, got %s", n+1, test.Err, err)
3031 } else if !reflect.DeepEqual(f, test.Output) {
3032 t.Errorf("test %d: expected FileInfo %v, got %v", n+1, test.Output, f)
3033 }
3034 }
3035 }
3036
3037 func TestReadlinkRW(t *testing.T) {
3038 for n, test := range [...]*struct {
3039 FS FS
3040 Path string
3041 Output string
3042 Err error
3043 }{
3044 { // 1
3045 FS: newFSRW(dnode{}),
3046 Path: ".",
3047 Err: &fs.PathError{
3048 Op: "readlink",
3049 Path: ".",
3050 Err: fs.ErrPermission,
3051 },
3052 },
3053 { // 2
3054 FS: newFSRW(dnode{
3055 modtime: time.Unix(1, 2),
3056 mode: fs.ModeDir | fs.ModePerm,
3057 }),
3058 Path: ".",
3059 Err: &fs.PathError{
3060 Op: "readlink",
3061 Path: ".",
3062 Err: fs.ErrInvalid,
3063 },
3064 },
3065 { // 3
3066 FS: newFSRW(dnode{
3067 modtime: time.Unix(1, 2),
3068 mode: fs.ModeDir | fs.ModePerm,
3069 }),
3070 Path: ".",
3071 Err: &fs.PathError{
3072 Op: "readlink",
3073 Path: ".",
3074 Err: fs.ErrInvalid,
3075 },
3076 },
3077 { // 4
3078 FS: newFSRW(dnode{
3079 entries: []*dirEnt{
3080 {
3081 directoryEntry: &inodeRW{},
3082 name: "a",
3083 },
3084 },
3085 modtime: time.Unix(1, 2),
3086 mode: fs.ModeDir | fs.ModePerm,
3087 }),
3088 Path: "a",
3089 Err: &fs.PathError{
3090 Op: "readlink",
3091 Path: "a",
3092 Err: fs.ErrInvalid,
3093 },
3094 },
3095 { // 5
3096 FS: newFSRW(dnode{
3097 entries: []*dirEnt{
3098 {
3099 directoryEntry: &inodeRW{
3100 inode: inode{
3101 mode: fs.ModeSymlink,
3102 },
3103 },
3104 name: "a",
3105 },
3106 },
3107 modtime: time.Unix(1, 2),
3108 mode: fs.ModeDir | fs.ModePerm,
3109 }),
3110 Path: "a",
3111 Err: &fs.PathError{
3112 Op: "readlink",
3113 Path: "a",
3114 Err: fs.ErrPermission,
3115 },
3116 },
3117 { // 6
3118 FS: newFSRW(dnode{
3119 entries: []*dirEnt{
3120 {
3121 directoryEntry: &inodeRW{
3122 inode: inode{
3123 mode: fs.ModeSymlink | fs.ModePerm,
3124 },
3125 },
3126 name: "a",
3127 },
3128 },
3129 modtime: time.Unix(1, 2),
3130 mode: fs.ModeDir | fs.ModePerm,
3131 }),
3132 Path: "a",
3133 },
3134 { // 7
3135 FS: newFSRW(dnode{
3136 entries: []*dirEnt{
3137 {
3138 directoryEntry: &inodeRW{
3139 inode: inode{
3140 data: []byte("/other/path"),
3141 mode: fs.ModeSymlink | fs.ModePerm,
3142 },
3143 },
3144 name: "a",
3145 },
3146 },
3147 modtime: time.Unix(1, 2),
3148 mode: fs.ModeDir | fs.ModePerm,
3149 }),
3150 Path: "a",
3151 Output: "/other/path",
3152 },
3153 } {
3154 if f, err := test.FS.Readlink(test.Path); !reflect.DeepEqual(err, test.Err) {
3155 t.Errorf("test %d: expecting error %s, got %s", n+1, test.Err, err)
3156 } else if !reflect.DeepEqual(f, test.Output) {
3157 t.Errorf("test %d: expected Link %v, got %v", n+1, test.Output, f)
3158 }
3159 }
3160 }
3161
3162 func TestChown(t *testing.T) {
3163 for n, test := range [...]*struct {
3164 FS FS
3165 Path string
3166 Err error
3167 }{
3168 { // 1
3169 FS: newFSRW(dnode{}),
3170 Path: ".",
3171 Err: &fs.PathError{
3172 Op: "chown",
3173 Path: ".",
3174 Err: fs.ErrPermission,
3175 },
3176 },
3177 { // 2
3178 FS: newFSRW(dnode{
3179 modtime: time.Unix(1, 2),
3180 mode: fs.ModeDir | fs.ModePerm,
3181 }),
3182 Path: ".",
3183 },
3184 { // 3
3185 FS: newFSRW(dnode{
3186 modtime: time.Unix(1, 2),
3187 mode: fs.ModeDir | fs.ModePerm,
3188 }),
3189 Path: "a",
3190 Err: &fs.PathError{
3191 Op: "chown",
3192 Path: "a",
3193 Err: fs.ErrNotExist,
3194 },
3195 },
3196 { // 5
3197 FS: newFSRW(dnode{
3198 entries: []*dirEnt{
3199 {
3200 directoryEntry: &inodeRW{},
3201 name: "a",
3202 },
3203 },
3204 modtime: time.Unix(1, 2),
3205 mode: fs.ModeDir | fs.ModePerm,
3206 }),
3207 Path: "a",
3208 },
3209 { // 6
3210 FS: newFSRW(dnode{
3211 entries: []*dirEnt{
3212 {
3213 directoryEntry: &dnodeRW{},
3214 name: "a",
3215 },
3216 },
3217 modtime: time.Unix(1, 2),
3218 mode: fs.ModeDir | fs.ModePerm,
3219 }),
3220 Path: "a",
3221 },
3222 { // 7
3223 FS: newFSRW(dnode{
3224 entries: []*dirEnt{
3225 {
3226 directoryEntry: &inodeRW{
3227 inode: inode{
3228 data: []byte("/b"),
3229 mode: fs.ModeSymlink | fs.ModePerm,
3230 },
3231 },
3232 name: "a",
3233 },
3234 },
3235 modtime: time.Unix(1, 2),
3236 mode: fs.ModeDir | fs.ModePerm,
3237 }),
3238 Path: "a",
3239 Err: &fs.PathError{
3240 Op: "chown",
3241 Path: "a",
3242 Err: fs.ErrNotExist,
3243 },
3244 },
3245 { // 8
3246 FS: newFSRW(dnode{
3247 entries: []*dirEnt{
3248 {
3249 directoryEntry: &inodeRW{
3250 inode: inode{
3251 data: []byte("/b"),
3252 mode: fs.ModeSymlink | fs.ModePerm,
3253 },
3254 },
3255 name: "a",
3256 },
3257 {
3258 directoryEntry: &inodeRW{},
3259 name: "b",
3260 },
3261 },
3262 modtime: time.Unix(3, 4),
3263 mode: fs.ModeDir | fs.ModePerm,
3264 }),
3265 Path: "a",
3266 },
3267 } {
3268 if err := test.FS.Chown(test.Path, 0, 0); !reflect.DeepEqual(err, test.Err) {
3269 t.Errorf("test %d: expecting error %s, got %s", n+1, test.Err, err)
3270 }
3271 }
3272 }
3273
3274 func TestChmod(t *testing.T) {
3275 for n, test := range [...]*struct {
3276 FS FS
3277 Path string
3278 Mode fs.FileMode
3279 Output FS
3280 Err error
3281 }{
3282 { // 1
3283 FS: newFSRW(dnode{}),
3284 Path: ".",
3285 Output: newFSRW(dnode{}),
3286 Err: &fs.PathError{
3287 Op: "chmod",
3288 Path: ".",
3289 Err: fs.ErrPermission,
3290 },
3291 },
3292 { // 2
3293 FS: newFSRW(dnode{
3294 modtime: time.Unix(1, 2),
3295 mode: fs.ModeDir | fs.ModePerm,
3296 }),
3297 Path: ".",
3298 Output: newFSRW(dnode{
3299 modtime: time.Unix(1, 2),
3300 mode: fs.ModeDir,
3301 }),
3302 },
3303 { // 3
3304 FS: newFSRW(dnode{
3305 entries: []*dirEnt{
3306 {
3307 directoryEntry: &inodeRW{},
3308 name: "a",
3309 },
3310 },
3311 modtime: time.Unix(1, 2),
3312 mode: fs.ModeDir | fs.ModePerm,
3313 }),
3314 Path: "a",
3315 Mode: 0o123,
3316 Output: newFSRW(dnode{
3317 entries: []*dirEnt{
3318 {
3319 directoryEntry: &inodeRW{
3320 inode: inode{
3321 mode: 0o123,
3322 },
3323 },
3324 name: "a",
3325 },
3326 },
3327 modtime: time.Unix(1, 2),
3328 mode: fs.ModeDir | fs.ModePerm,
3329 }),
3330 },
3331 { // 4
3332 FS: newFSRW(dnode{
3333 entries: []*dirEnt{
3334 {
3335 directoryEntry: &dnodeRW{
3336 dnode: dnode{
3337 mode: fs.ModeDir,
3338 },
3339 },
3340 name: "a",
3341 },
3342 },
3343 modtime: time.Unix(1, 2),
3344 mode: fs.ModeDir | fs.ModePerm,
3345 }),
3346 Path: "a",
3347 Mode: 0o123,
3348 Output: newFSRW(dnode{
3349 entries: []*dirEnt{
3350 {
3351 directoryEntry: &dnodeRW{
3352 dnode: dnode{
3353 mode: fs.ModeDir | 0o123,
3354 },
3355 },
3356 name: "a",
3357 },
3358 },
3359 modtime: time.Unix(1, 2),
3360 mode: fs.ModeDir | fs.ModePerm,
3361 }),
3362 },
3363 { // 5
3364 FS: newFSRW(dnode{
3365 entries: []*dirEnt{
3366 {
3367 directoryEntry: &inodeRW{
3368 inode: inode{
3369 data: []byte("/b"),
3370 mode: fs.ModeSymlink,
3371 },
3372 },
3373 name: "a",
3374 },
3375 },
3376 modtime: time.Unix(1, 2),
3377 mode: fs.ModeDir | fs.ModePerm,
3378 }),
3379 Path: "a",
3380 Mode: 0o123,
3381 Output: newFSRW(dnode{
3382 entries: []*dirEnt{
3383 {
3384 directoryEntry: &inodeRW{
3385 inode: inode{
3386 data: []byte("/b"),
3387 mode: fs.ModeSymlink,
3388 },
3389 },
3390 name: "a",
3391 },
3392 },
3393 modtime: time.Unix(1, 2),
3394 mode: fs.ModeDir | fs.ModePerm,
3395 }),
3396 Err: &fs.PathError{
3397 Op: "chmod",
3398 Path: "a",
3399 Err: fs.ErrPermission,
3400 },
3401 },
3402 { // 6
3403 FS: newFSRW(dnode{
3404 entries: []*dirEnt{
3405 {
3406 directoryEntry: &inodeRW{
3407 inode: inode{
3408 data: []byte("/b"),
3409 mode: fs.ModeSymlink | fs.ModePerm,
3410 },
3411 },
3412 name: "a",
3413 },
3414 {
3415 directoryEntry: &inodeRW{},
3416 name: "b",
3417 },
3418 },
3419 modtime: time.Unix(1, 2),
3420 mode: fs.ModeDir | fs.ModePerm,
3421 }),
3422 Path: "a",
3423 Mode: 0o123,
3424 Output: newFSRW(dnode{
3425 entries: []*dirEnt{
3426 {
3427 directoryEntry: &inodeRW{
3428 inode: inode{
3429 data: []byte("/b"),
3430 mode: fs.ModeSymlink | fs.ModePerm,
3431 },
3432 },
3433 name: "a",
3434 },
3435 {
3436 directoryEntry: &inodeRW{
3437 inode: inode{
3438 mode: 0o123,
3439 },
3440 },
3441 name: "b",
3442 },
3443 },
3444 modtime: time.Unix(1, 2),
3445 mode: fs.ModeDir | fs.ModePerm,
3446 }),
3447 },
3448 } {
3449 if err := test.FS.Chmod(test.Path, test.Mode); !reflect.DeepEqual(err, test.Err) {
3450 t.Errorf("test %d: expecting error %s, got %s", n+1, test.Err, err)
3451 } else if !reflect.DeepEqual(&test.FS, &test.Output) {
3452 t.Errorf("test %d: expected %v, got %v", n+1, &test.Output, &test.FS)
3453 }
3454 }
3455 }
3456
3457 func TestLchown(t *testing.T) {
3458 for n, test := range [...]*struct {
3459 FS FS
3460 Path string
3461 Err error
3462 }{
3463 { // 1
3464 FS: newFSRW(dnode{}),
3465 Path: ".",
3466 Err: &fs.PathError{
3467 Op: "lchown",
3468 Path: ".",
3469 Err: fs.ErrPermission,
3470 },
3471 },
3472 { // 2
3473 FS: newFSRW(dnode{
3474 modtime: time.Unix(1, 2),
3475 mode: fs.ModeDir | fs.ModePerm,
3476 }),
3477 Path: ".",
3478 },
3479 { // 3
3480 FS: newFSRW(dnode{
3481 modtime: time.Unix(1, 2),
3482 mode: fs.ModeDir | fs.ModePerm,
3483 }),
3484 Path: "a",
3485 Err: &fs.PathError{
3486 Op: "lchown",
3487 Path: "a",
3488 Err: fs.ErrNotExist,
3489 },
3490 },
3491 { // 5
3492 FS: newFSRW(dnode{
3493 entries: []*dirEnt{
3494 {
3495 directoryEntry: &inodeRW{},
3496 name: "a",
3497 },
3498 },
3499 modtime: time.Unix(1, 2),
3500 mode: fs.ModeDir | fs.ModePerm,
3501 }),
3502 Path: "a",
3503 },
3504 { // 6
3505 FS: newFSRW(dnode{
3506 entries: []*dirEnt{
3507 {
3508 directoryEntry: &dnodeRW{},
3509 name: "a",
3510 },
3511 },
3512 modtime: time.Unix(1, 2),
3513 mode: fs.ModeDir | fs.ModePerm,
3514 }),
3515 Path: "a",
3516 },
3517 { // 7
3518 FS: newFSRW(dnode{
3519 entries: []*dirEnt{
3520 {
3521 directoryEntry: &inodeRW{
3522 inode: inode{
3523 data: []byte("/b"),
3524 mode: fs.ModeSymlink | fs.ModePerm,
3525 },
3526 },
3527 name: "a",
3528 },
3529 },
3530 modtime: time.Unix(1, 2),
3531 mode: fs.ModeDir | fs.ModePerm,
3532 }),
3533 Path: "a",
3534 },
3535 } {
3536 if err := test.FS.Lchown(test.Path, 0, 0); !reflect.DeepEqual(err, test.Err) {
3537 t.Errorf("test %d: expecting error %s, got %s", n+1, test.Err, err)
3538 }
3539 }
3540 }
3541
3542 func TestChtimes(t *testing.T) {
3543 for n, test := range [...]*struct {
3544 FS FS
3545 Path string
3546 MTime time.Time
3547 Output FS
3548 Err error
3549 }{
3550 { // 1
3551 FS: newFSRW(dnode{}),
3552 Path: ".",
3553 Output: newFSRW(dnode{}),
3554 Err: &fs.PathError{
3555 Op: "chtimes",
3556 Path: ".",
3557 Err: fs.ErrPermission,
3558 },
3559 },
3560 { // 2
3561 FS: newFSRW(dnode{
3562 modtime: time.Unix(1, 2),
3563 mode: fs.ModeDir | fs.ModePerm,
3564 }),
3565 Path: ".",
3566 Output: newFSRW(dnode{
3567 mode: fs.ModeDir | fs.ModePerm,
3568 }),
3569 },
3570 { // 3
3571 FS: newFSRW(dnode{
3572 modtime: time.Unix(1, 2),
3573 mode: fs.ModeDir | fs.ModePerm,
3574 }),
3575 Path: ".",
3576 MTime: time.Unix(3, 4),
3577 Output: newFSRW(dnode{
3578 modtime: time.Unix(3, 4),
3579 mode: fs.ModeDir | fs.ModePerm,
3580 }),
3581 },
3582 { // 4
3583 FS: newFSRW(dnode{
3584 modtime: time.Unix(1, 2),
3585 mode: fs.ModeDir | fs.ModePerm,
3586 }),
3587 Path: "a",
3588 MTime: time.Unix(3, 4),
3589 Output: newFSRW(dnode{
3590 modtime: time.Unix(1, 2),
3591 mode: fs.ModeDir | fs.ModePerm,
3592 }),
3593 Err: &fs.PathError{
3594 Op: "chtimes",
3595 Path: "a",
3596 Err: fs.ErrNotExist,
3597 },
3598 },
3599 { // 5
3600 FS: newFSRW(dnode{
3601 entries: []*dirEnt{
3602 {
3603 directoryEntry: &inodeRW{
3604 inode: inode{
3605 modtime: time.Unix(3, 4),
3606 },
3607 },
3608 name: "a",
3609 },
3610 },
3611 modtime: time.Unix(1, 2),
3612 mode: fs.ModeDir | fs.ModePerm,
3613 }),
3614 Path: "a",
3615 MTime: time.Unix(5, 6),
3616 Output: newFSRW(dnode{
3617 entries: []*dirEnt{
3618 {
3619 directoryEntry: &inodeRW{
3620 inode: inode{
3621 modtime: time.Unix(5, 6),
3622 },
3623 },
3624 name: "a",
3625 },
3626 },
3627 modtime: time.Unix(1, 2),
3628 mode: fs.ModeDir | fs.ModePerm,
3629 }),
3630 },
3631 { // 6
3632 FS: newFSRW(dnode{
3633 entries: []*dirEnt{
3634 {
3635 directoryEntry: &dnodeRW{
3636 dnode: dnode{
3637 modtime: time.Unix(3, 4),
3638 },
3639 },
3640 name: "a",
3641 },
3642 },
3643 modtime: time.Unix(1, 2),
3644 mode: fs.ModeDir | fs.ModePerm,
3645 }),
3646 Path: "a",
3647 MTime: time.Unix(5, 6),
3648 Output: newFSRW(dnode{
3649 entries: []*dirEnt{
3650 {
3651 directoryEntry: &dnodeRW{
3652 dnode: dnode{
3653 modtime: time.Unix(5, 6),
3654 },
3655 },
3656 name: "a",
3657 },
3658 },
3659 modtime: time.Unix(1, 2),
3660 mode: fs.ModeDir | fs.ModePerm,
3661 }),
3662 },
3663 { // 7
3664 FS: newFSRW(dnode{
3665 entries: []*dirEnt{
3666 {
3667 directoryEntry: &inodeRW{
3668 inode: inode{
3669 data: []byte("/b"),
3670 mode: fs.ModeSymlink | 0o444,
3671 modtime: time.Unix(3, 4),
3672 },
3673 },
3674 name: "a",
3675 },
3676 {
3677 directoryEntry: &dnodeRW{
3678 dnode: dnode{
3679 modtime: time.Unix(5, 6),
3680 },
3681 },
3682 name: "b",
3683 },
3684 },
3685 modtime: time.Unix(1, 2),
3686 mode: fs.ModeDir | fs.ModePerm,
3687 }),
3688 Path: "a",
3689 MTime: time.Unix(7, 8),
3690 Output: newFSRW(dnode{
3691 entries: []*dirEnt{
3692 {
3693 directoryEntry: &inodeRW{
3694 inode: inode{
3695 data: []byte("/b"),
3696 mode: fs.ModeSymlink | 0o444,
3697 modtime: time.Unix(3, 4),
3698 },
3699 },
3700 name: "a",
3701 },
3702 {
3703 directoryEntry: &dnodeRW{
3704 dnode: dnode{
3705 modtime: time.Unix(7, 8),
3706 },
3707 },
3708 name: "b",
3709 },
3710 },
3711 modtime: time.Unix(1, 2),
3712 mode: fs.ModeDir | fs.ModePerm,
3713 }),
3714 },
3715 } {
3716 if err := test.FS.Chtimes(test.Path, time.Time{}, test.MTime); !reflect.DeepEqual(err, test.Err) {
3717 t.Errorf("test %d: expecting error %s, got %s", n+1, test.Err, err)
3718 } else if !reflect.DeepEqual(&test.FS, &test.Output) {
3719 t.Errorf("test %d: expected %v, got %v", n+1, &test.Output, &test.FS)
3720 }
3721 }
3722 }
3723
3724 func TestLchtimes(t *testing.T) {
3725 for n, test := range [...]*struct {
3726 FS FS
3727 Path string
3728 MTime time.Time
3729 Output FS
3730 Err error
3731 }{
3732 { // 1
3733 FS: newFSRW(dnode{}),
3734 Path: ".",
3735 Output: newFSRW(dnode{}),
3736 Err: &fs.PathError{
3737 Op: "lchtimes",
3738 Path: ".",
3739 Err: fs.ErrPermission,
3740 },
3741 },
3742 { // 2
3743 FS: newFSRW(dnode{
3744 modtime: time.Unix(1, 2),
3745 mode: fs.ModeDir | fs.ModePerm,
3746 }),
3747 Path: ".",
3748 Output: newFSRW(dnode{
3749 mode: fs.ModeDir | fs.ModePerm,
3750 }),
3751 },
3752 { // 3
3753 FS: newFSRW(dnode{
3754 modtime: time.Unix(1, 2),
3755 mode: fs.ModeDir | fs.ModePerm,
3756 }),
3757 Path: ".",
3758 MTime: time.Unix(3, 4),
3759 Output: newFSRW(dnode{
3760 modtime: time.Unix(3, 4),
3761 mode: fs.ModeDir | fs.ModePerm,
3762 }),
3763 },
3764 { // 4
3765 FS: newFSRW(dnode{
3766 modtime: time.Unix(1, 2),
3767 mode: fs.ModeDir | fs.ModePerm,
3768 }),
3769 Path: "a",
3770 MTime: time.Unix(3, 4),
3771 Output: newFSRW(dnode{
3772 modtime: time.Unix(1, 2),
3773 mode: fs.ModeDir | fs.ModePerm,
3774 }),
3775 Err: &fs.PathError{
3776 Op: "lchtimes",
3777 Path: "a",
3778 Err: fs.ErrNotExist,
3779 },
3780 },
3781 { // 5
3782 FS: newFSRW(dnode{
3783 entries: []*dirEnt{
3784 {
3785 directoryEntry: &inodeRW{
3786 inode: inode{
3787 modtime: time.Unix(3, 4),
3788 },
3789 },
3790 name: "a",
3791 },
3792 },
3793 modtime: time.Unix(1, 2),
3794 mode: fs.ModeDir | fs.ModePerm,
3795 }),
3796 Path: "a",
3797 MTime: time.Unix(5, 6),
3798 Output: newFSRW(dnode{
3799 entries: []*dirEnt{
3800 {
3801 directoryEntry: &inodeRW{
3802 inode: inode{
3803 modtime: time.Unix(5, 6),
3804 },
3805 },
3806 name: "a",
3807 },
3808 },
3809 modtime: time.Unix(1, 2),
3810 mode: fs.ModeDir | fs.ModePerm,
3811 }),
3812 },
3813 { // 6
3814 FS: newFSRW(dnode{
3815 entries: []*dirEnt{
3816 {
3817 directoryEntry: &dnodeRW{
3818 dnode: dnode{
3819 modtime: time.Unix(3, 4),
3820 },
3821 },
3822 name: "a",
3823 },
3824 },
3825 modtime: time.Unix(1, 2),
3826 mode: fs.ModeDir | fs.ModePerm,
3827 }),
3828 Path: "a",
3829 MTime: time.Unix(5, 6),
3830 Output: newFSRW(dnode{
3831 entries: []*dirEnt{
3832 {
3833 directoryEntry: &dnodeRW{
3834 dnode: dnode{
3835 modtime: time.Unix(5, 6),
3836 },
3837 },
3838 name: "a",
3839 },
3840 },
3841 modtime: time.Unix(1, 2),
3842 mode: fs.ModeDir | fs.ModePerm,
3843 }),
3844 },
3845 { // 7
3846 FS: newFSRW(dnode{
3847 entries: []*dirEnt{
3848 {
3849 directoryEntry: &inodeRW{
3850 inode: inode{
3851 data: []byte("/b"),
3852 mode: fs.ModeSymlink | 0o444,
3853 modtime: time.Unix(3, 4),
3854 },
3855 },
3856 name: "a",
3857 },
3858 {
3859 directoryEntry: &dnodeRW{
3860 dnode: dnode{
3861 modtime: time.Unix(5, 6),
3862 },
3863 },
3864 name: "b",
3865 },
3866 },
3867 modtime: time.Unix(1, 2),
3868 mode: fs.ModeDir | fs.ModePerm,
3869 }),
3870 Path: "a",
3871 MTime: time.Unix(7, 8),
3872 Output: newFSRW(dnode{
3873 entries: []*dirEnt{
3874 {
3875 directoryEntry: &inodeRW{
3876 inode: inode{
3877 data: []byte("/b"),
3878 mode: fs.ModeSymlink | 0o444,
3879 modtime: time.Unix(7, 8),
3880 },
3881 },
3882 name: "a",
3883 },
3884 {
3885 directoryEntry: &dnodeRW{
3886 dnode: dnode{
3887 modtime: time.Unix(5, 6),
3888 },
3889 },
3890 name: "b",
3891 },
3892 },
3893 modtime: time.Unix(1, 2),
3894 mode: fs.ModeDir | fs.ModePerm,
3895 }),
3896 },
3897 } {
3898 if err := test.FS.Lchtimes(test.Path, time.Time{}, test.MTime); !reflect.DeepEqual(err, test.Err) {
3899 t.Errorf("test %d: expecting error %s, got %s", n+1, test.Err, err)
3900 } else if !reflect.DeepEqual(&test.FS, &test.Output) {
3901 t.Errorf("test %d: expected %v, got %v", n+1, &test.Output, &test.FS)
3902 }
3903 }
3904 }
3905
3906 func TestSeal(t *testing.T) {
3907 input := FS{
3908 fsRO: fsRO{
3909 de: &dnodeRW{
3910 dnode: dnode{
3911 modtime: time.Unix(1, 2),
3912 mode: 0,
3913 entries: []*dirEnt{
3914 {
3915 name: "a",
3916 directoryEntry: &inodeRW{
3917 inode: inode{
3918 modtime: time.Unix(3, 4),
3919 mode: 1,
3920 data: []byte("Foo"),
3921 },
3922 },
3923 },
3924 {
3925 name: "b",
3926 directoryEntry: &dnodeRW{
3927 dnode: dnode{
3928 modtime: time.Unix(5, 6),
3929 mode: 2,
3930 entries: []*dirEnt{
3931 {
3932 name: "c",
3933 directoryEntry: &inodeRW{
3934 inode: inode{
3935 modtime: time.Unix(7, 8),
3936 mode: 3,
3937 data: []byte("Hello"),
3938 },
3939 },
3940 },
3941 {
3942 name: "d",
3943 directoryEntry: &inodeRW{
3944 inode: inode{
3945 modtime: time.Unix(9, 10),
3946 mode: 4,
3947 data: []byte("World"),
3948 },
3949 },
3950 },
3951 },
3952 },
3953 },
3954 },
3955 },
3956 },
3957 },
3958 },
3959 }
3960 expected := fsRO{
3961 de: &dnode{
3962 modtime: time.Unix(1, 2),
3963 mode: 0,
3964 entries: []*dirEnt{
3965 {
3966 name: "a",
3967 directoryEntry: &inode{
3968 modtime: time.Unix(3, 4),
3969 mode: 1,
3970 data: []byte("Foo"),
3971 },
3972 },
3973 {
3974 name: "b",
3975 directoryEntry: &dnode{
3976 modtime: time.Unix(5, 6),
3977 mode: 2,
3978 entries: []*dirEnt{
3979 {
3980 name: "c",
3981 directoryEntry: &inode{
3982 modtime: time.Unix(7, 8),
3983 mode: 3,
3984 data: []byte("Hello"),
3985 },
3986 },
3987 {
3988 name: "d",
3989 directoryEntry: &inode{
3990 modtime: time.Unix(9, 10),
3991 mode: 4,
3992 data: []byte("World"),
3993 },
3994 },
3995 },
3996 },
3997 },
3998 },
3999 },
4000 }
4001
4002 output := input.Seal()
4003
4004 if !reflect.DeepEqual(&expected, output) {
4005 t.Errorf("output did not match expected")
4006 }
4007 }
4008
4009 func TestSubRW(t *testing.T) {
4010 for n, test := range [...]*struct {
4011 FS FS
4012 Path string
4013 Output fs.FS
4014 Err error
4015 }{
4016 { // 1
4017 FS: FS{
4018 fsRO: fsRO{
4019 de: &dnodeRW{},
4020 },
4021 },
4022 Path: ".",
4023 Err: &fs.PathError{
4024 Op: "sub",
4025 Path: ".",
4026 Err: fs.ErrPermission,
4027 },
4028 },
4029 { // 2
4030 FS: FS{
4031 fsRO: fsRO{
4032 de: &dnodeRW{
4033 dnode: dnode{
4034 modtime: time.Unix(1, 2),
4035 mode: fs.ModeDir | 0x444,
4036 entries: []*dirEnt{
4037 {
4038 name: "a",
4039 directoryEntry: &inode{
4040 modtime: time.Unix(3, 4),
4041 mode: 1,
4042 data: []byte("Foo"),
4043 },
4044 },
4045 {
4046 name: "b",
4047 directoryEntry: &dnodeRW{
4048 dnode: dnode{
4049 modtime: time.Unix(5, 6),
4050 mode: fs.ModeDir | 0x444,
4051 entries: []*dirEnt{
4052 {
4053 name: "c",
4054 directoryEntry: &inodeRW{
4055 inode: inode{
4056 modtime: time.Unix(7, 8),
4057 mode: 3,
4058 data: []byte("Hello"),
4059 },
4060 },
4061 },
4062 {
4063 name: "d",
4064 directoryEntry: &inodeRW{
4065 inode: inode{
4066 modtime: time.Unix(9, 10),
4067 mode: 4,
4068 data: []byte("World"),
4069 },
4070 },
4071 },
4072 },
4073 },
4074 },
4075 },
4076 },
4077 },
4078 },
4079 },
4080 },
4081 Path: "b",
4082 Output: &FS{
4083 fsRO: fsRO{
4084 de: &dnodeRW{
4085 dnode: dnode{
4086 modtime: time.Unix(5, 6),
4087 mode: fs.ModeDir | 0x444,
4088 entries: []*dirEnt{
4089 {
4090 name: "c",
4091 directoryEntry: &inodeRW{
4092 inode: inode{
4093 modtime: time.Unix(7, 8),
4094 mode: 3,
4095 data: []byte("Hello"),
4096 },
4097 },
4098 },
4099 {
4100 name: "d",
4101 directoryEntry: &inodeRW{
4102 inode: inode{
4103 modtime: time.Unix(9, 10),
4104 mode: 4,
4105 data: []byte("World"),
4106 },
4107 },
4108 },
4109 },
4110 },
4111 },
4112 },
4113 },
4114 },
4115 { // 3
4116 FS: FS{
4117 fsRO: fsRO{
4118 de: &dnodeRW{
4119 dnode: dnode{
4120 modtime: time.Unix(1, 2),
4121 mode: fs.ModeDir | 0x444,
4122 entries: []*dirEnt{
4123 {
4124 name: "a",
4125 directoryEntry: &inodeRW{
4126 inode: inode{
4127 modtime: time.Unix(3, 4),
4128 mode: 1,
4129 data: []byte("Foo"),
4130 },
4131 },
4132 },
4133 {
4134 name: "b",
4135 directoryEntry: &dnodeRW{
4136 dnode: dnode{
4137 modtime: time.Unix(5, 6),
4138 mode: fs.ModeDir | 0x444,
4139 entries: []*dirEnt{
4140 {
4141 name: "c",
4142 directoryEntry: &inodeRW{
4143 inode: inode{
4144 modtime: time.Unix(7, 8),
4145 mode: 3,
4146 data: []byte("Hello"),
4147 },
4148 },
4149 },
4150 {
4151 name: "d",
4152 directoryEntry: &inodeRW{
4153 inode: inode{
4154 modtime: time.Unix(9, 10),
4155 mode: 4,
4156 data: []byte("World"),
4157 },
4158 },
4159 },
4160 },
4161 },
4162 },
4163 },
4164 },
4165 },
4166 },
4167 },
4168 },
4169 Path: "a",
4170 Err: &fs.PathError{
4171 Op: "sub",
4172 Path: "a",
4173 Err: fs.ErrInvalid,
4174 },
4175 },
4176 } {
4177 if output, err := test.FS.Sub(test.Path); !reflect.DeepEqual(err, test.Err) {
4178 t.Errorf("test %d: expecting error %s, got %s", n+1, test.Err, err)
4179 } else if !reflect.DeepEqual(output, test.Output) {
4180 t.Errorf("test %d: expected FS %v, got %v", n+1, test.Output, output)
4181 }
4182 }
4183 }
4184
4185 func TestSubRWRemove(t *testing.T) {
4186 for n, test := range [...]*struct {
4187 FS FS
4188 Path string
4189 Rm string
4190 Output fs.FS
4191 Err error
4192 }{
4193 { // 1
4194 FS: FS{
4195 fsRO: fsRO{
4196 de: &dnodeRW{},
4197 },
4198 },
4199 Path: ".",
4200 Err: &fs.PathError{
4201 Op: "sub",
4202 Path: ".",
4203 Err: fs.ErrPermission,
4204 },
4205 },
4206 { // 2
4207 FS: FS{
4208 fsRO: fsRO{
4209 de: &dnodeRW{
4210 dnode: dnode{
4211 modtime: time.Unix(1, 2),
4212 mode: fs.ModeDir | 0x444,
4213 entries: []*dirEnt{
4214 {
4215 name: "a",
4216 directoryEntry: &inode{
4217 modtime: time.Unix(3, 4),
4218 mode: 1,
4219 data: []byte("Foo"),
4220 },
4221 },
4222 {
4223 name: "b",
4224 directoryEntry: &dnodeRW{
4225 dnode: dnode{
4226 modtime: time.Unix(5, 6),
4227 mode: fs.ModeDir | 0x777,
4228 entries: []*dirEnt{
4229 {
4230 name: "c",
4231 directoryEntry: &inodeRW{
4232 inode: inode{
4233 modtime: time.Unix(7, 8),
4234 mode: 3,
4235 data: []byte("Hello"),
4236 },
4237 },
4238 },
4239 {
4240 name: "d",
4241 directoryEntry: &inodeRW{
4242 inode: inode{
4243 modtime: time.Unix(9, 10),
4244 mode: 4,
4245 data: []byte("World"),
4246 },
4247 },
4248 },
4249 },
4250 },
4251 },
4252 },
4253 },
4254 },
4255 },
4256 },
4257 },
4258 Path: "b",
4259 Rm: "d",
4260 Output: &FS{
4261 fsRO: fsRO{
4262 de: &dnodeRW{
4263 dnode: dnode{
4264 modtime: time.Unix(5, 6),
4265 mode: fs.ModeDir | 0x777,
4266 entries: []*dirEnt{
4267 {
4268 name: "c",
4269 directoryEntry: &inodeRW{
4270 inode: inode{
4271 modtime: time.Unix(7, 8),
4272 mode: 3,
4273 data: []byte("Hello"),
4274 },
4275 },
4276 },
4277 },
4278 },
4279 },
4280 },
4281 },
4282 },
4283 } {
4284 if output, err := test.FS.Sub(test.Path); !reflect.DeepEqual(err, test.Err) {
4285 t.Errorf("test %d: expecting error %s, got %s", n+1, test.Err, err)
4286 } else if fs, ok := output.(*FS); ok {
4287 if err = fs.Remove(test.Rm); err != nil {
4288 t.Errorf("test %d: unexpected error %s", n+1, err)
4289 } else {
4290 fs.de.(*dnodeRW).modtime = time.Unix(5, 6)
4291
4292 if !reflect.DeepEqual(fs, test.Output) {
4293 t.Errorf("test %d: expected FS %v, got %v", n+1, test.Output, fs)
4294 }
4295 }
4296 }
4297 }
4298 }
4299
4300 func TestSubRWRemoveAll(t *testing.T) {
4301 for n, test := range [...]*struct {
4302 FS FS
4303 Path string
4304 Rm string
4305 Output fs.FS
4306 Err error
4307 }{
4308 { // 1
4309 FS: FS{
4310 fsRO: fsRO{
4311 de: &dnodeRW{},
4312 },
4313 },
4314 Path: ".",
4315 Err: &fs.PathError{
4316 Op: "sub",
4317 Path: ".",
4318 Err: fs.ErrPermission,
4319 },
4320 },
4321 { // 2
4322 FS: FS{
4323 fsRO: fsRO{
4324 de: &dnodeRW{
4325 dnode: dnode{
4326 modtime: time.Unix(1, 2),
4327 mode: fs.ModeDir | 0x444,
4328 entries: []*dirEnt{
4329 {
4330 name: "a",
4331 directoryEntry: &inode{
4332 modtime: time.Unix(3, 4),
4333 mode: 1,
4334 data: []byte("Foo"),
4335 },
4336 },
4337 {
4338 name: "b",
4339 directoryEntry: &dnodeRW{
4340 dnode: dnode{
4341 modtime: time.Unix(5, 6),
4342 mode: fs.ModeDir | 0x777,
4343 entries: []*dirEnt{
4344 {
4345 name: "c",
4346 directoryEntry: &dnodeRW{
4347 dnode: dnode{
4348 modtime: time.Unix(5, 6),
4349 mode: fs.ModeDir | 0x777,
4350 entries: []*dirEnt{
4351 {
4352 name: "d",
4353 directoryEntry: &inodeRW{
4354 inode: inode{
4355 modtime: time.Unix(7, 8),
4356 mode: 3,
4357 data: []byte("Hello"),
4358 },
4359 },
4360 },
4361 },
4362 },
4363 },
4364 },
4365 },
4366 },
4367 },
4368 },
4369 },
4370 },
4371 },
4372 },
4373 },
4374 Path: "b",
4375 Rm: "c",
4376 Output: &FS{
4377 fsRO: fsRO{
4378 de: &dnodeRW{
4379 dnode: dnode{
4380 modtime: time.Unix(5, 6),
4381 mode: fs.ModeDir | 0x777,
4382 entries: []*dirEnt{},
4383 },
4384 },
4385 },
4386 },
4387 },
4388 } {
4389 if output, err := test.FS.Sub(test.Path); !reflect.DeepEqual(err, test.Err) {
4390 t.Errorf("test %d: expecting error %s, got %s", n+1, test.Err, err)
4391 } else if fs, ok := output.(*FS); ok {
4392 if err = fs.RemoveAll(test.Rm); err != nil {
4393 t.Errorf("test %d: unexpected error %s", n+1, err)
4394 } else {
4395 fs.de.(*dnodeRW).modtime = time.Unix(5, 6)
4396
4397 if !reflect.DeepEqual(fs, test.Output) {
4398 t.Errorf("test %d: expected FS %v, got %v", n+1, test.Output, fs)
4399 }
4400 }
4401 }
4402 }
4403 }
4404