memfs - memfs_test.go
1 package memfs
2
3 import (
4 "bytes"
5 "errors"
6 "io/fs"
7 "reflect"
8 "testing"
9 "testing/fstest"
10 "time"
11 )
12
13 func newFS(d dnode) fsRO {
14 return fsRO{
15 de: &d,
16 }
17 }
18
19 func TestOpen(t *testing.T) {
20 for n, test := range [...]struct {
21 FS fsRO
22 Path string
23 File fs.File
24 Err error
25 }{
26 { // 1
27 FS: newFS(dnode{}),
28 Path: "file",
29 Err: &fs.PathError{
30 Op: "open",
31 Path: "file",
32 Err: fs.ErrPermission,
33 },
34 },
35 { // 2
36 FS: newFS(dnode{
37 mode: fs.ModeDir | fs.ModePerm,
38 }),
39 Path: "file",
40 Err: &fs.PathError{
41 Op: "open",
42 Path: "file",
43 Err: fs.ErrNotExist,
44 },
45 },
46 { // 3
47 FS: newFS(dnode{
48 entries: []*dirEnt{
49 {
50 directoryEntry: &inode{},
51 name: "file",
52 },
53 },
54 }),
55 Path: "file",
56 Err: &fs.PathError{
57 Op: "open",
58 Path: "file",
59 Err: fs.ErrPermission,
60 },
61 },
62 { // 4
63 FS: newFS(dnode{
64 entries: []*dirEnt{
65 {
66 directoryEntry: &inode{
67 mode: fs.ModeDir | fs.ModePerm,
68 },
69 name: "file",
70 },
71 },
72 mode: fs.ModeDir | fs.ModePerm,
73 }),
74 Path: "file",
75 File: &file{
76 name: "file",
77 inode: &inode{
78 mode: fs.ModeDir | fs.ModePerm,
79 },
80 opMode: opRead | opSeek,
81 },
82 },
83 { // 5
84 FS: newFS(dnode{
85 entries: []*dirEnt{
86 {
87 directoryEntry: &inode{
88 mode: fs.ModeDir | fs.ModePerm,
89 },
90 name: "otherFile",
91 },
92 },
93 mode: fs.ModeDir | fs.ModePerm,
94 }),
95 Path: "file",
96 Err: &fs.PathError{
97 Op: "open",
98 Path: "file",
99 Err: fs.ErrNotExist,
100 },
101 },
102 { // 6
103 FS: newFS(dnode{
104 entries: []*dirEnt{
105 {
106 directoryEntry: &dnode{
107 entries: []*dirEnt{
108 {
109 directoryEntry: &inode{
110 mode: fs.ModeDir | fs.ModePerm,
111 },
112 name: "deepFile",
113 },
114 },
115 mode: fs.ModeDir | fs.ModePerm,
116 },
117 name: "dir",
118 },
119 },
120 mode: fs.ModeDir | fs.ModePerm,
121 }),
122 Path: "dir/deepFile",
123 File: &file{
124 name: "deepFile",
125 inode: &inode{
126 mode: fs.ModeDir | fs.ModePerm,
127 },
128 opMode: opRead | opSeek,
129 },
130 },
131 } {
132 f, err := test.FS.Open(test.Path)
133 if !reflect.DeepEqual(err, test.Err) {
134 t.Errorf("test %d: expecting error %s, got %s", n+1, test.Err, err)
135 } else if !reflect.DeepEqual(f, test.File) {
136 t.Errorf("test %d: expected file %v, got %v", n+1, test.File, f)
137 }
138 }
139 }
140
141 func TestFSReadDir(t *testing.T) {
142 for n, test := range [...]struct {
143 FS fsRO
144 Path string
145 Output []fs.DirEntry
146 Err error
147 }{
148 { // 1
149 FS: newFS(dnode{}),
150 Path: ".",
151 Err: &fs.PathError{
152 Op: "readdir",
153 Path: ".",
154 Err: fs.ErrPermission,
155 },
156 },
157 { // 2
158 FS: newFS(dnode{
159 mode: fs.ModeDir | fs.ModePerm,
160 }),
161 Path: ".",
162 Output: []fs.DirEntry{},
163 },
164 { // 3
165 FS: newFS(dnode{
166 entries: []*dirEnt{
167 {
168 directoryEntry: &inode{
169 modtime: time.Unix(1, 0),
170 mode: 2,
171 },
172 name: "test",
173 },
174 },
175 mode: fs.ModeDir | fs.ModePerm,
176 }),
177 Path: ".",
178 Output: []fs.DirEntry{
179 &dirEnt{
180 directoryEntry: &inode{
181 modtime: time.Unix(1, 0),
182 mode: 2,
183 },
184 name: "test",
185 },
186 },
187 },
188 { // 4
189 FS: newFS(dnode{
190 entries: []*dirEnt{
191 {
192 directoryEntry: &inode{
193 modtime: time.Unix(1, 0),
194 mode: 2,
195 },
196 name: "test",
197 },
198 {
199 directoryEntry: &inode{
200 modtime: time.Unix(3, 0),
201 mode: 4,
202 },
203 name: "test2",
204 },
205 },
206 mode: fs.ModeDir | fs.ModePerm,
207 }),
208 Path: ".",
209 Output: []fs.DirEntry{
210 &dirEnt{
211 directoryEntry: &inode{
212 modtime: time.Unix(1, 0),
213 mode: 2,
214 },
215 name: "test",
216 },
217 &dirEnt{
218 directoryEntry: &inode{
219 modtime: time.Unix(3, 0),
220 mode: 4,
221 },
222 name: "test2",
223 },
224 },
225 },
226 { // 5
227 FS: newFS(dnode{
228 entries: []*dirEnt{
229 {
230 directoryEntry: &inode{
231 modtime: time.Unix(1, 0),
232 mode: 2,
233 },
234 name: "test",
235 },
236 {
237 directoryEntry: &dnode{
238 entries: []*dirEnt{
239 {
240 directoryEntry: &inode{
241 modtime: time.Unix(3, 0),
242 mode: 4,
243 },
244 name: "test3",
245 },
246 },
247 modtime: time.Unix(5, 0),
248 mode: fs.ModeDir | fs.ModePerm,
249 },
250 name: "test2",
251 },
252 },
253 mode: fs.ModeDir | fs.ModePerm,
254 }),
255 Path: ".",
256 Output: []fs.DirEntry{
257 &dirEnt{
258 directoryEntry: &inode{
259 modtime: time.Unix(1, 0),
260 mode: 2,
261 },
262 name: "test",
263 },
264 &dirEnt{
265 directoryEntry: &dnode{
266 entries: []*dirEnt{
267 {
268 directoryEntry: &inode{
269 modtime: time.Unix(3, 0),
270 mode: 4,
271 },
272 name: "test3",
273 },
274 },
275 modtime: time.Unix(5, 0),
276 mode: fs.ModeDir | fs.ModePerm,
277 },
278 name: "test2",
279 },
280 },
281 },
282 { // 6
283 FS: newFS(dnode{
284 entries: []*dirEnt{
285 {
286 directoryEntry: &inode{
287 modtime: time.Unix(1, 0),
288 mode: 2,
289 },
290 name: "test",
291 },
292 {
293 directoryEntry: &dnode{
294 entries: []*dirEnt{
295 {
296 directoryEntry: &inode{
297 modtime: time.Unix(3, 0),
298 mode: 4,
299 },
300 name: "test3",
301 },
302 },
303 modtime: time.Unix(5, 0),
304 mode: fs.ModeDir | fs.ModePerm,
305 },
306 name: "test2",
307 },
308 },
309 mode: fs.ModeDir | fs.ModePerm,
310 }),
311 Path: "test2",
312 Output: []fs.DirEntry{
313 &dirEnt{
314 directoryEntry: &inode{
315 modtime: time.Unix(3, 0),
316 mode: 4,
317 },
318 name: "test3",
319 },
320 },
321 },
322 { // 7
323 FS: newFS(dnode{
324 entries: []*dirEnt{
325 {
326 directoryEntry: &inode{
327 modtime: time.Unix(1, 0),
328 mode: 2,
329 },
330 name: "test",
331 },
332 {
333 directoryEntry: &dnode{
334 entries: []*dirEnt{
335 {
336 directoryEntry: &inode{
337 modtime: time.Unix(3, 0),
338 mode: 4,
339 },
340 name: "test3",
341 },
342 },
343 modtime: time.Unix(5, 0),
344 mode: fs.ModeDir,
345 },
346 name: "test2",
347 },
348 },
349 mode: fs.ModeDir | fs.ModePerm,
350 }),
351 Path: "test2",
352 Err: &fs.PathError{
353 Op: "readdir",
354 Path: "test2",
355 Err: fs.ErrPermission,
356 },
357 },
358 } {
359 de, err := test.FS.ReadDir(test.Path)
360 if !reflect.DeepEqual(err, test.Err) {
361 t.Errorf("test %d: expecting error %s, got %s", n+1, test.Err, err)
362 } else if !reflect.DeepEqual(test.Output, de) {
363 t.Errorf("test %d: expecting to get %v, got %v", n+1, test.Output, de)
364 }
365 }
366 }
367
368 func TestReadFile(t *testing.T) {
369 for n, test := range [...]struct {
370 FS fsRO
371 Path string
372 Output []byte
373 Err error
374 }{
375 { // 1
376 FS: newFS(dnode{}),
377 Path: ".",
378 Err: &fs.PathError{
379 Op: "readfile",
380 Path: ".",
381 Err: fs.ErrPermission,
382 },
383 },
384 { // 2
385 FS: newFS(dnode{
386 mode: fs.ModeDir | fs.ModePerm,
387 }),
388 Path: ".",
389 Err: &fs.PathError{
390 Op: "readfile",
391 Path: ".",
392 Err: fs.ErrInvalid,
393 },
394 },
395 { // 3
396 FS: newFS(dnode{
397 mode: fs.ModeDir | fs.ModePerm,
398 }),
399 Path: "file",
400 Err: &fs.PathError{
401 Op: "readfile",
402 Path: "file",
403 Err: fs.ErrNotExist,
404 },
405 },
406 { // 4
407 FS: newFS(dnode{
408 entries: []*dirEnt{
409 {
410 directoryEntry: &inode{},
411 name: "notFile",
412 },
413 },
414 mode: fs.ModeDir | fs.ModePerm,
415 }),
416 Path: "file",
417 Err: &fs.PathError{
418 Op: "readfile",
419 Path: "file",
420 Err: fs.ErrNotExist,
421 },
422 },
423 { // 5
424 FS: newFS(dnode{
425 entries: []*dirEnt{
426 {
427 directoryEntry: &inode{},
428 name: "file",
429 },
430 },
431 mode: fs.ModeDir | fs.ModePerm,
432 }),
433 Path: "file",
434 Err: &fs.PathError{
435 Op: "readfile",
436 Path: "file",
437 Err: fs.ErrPermission,
438 },
439 },
440 { // 6
441 FS: newFS(dnode{
442 entries: []*dirEnt{
443 {
444 directoryEntry: &inode{
445 data: []byte("DATA"),
446 mode: fs.ModePerm,
447 },
448 name: "file",
449 },
450 },
451 mode: fs.ModeDir | fs.ModePerm,
452 }),
453 Path: "file",
454 Output: []byte("DATA"),
455 },
456 { // 7
457 FS: newFS(dnode{
458 entries: []*dirEnt{
459 {
460 directoryEntry: &inode{
461 data: []byte("DATA"),
462 mode: fs.ModePerm,
463 },
464 name: "file",
465 },
466 {
467 directoryEntry: &dnode{
468 entries: []*dirEnt{
469 {
470 directoryEntry: &inode{
471 data: []byte("MORE DATA"),
472 mode: fs.ModePerm,
473 },
474 name: "file2",
475 },
476 },
477 mode: fs.ModeDir | fs.ModePerm,
478 },
479 name: "DIR",
480 },
481 },
482 mode: fs.ModeDir | fs.ModePerm,
483 }),
484 Path: "DIR/file2",
485 Output: []byte("MORE DATA"),
486 },
487 } {
488 data, err := test.FS.ReadFile(test.Path)
489 if !reflect.DeepEqual(err, test.Err) {
490 t.Errorf("test %d: expecting error %s, got %s", n+1, test.Err, err)
491 } else if !bytes.Equal(test.Output, data) {
492 t.Errorf("test %d: expecting to get %v, got %v", n+1, test.Output, data)
493 }
494 }
495 }
496
497 func TestStat(t *testing.T) {
498 for n, test := range [...]struct {
499 FS fsRO
500 Path string
501 Output fs.FileInfo
502 Err error
503 }{
504 { // 1
505 FS: newFS(dnode{}),
506 Path: ".",
507 Err: &fs.PathError{
508 Op: "stat",
509 Path: ".",
510 Err: fs.ErrPermission,
511 },
512 },
513 { // 2
514 FS: newFS(dnode{
515 modtime: time.Unix(1, 2),
516 mode: fs.ModeDir | fs.ModePerm,
517 }),
518 Path: ".",
519 Output: &dirEnt{
520 directoryEntry: &dnode{
521 modtime: time.Unix(1, 2),
522 mode: fs.ModeDir | fs.ModePerm,
523 },
524 name: "/",
525 },
526 },
527 { // 3
528 FS: newFS(dnode{
529 mode: fs.ModeDir | fs.ModePerm,
530 }),
531 Path: "file",
532 Err: &fs.PathError{
533 Op: "stat",
534 Path: "file",
535 Err: fs.ErrNotExist,
536 },
537 },
538 { // 4
539 FS: newFS(dnode{
540 entries: []*dirEnt{
541 {
542 directoryEntry: &inode{},
543 name: "notFile",
544 },
545 },
546 mode: fs.ModeDir | fs.ModePerm,
547 }),
548 Path: "file",
549 Err: &fs.PathError{
550 Op: "stat",
551 Path: "file",
552 Err: fs.ErrNotExist,
553 },
554 },
555 { // 5
556 FS: newFS(dnode{
557 entries: []*dirEnt{
558 {
559 directoryEntry: &inode{
560 modtime: time.Unix(1, 2),
561 mode: 3,
562 },
563 name: "file",
564 },
565 },
566 mode: fs.ModeDir | fs.ModePerm,
567 }),
568 Path: "file",
569 Output: &dirEnt{
570 directoryEntry: &inode{
571 modtime: time.Unix(1, 2),
572 mode: 3,
573 },
574 name: "file",
575 },
576 },
577 { // 6
578 FS: newFS(dnode{
579 entries: []*dirEnt{
580 {
581 directoryEntry: &inode{
582 modtime: time.Unix(1, 2),
583 mode: 3,
584 },
585 name: "file",
586 },
587 {
588 directoryEntry: &dnode{
589 modtime: time.Unix(4, 5),
590 mode: 6,
591 },
592 name: "dir",
593 },
594 },
595 mode: fs.ModeDir | fs.ModePerm,
596 }),
597 Path: "dir",
598 Output: &dirEnt{
599 directoryEntry: &dnode{
600 modtime: time.Unix(4, 5),
601 mode: 6,
602 },
603 name: "dir",
604 },
605 },
606 { // 7
607 FS: newFS(dnode{
608 entries: []*dirEnt{
609 {
610 directoryEntry: &inode{
611 modtime: time.Unix(1, 2),
612 mode: 3,
613 },
614 name: "file",
615 },
616 {
617 directoryEntry: &dnode{
618 entries: []*dirEnt{
619 {
620 directoryEntry: &inode{
621 modtime: time.Unix(4, 5),
622 mode: 6,
623 },
624 name: "anotherFile",
625 },
626 },
627 modtime: time.Unix(7, 8),
628 mode: 9,
629 },
630 name: "dir",
631 },
632 },
633 mode: fs.ModeDir | fs.ModePerm,
634 }),
635 Path: "dir/anotherFile",
636 Err: &fs.PathError{
637 Op: "stat",
638 Path: "dir/anotherFile",
639 Err: fs.ErrPermission,
640 },
641 },
642 { // 8
643 FS: newFS(dnode{
644 entries: []*dirEnt{
645 {
646 directoryEntry: &inode{
647 modtime: time.Unix(1, 2),
648 mode: 3,
649 },
650 name: "file",
651 },
652 {
653 directoryEntry: &dnode{
654 entries: []*dirEnt{
655 {
656 directoryEntry: &inode{
657 modtime: time.Unix(4, 5),
658 mode: 6,
659 },
660 name: "anotherFile",
661 },
662 },
663 modtime: time.Unix(7, 8),
664 mode: fs.ModeDir | fs.ModePerm,
665 },
666 name: "dir",
667 },
668 },
669 mode: fs.ModeDir | fs.ModePerm,
670 }),
671 Path: "dir/anotherFile",
672 Output: &dirEnt{
673 directoryEntry: &inode{
674 modtime: time.Unix(4, 5),
675 mode: 6,
676 },
677 name: "anotherFile",
678 },
679 },
680 } {
681 stat, err := test.FS.Stat(test.Path)
682 if !reflect.DeepEqual(err, test.Err) {
683 t.Errorf("test %d: expecting error %s, got %s", n+1, test.Err, err)
684 } else if !reflect.DeepEqual(test.Output, stat) {
685 t.Errorf("test %d: expecting to get %v, got %v", n+1, test.Output, stat)
686 }
687 }
688 }
689
690 func TestSymlinkResolveFile(t *testing.T) {
691 for n, test := range [...]struct {
692 FS fsRO
693 Path string
694 Output []byte
695 Err error
696 }{
697 { // 1
698 FS: newFS(dnode{
699 entries: []*dirEnt{
700 {
701 name: "a",
702 directoryEntry: &inode{
703 data: []byte("Hello"),
704 mode: fs.ModePerm,
705 },
706 },
707 {
708 name: "b",
709 directoryEntry: &inode{
710 data: []byte("/a"),
711 mode: fs.ModeSymlink | fs.ModePerm,
712 },
713 },
714 },
715 mode: fs.ModeDir | fs.ModePerm,
716 }),
717 Path: "b",
718 Output: []byte("Hello"),
719 },
720 { // 2
721 FS: newFS(dnode{
722 entries: []*dirEnt{
723 {
724 name: "a",
725 directoryEntry: &inode{
726 data: []byte("Hello"),
727 mode: fs.ModePerm,
728 },
729 },
730 {
731 name: "b",
732 directoryEntry: &inode{
733 data: []byte("/c"),
734 mode: fs.ModeSymlink | fs.ModePerm,
735 },
736 },
737 },
738 mode: fs.ModeDir | fs.ModePerm,
739 }),
740 Path: "b",
741 Err: fs.ErrNotExist,
742 },
743 { // 3
744 FS: newFS(dnode{
745 entries: []*dirEnt{
746 {
747 name: "a",
748 directoryEntry: &inode{
749 data: []byte("Hello"),
750 },
751 },
752 {
753 name: "b",
754 directoryEntry: &inode{
755 data: []byte("/a"),
756 mode: fs.ModeSymlink | fs.ModePerm,
757 },
758 },
759 },
760 mode: fs.ModeDir | fs.ModePerm,
761 }),
762 Path: "b",
763 Err: fs.ErrPermission,
764 },
765 { // 4
766 FS: newFS(dnode{
767 entries: []*dirEnt{
768 {
769 name: "a",
770 directoryEntry: &dnode{
771 mode: fs.ModeDir | fs.ModePerm,
772 entries: []*dirEnt{
773 {
774 name: "b",
775 directoryEntry: &inode{
776 data: []byte("World"),
777 mode: fs.ModePerm,
778 },
779 },
780 },
781 },
782 },
783 {
784 name: "c",
785 directoryEntry: &inode{
786 data: []byte("/a/b"),
787 mode: fs.ModeSymlink | fs.ModePerm,
788 },
789 },
790 },
791 mode: fs.ModeDir | fs.ModePerm,
792 }),
793 Path: "c",
794 Output: []byte("World"),
795 },
796 { // 5
797 FS: newFS(dnode{
798 entries: []*dirEnt{
799 {
800 name: "a",
801 directoryEntry: &dnode{
802 mode: fs.ModeDir | fs.ModePerm,
803 entries: []*dirEnt{
804 {
805 name: "b",
806 directoryEntry: &inode{
807 data: []byte("World"),
808 mode: fs.ModePerm,
809 },
810 },
811 },
812 },
813 },
814 {
815 name: "c",
816 directoryEntry: &inode{
817 data: []byte("a/b"),
818 mode: fs.ModeSymlink | fs.ModePerm,
819 },
820 },
821 },
822 mode: fs.ModeDir | fs.ModePerm,
823 }),
824 Path: "c",
825 Output: []byte("World"),
826 },
827 { // 6
828 FS: newFS(dnode{
829 entries: []*dirEnt{
830 {
831 name: "a",
832 directoryEntry: &dnode{
833 mode: fs.ModeDir | fs.ModePerm,
834 entries: []*dirEnt{
835 {
836 name: "b",
837 directoryEntry: &inode{
838 data: []byte("/c"),
839 mode: fs.ModeSymlink | fs.ModePerm,
840 },
841 },
842 },
843 },
844 },
845 {
846 name: "c",
847 directoryEntry: &inode{
848 data: []byte("FooBar"),
849 mode: fs.ModePerm,
850 },
851 },
852 },
853 mode: fs.ModeDir | fs.ModePerm,
854 }),
855 Path: "a/b",
856 Output: []byte("FooBar"),
857 },
858 { // 7
859 FS: newFS(dnode{
860 entries: []*dirEnt{
861 {
862 name: "a",
863 directoryEntry: &dnode{
864 mode: fs.ModeDir | fs.ModePerm,
865 entries: []*dirEnt{
866 {
867 name: "b",
868 directoryEntry: &inode{
869 data: []byte("../c"),
870 mode: fs.ModeSymlink | fs.ModePerm,
871 },
872 },
873 },
874 },
875 },
876 {
877 name: "c",
878 directoryEntry: &inode{
879 data: []byte("FooBar"),
880 mode: fs.ModePerm,
881 },
882 },
883 },
884 mode: fs.ModeDir | fs.ModePerm,
885 }),
886 Path: "a/b",
887 Output: []byte("FooBar"),
888 },
889 { // 8
890 FS: newFS(dnode{
891 entries: []*dirEnt{
892 {
893 name: "a",
894 directoryEntry: &dnode{
895 mode: fs.ModeDir | fs.ModePerm,
896 entries: []*dirEnt{
897 {
898 name: "b",
899 directoryEntry: &inode{
900 data: []byte("../c"),
901 mode: fs.ModeSymlink | fs.ModePerm,
902 },
903 },
904 },
905 },
906 },
907 {
908 name: "c",
909 directoryEntry: &inode{
910 data: []byte("d"),
911 mode: fs.ModeSymlink | fs.ModePerm,
912 },
913 },
914 {
915 name: "d",
916 directoryEntry: &inode{
917 data: []byte("Baz"),
918 mode: fs.ModePerm,
919 },
920 },
921 },
922 mode: fs.ModeDir | fs.ModePerm,
923 }),
924 Path: "a/b",
925 Output: []byte("Baz"),
926 },
927 } {
928 if output, err := test.FS.ReadFile(test.Path); !errors.Is(err, test.Err) {
929 t.Errorf("test %d: expecting error %s, got %s", n+1, test.Err, err)
930 } else if !bytes.Equal(test.Output, output) {
931 t.Errorf("test %d: expecting to get %v, got %v", n+1, test.Output, output)
932 }
933 }
934 }
935
936 func TestSymlinkResolveDir(t *testing.T) {
937 for n, test := range [...]struct {
938 FS fsRO
939 Path string
940 Output []fs.DirEntry
941 Err error
942 }{
943 { // 1
944 FS: newFS(dnode{
945 entries: []*dirEnt{
946 {
947 name: "a",
948 directoryEntry: &dnode{
949 entries: []*dirEnt{
950 {
951 name: "b",
952 directoryEntry: &inode{
953 data: []byte("Foo"),
954 mode: fs.ModePerm,
955 },
956 },
957 {
958 name: "c",
959 directoryEntry: &inode{
960 data: []byte("Bar"),
961 mode: fs.ModePerm,
962 },
963 },
964 {
965 name: "d",
966 directoryEntry: &inode{
967 data: []byte("Baz"),
968 mode: fs.ModePerm,
969 },
970 },
971 },
972 mode: fs.ModeDir | fs.ModePerm,
973 },
974 },
975 {
976 name: "e",
977 directoryEntry: &inode{
978 data: []byte("/f"),
979 mode: fs.ModeSymlink | fs.ModePerm,
980 },
981 },
982 },
983 mode: fs.ModeDir | fs.ModePerm,
984 }),
985 Path: "e",
986 Err: fs.ErrNotExist,
987 },
988 { // 2
989 FS: newFS(dnode{
990 entries: []*dirEnt{
991 {
992 name: "a",
993 directoryEntry: &dnode{
994 entries: []*dirEnt{
995 {
996 name: "b",
997 directoryEntry: &inode{
998 data: []byte("Foo"),
999 mode: fs.ModePerm,
1000 },
1001 },
1002 {
1003 name: "c",
1004 directoryEntry: &inode{
1005 data: []byte("Bar"),
1006 mode: fs.ModePerm,
1007 },
1008 },
1009 {
1010 name: "d",
1011 directoryEntry: &inode{
1012 data: []byte("Baz"),
1013 mode: fs.ModePerm,
1014 },
1015 },
1016 },
1017 mode: fs.ModeDir | fs.ModePerm,
1018 },
1019 },
1020 {
1021 name: "e",
1022 directoryEntry: &inode{
1023 data: []byte("/a"),
1024 mode: fs.ModeSymlink | fs.ModePerm,
1025 },
1026 },
1027 },
1028 mode: fs.ModeDir | fs.ModePerm,
1029 }),
1030 Path: "e",
1031 Output: []fs.DirEntry{
1032 &dirEnt{
1033 name: "b",
1034 directoryEntry: &inode{
1035 data: []byte("Foo"),
1036 mode: fs.ModePerm,
1037 },
1038 },
1039 &dirEnt{
1040 name: "c",
1041 directoryEntry: &inode{
1042 data: []byte("Bar"),
1043 mode: fs.ModePerm,
1044 },
1045 },
1046 &dirEnt{
1047 name: "d",
1048 directoryEntry: &inode{
1049 data: []byte("Baz"),
1050 mode: fs.ModePerm,
1051 },
1052 },
1053 },
1054 },
1055 } {
1056 de, err := test.FS.ReadDir(test.Path)
1057 if !errors.Is(err, test.Err) {
1058 t.Errorf("test %d: expecting error %s, got %s", n+1, test.Err, err)
1059 } else if !reflect.DeepEqual(test.Output, de) {
1060 t.Errorf("test %d: expecting to get %v, got %v", n+1, test.Output, de)
1061 }
1062 }
1063 }
1064
1065 func TestLStat(t *testing.T) {
1066 for n, test := range [...]struct {
1067 FS fsRO
1068 Path string
1069 Output fs.FileInfo
1070 Err error
1071 }{
1072 { // 1
1073 FS: newFS(dnode{}),
1074 Path: ".",
1075 Err: &fs.PathError{
1076 Op: "lstat",
1077 Path: ".",
1078 Err: fs.ErrPermission,
1079 },
1080 },
1081 { // 2
1082 FS: newFS(dnode{
1083 modtime: time.Unix(1, 2),
1084 mode: fs.ModeDir | fs.ModePerm,
1085 }),
1086 Path: ".",
1087 Output: &dirEnt{
1088 directoryEntry: &dnode{
1089 modtime: time.Unix(1, 2),
1090 mode: fs.ModeDir | fs.ModePerm,
1091 },
1092 name: "/",
1093 },
1094 },
1095 { // 3
1096 FS: newFS(dnode{
1097 mode: fs.ModeDir | fs.ModePerm,
1098 }),
1099 Path: "file",
1100 Err: &fs.PathError{
1101 Op: "lstat",
1102 Path: "file",
1103 Err: fs.ErrNotExist,
1104 },
1105 },
1106 { // 4
1107 FS: newFS(dnode{
1108 entries: []*dirEnt{
1109 {
1110 directoryEntry: &inode{},
1111 name: "notFile",
1112 },
1113 },
1114 mode: fs.ModeDir | fs.ModePerm,
1115 }),
1116 Path: "file",
1117 Err: &fs.PathError{
1118 Op: "lstat",
1119 Path: "file",
1120 Err: fs.ErrNotExist,
1121 },
1122 },
1123 { // 5
1124 FS: newFS(dnode{
1125 entries: []*dirEnt{
1126 {
1127 directoryEntry: &inode{
1128 modtime: time.Unix(1, 2),
1129 mode: fs.ModeSymlink | 3,
1130 },
1131 name: "file",
1132 },
1133 },
1134 mode: fs.ModeDir | fs.ModePerm,
1135 }),
1136 Path: "file",
1137 Output: &dirEnt{
1138 directoryEntry: &inode{
1139 modtime: time.Unix(1, 2),
1140 mode: fs.ModeSymlink | 3,
1141 },
1142 name: "file",
1143 },
1144 },
1145 { // 6
1146 FS: newFS(dnode{
1147 entries: []*dirEnt{
1148 {
1149 directoryEntry: &inode{
1150 modtime: time.Unix(1, 2),
1151 mode: 3,
1152 },
1153 name: "file",
1154 },
1155 {
1156 directoryEntry: &dnode{
1157 modtime: time.Unix(4, 5),
1158 mode: 6,
1159 },
1160 name: "dir",
1161 },
1162 },
1163 mode: fs.ModeDir | fs.ModePerm,
1164 }),
1165 Path: "dir",
1166 Output: &dirEnt{
1167 directoryEntry: &dnode{
1168 modtime: time.Unix(4, 5),
1169 mode: 6,
1170 },
1171 name: "dir",
1172 },
1173 },
1174 { // 7
1175 FS: newFS(dnode{
1176 entries: []*dirEnt{
1177 {
1178 directoryEntry: &inode{
1179 modtime: time.Unix(1, 2),
1180 mode: 3,
1181 },
1182 name: "file",
1183 },
1184 {
1185 directoryEntry: &dnode{
1186 entries: []*dirEnt{
1187 {
1188 directoryEntry: &inode{
1189 modtime: time.Unix(4, 5),
1190 mode: 6,
1191 },
1192 name: "anotherFile",
1193 },
1194 },
1195 modtime: time.Unix(7, 8),
1196 mode: 9,
1197 },
1198 name: "dir",
1199 },
1200 },
1201 mode: fs.ModeDir | fs.ModePerm,
1202 }),
1203 Path: "dir/anotherFile",
1204 Err: &fs.PathError{
1205 Op: "lstat",
1206 Path: "dir/anotherFile",
1207 Err: fs.ErrPermission,
1208 },
1209 },
1210 { // 8
1211 FS: newFS(dnode{
1212 entries: []*dirEnt{
1213 {
1214 directoryEntry: &inode{
1215 modtime: time.Unix(1, 2),
1216 mode: 3,
1217 },
1218 name: "file",
1219 },
1220 {
1221 directoryEntry: &dnode{
1222 entries: []*dirEnt{
1223 {
1224 directoryEntry: &inode{
1225 modtime: time.Unix(4, 5),
1226 mode: fs.ModeSymlink | 6,
1227 },
1228 name: "anotherFile",
1229 },
1230 },
1231 modtime: time.Unix(7, 8),
1232 mode: fs.ModeDir | fs.ModePerm,
1233 },
1234 name: "dir",
1235 },
1236 },
1237 mode: fs.ModeDir | fs.ModePerm,
1238 }),
1239 Path: "dir/anotherFile",
1240 Output: &dirEnt{
1241 directoryEntry: &inode{
1242 modtime: time.Unix(4, 5),
1243 mode: fs.ModeSymlink | 6,
1244 },
1245 name: "anotherFile",
1246 },
1247 },
1248 } {
1249 if f, err := test.FS.LStat(test.Path); !reflect.DeepEqual(err, test.Err) {
1250 t.Errorf("test %d: expecting error %s, got %s", n+1, test.Err, err)
1251 } else if !reflect.DeepEqual(f, test.Output) {
1252 t.Errorf("test %d: expected FileInfo %v, got %v", n+1, test.Output, f)
1253 }
1254 }
1255 }
1256
1257 func TestReadlink(t *testing.T) {
1258 for n, test := range [...]struct {
1259 FS fsRO
1260 Path string
1261 Output string
1262 Err error
1263 }{
1264 { // 1
1265 FS: newFS(dnode{}),
1266 Path: ".",
1267 Err: &fs.PathError{
1268 Op: "readlink",
1269 Path: ".",
1270 Err: fs.ErrPermission,
1271 },
1272 },
1273 { // 2
1274 FS: newFS(dnode{
1275 modtime: time.Unix(1, 2),
1276 mode: fs.ModeDir | fs.ModePerm,
1277 }),
1278 Path: ".",
1279 Err: &fs.PathError{
1280 Op: "readlink",
1281 Path: ".",
1282 Err: fs.ErrInvalid,
1283 },
1284 },
1285 { // 3
1286 FS: newFS(dnode{
1287 modtime: time.Unix(1, 2),
1288 mode: fs.ModeDir | fs.ModePerm,
1289 }),
1290 Path: ".",
1291 Err: &fs.PathError{
1292 Op: "readlink",
1293 Path: ".",
1294 Err: fs.ErrInvalid,
1295 },
1296 },
1297 { // 4
1298 FS: newFS(dnode{
1299 entries: []*dirEnt{
1300 {
1301 directoryEntry: &inode{},
1302 name: "a",
1303 },
1304 },
1305 modtime: time.Unix(1, 2),
1306 mode: fs.ModeDir | fs.ModePerm,
1307 }),
1308 Path: "a",
1309 Err: &fs.PathError{
1310 Op: "readlink",
1311 Path: "a",
1312 Err: fs.ErrInvalid,
1313 },
1314 },
1315 { // 5
1316 FS: newFS(dnode{
1317 entries: []*dirEnt{
1318 {
1319 directoryEntry: &inode{
1320 mode: fs.ModeSymlink,
1321 },
1322 name: "a",
1323 },
1324 },
1325 modtime: time.Unix(1, 2),
1326 mode: fs.ModeDir | fs.ModePerm,
1327 }),
1328 Path: "a",
1329 Err: &fs.PathError{
1330 Op: "readlink",
1331 Path: "a",
1332 Err: fs.ErrPermission,
1333 },
1334 },
1335 { // 6
1336 FS: newFS(dnode{
1337 entries: []*dirEnt{
1338 {
1339 directoryEntry: &inode{
1340 mode: fs.ModeSymlink | fs.ModePerm,
1341 },
1342 name: "a",
1343 },
1344 },
1345 modtime: time.Unix(1, 2),
1346 mode: fs.ModeDir | fs.ModePerm,
1347 }),
1348 Path: "a",
1349 },
1350 { // 7
1351 FS: newFS(dnode{
1352 entries: []*dirEnt{
1353 {
1354 directoryEntry: &inode{
1355 data: []byte("/other/path"),
1356 mode: fs.ModeSymlink | fs.ModePerm,
1357 },
1358 name: "a",
1359 },
1360 },
1361 modtime: time.Unix(1, 2),
1362 mode: fs.ModeDir | fs.ModePerm,
1363 }),
1364 Path: "a",
1365 Output: "/other/path",
1366 },
1367 } {
1368 if f, err := test.FS.Readlink(test.Path); !reflect.DeepEqual(err, test.Err) {
1369 t.Errorf("test %d: expecting error %s, got %s", n+1, test.Err, err)
1370 } else if !reflect.DeepEqual(f, test.Output) {
1371 t.Errorf("test %d: expected Link %v, got %v", n+1, test.Output, f)
1372 }
1373 }
1374 }
1375
1376 func TestSub(t *testing.T) {
1377 for n, test := range [...]struct {
1378 FS fsRO
1379 Path string
1380 Output fs.FS
1381 Err error
1382 }{
1383 { // 1
1384 FS: fsRO{
1385 de: &dnode{},
1386 },
1387 Path: ".",
1388 Err: &fs.PathError{
1389 Op: "sub",
1390 Path: ".",
1391 Err: fs.ErrPermission,
1392 },
1393 },
1394 { // 2
1395 FS: fsRO{
1396 de: &dnode{
1397 modtime: time.Unix(1, 2),
1398 mode: fs.ModeDir | 0x444,
1399 entries: []*dirEnt{
1400 {
1401 name: "a",
1402 directoryEntry: &inode{
1403 modtime: time.Unix(3, 4),
1404 mode: 1,
1405 data: []byte("Foo"),
1406 },
1407 },
1408 {
1409 name: "b",
1410 directoryEntry: &dnode{
1411 modtime: time.Unix(5, 6),
1412 mode: fs.ModeDir | 0x444,
1413 entries: []*dirEnt{
1414 {
1415 name: "c",
1416 directoryEntry: &inode{
1417 modtime: time.Unix(7, 8),
1418 mode: 3,
1419 data: []byte("Hello"),
1420 },
1421 },
1422 {
1423 name: "d",
1424 directoryEntry: &inode{
1425 modtime: time.Unix(9, 10),
1426 mode: 4,
1427 data: []byte("World"),
1428 },
1429 },
1430 },
1431 },
1432 },
1433 },
1434 },
1435 },
1436 Path: "b",
1437 Output: &fsRO{
1438 de: &dnode{
1439 modtime: time.Unix(5, 6),
1440 mode: fs.ModeDir | 0x444,
1441 entries: []*dirEnt{
1442 {
1443 name: "c",
1444 directoryEntry: &inode{
1445 modtime: time.Unix(7, 8),
1446 mode: 3,
1447 data: []byte("Hello"),
1448 },
1449 },
1450 {
1451 name: "d",
1452 directoryEntry: &inode{
1453 modtime: time.Unix(9, 10),
1454 mode: 4,
1455 data: []byte("World"),
1456 },
1457 },
1458 },
1459 },
1460 },
1461 },
1462 { // 3
1463 FS: fsRO{
1464 de: &dnode{
1465 modtime: time.Unix(1, 2),
1466 mode: fs.ModeDir | 0x444,
1467 entries: []*dirEnt{
1468 {
1469 name: "a",
1470 directoryEntry: &inode{
1471 modtime: time.Unix(3, 4),
1472 mode: 1,
1473 data: []byte("Foo"),
1474 },
1475 },
1476 {
1477 name: "b",
1478 directoryEntry: &dnode{
1479 modtime: time.Unix(5, 6),
1480 mode: fs.ModeDir | 0x444,
1481 entries: []*dirEnt{
1482 {
1483 name: "c",
1484 directoryEntry: &inode{
1485 modtime: time.Unix(7, 8),
1486 mode: 3,
1487 data: []byte("Hello"),
1488 },
1489 },
1490 {
1491 name: "d",
1492 directoryEntry: &inode{
1493 modtime: time.Unix(9, 10),
1494 mode: 4,
1495 data: []byte("World"),
1496 },
1497 },
1498 },
1499 },
1500 },
1501 },
1502 },
1503 },
1504 Path: "a",
1505 Err: &fs.PathError{
1506 Op: "sub",
1507 Path: "a",
1508 Err: fs.ErrInvalid,
1509 },
1510 },
1511 } {
1512 if output, err := test.FS.Sub(test.Path); !reflect.DeepEqual(err, test.Err) {
1513 t.Errorf("test %d: expecting error %s, got %s", n+1, test.Err, err)
1514 } else if !reflect.DeepEqual(output, test.Output) {
1515 t.Errorf("test %d: expected FS %v, got %v", n+1, test.Output, output)
1516 }
1517 }
1518 }
1519
1520 func TestFSTest(t *testing.T) {
1521 fs := fsRO{
1522 de: &dnode{
1523 modtime: time.Unix(1, 2),
1524 mode: fs.ModeDir | 0x444,
1525 entries: []*dirEnt{
1526 {
1527 name: "a",
1528 directoryEntry: &inode{
1529 modtime: time.Unix(3, 4),
1530 mode: 0x444,
1531 data: []byte("Foo"),
1532 },
1533 },
1534 {
1535 name: "b",
1536 directoryEntry: &dnode{
1537 modtime: time.Unix(5, 6),
1538 mode: fs.ModeDir | 0x444,
1539 entries: []*dirEnt{
1540 {
1541 name: "c",
1542 directoryEntry: &inode{
1543 modtime: time.Unix(7, 8),
1544 mode: 0x444,
1545 data: []byte("Hello"),
1546 },
1547 },
1548 {
1549 name: "d",
1550 directoryEntry: &inode{
1551 modtime: time.Unix(9, 10),
1552 mode: 0x444,
1553 data: []byte("World"),
1554 },
1555 },
1556 },
1557 },
1558 },
1559 },
1560 },
1561 }
1562
1563 if err := fstest.TestFS(&fs, "b/d"); err != nil {
1564 t.Errorf("error during fstest: %s", err)
1565 }
1566 }
1567