memfs - file_test.go
1 package memfs
2
3 import (
4 "bytes"
5 "errors"
6 "io"
7 "io/fs"
8 "reflect"
9 "strings"
10 "testing"
11 )
12
13 var _ interface {
14 io.ReadSeekCloser
15 io.ReaderAt
16 io.WriterTo
17 io.RuneScanner
18 io.ByteScanner
19 } = &file{}
20
21 func TestRead(t *testing.T) {
22 for n, test := range [...]struct {
23 Data []byte
24 Mode opMode
25 Err error
26 }{
27 {
28 Err: &fs.PathError{
29 Op: "read",
30 Err: fs.ErrClosed,
31 },
32 },
33 {
34 Mode: opRead,
35 },
36 {
37 Data: []byte("Hello, World"),
38 Mode: opRead,
39 },
40 {
41 Data: []byte(strings.Repeat("Hello, World!", 1000)),
42 Mode: opRead,
43 },
44 {
45 Mode: opSeek,
46 Err: &fs.PathError{
47 Op: "read",
48 Err: fs.ErrInvalid,
49 },
50 },
51 } {
52 f := file{
53 inode: &inode{
54 data: test.Data,
55 },
56 opMode: test.Mode,
57 }
58
59 data, err := io.ReadAll(&f)
60 if !reflect.DeepEqual(err, test.Err) {
61 t.Errorf("test %d: expecting error %s, got %s", n+1, test.Err, err)
62 } else if !bytes.Equal(data, test.Data) {
63 t.Errorf("test %d: expecting bytes %v, got %v", n+1, test.Data, data)
64 }
65 }
66 }
67
68 func TestReadAt(t *testing.T) {
69 for n, test := range [...]struct {
70 Data []byte
71 Mode opMode
72 Read [][2]int64
73 Output [][]byte
74 Err error
75 }{
76 {
77 Err: fs.ErrClosed,
78 },
79 {
80 Data: []byte("Hello, World"),
81 Mode: opRead,
82 Read: [][2]int64{
83 {1, 0},
84 },
85 Output: [][]byte{
86 []byte("H"),
87 },
88 Err: &fs.PathError{
89 Op: "readat",
90 Path: "",
91 Err: fs.ErrInvalid,
92 },
93 },
94 {
95 Mode: opSeek,
96 Data: []byte("Hello, World"),
97 Read: [][2]int64{
98 {1, 0},
99 },
100 Output: [][]byte{
101 []byte("H"),
102 },
103 Err: &fs.PathError{
104 Op: "readat",
105 Path: "",
106 Err: fs.ErrInvalid,
107 },
108 },
109 {
110 Mode: opRead | opSeek,
111 Data: []byte("Hello, World"),
112 Read: [][2]int64{
113 {1, 0},
114 },
115 Output: [][]byte{
116 []byte("H"),
117 },
118 },
119 {
120 Mode: opRead | opSeek,
121 Data: []byte("Hello, World"),
122 Read: [][2]int64{
123 {1, 0},
124 {1, 0},
125 {2, 0},
126 {2, 4},
127 {4, 2},
128 },
129 Output: [][]byte{
130 []byte("H"),
131 []byte("H"),
132 []byte("He"),
133 []byte("o,"),
134 []byte("llo,"),
135 },
136 },
137 } {
138 f := file{
139 inode: &inode{
140 data: test.Data,
141 },
142 opMode: test.Mode,
143 }
144
145 readAtTests := func(o int) bool {
146 for m, toRead := range test.Read {
147 buf := make([]byte, toRead[0])
148 l, err := f.ReadAt(buf, toRead[1])
149 if !reflect.DeepEqual(err, test.Err) {
150 t.Errorf("test %d.%d.%d: expecting error %s, got %s", n+1, o, m+1, test.Err, err)
151
152 return false
153 } else if test.Err != nil {
154 return false
155 } else if len(buf) != l {
156 t.Errorf("test %d.%d.%d: expecting to read %d bytes, read %d", n+1, o, m+1, len(buf), l)
157
158 return false
159 } else if string(buf) != string(test.Output[m]) {
160 t.Errorf("test %d.%d.%d: expecting to read %s, read %s", n+1, o, m+1, test.Output[m], buf)
161
162 return false
163 }
164 }
165
166 return true
167 }
168
169 if !readAtTests(1) {
170 continue
171 }
172
173 if !readAtTests(2) {
174 continue
175 }
176
177 if test.Err != nil {
178 continue
179 }
180
181 _, err := f.Read([]byte{1})
182 if err != nil {
183 t.Errorf("test %d: unexpected error: %s", n+1, err)
184
185 continue
186 }
187
188 if !readAtTests(3) {
189 continue
190 }
191
192 _, err = f.Read([]byte{2})
193 if err != nil {
194 t.Errorf("test %d: unexpected error: %s", n+1, err)
195
196 continue
197 }
198
199 if !readAtTests(4) {
200 continue
201 }
202
203 _, err = io.ReadAll(&f)
204 if err != nil {
205 t.Errorf("test %d: unexpected error: %s", n+1, err)
206
207 continue
208 }
209
210 if !readAtTests(5) {
211 continue
212 }
213 }
214 }
215
216 func TestReadByte(t *testing.T) {
217 Tests:
218 for n, test := range [...]struct {
219 Data []byte
220 Mode opMode
221 Err error
222 }{
223 {
224 Err: &fs.PathError{
225 Op: "readbyte",
226 Path: "",
227 Err: fs.ErrInvalid,
228 },
229 },
230 {
231 Data: []byte{'a'},
232 Err: &fs.PathError{
233 Op: "readbyte",
234 Path: "",
235 Err: fs.ErrClosed,
236 },
237 },
238 {
239 Data: []byte("abc"),
240 Mode: opRead,
241 },
242 } {
243 f := file{
244 inode: &inode{
245 data: test.Data,
246 },
247 opMode: test.Mode,
248 }
249
250 for i := range test.Data {
251 b, err := f.ReadByte()
252 if !reflect.DeepEqual(err, test.Err) {
253 t.Errorf("test %d.%d: expecting error %s, got %s", n+1, i+1, test.Err, err)
254 } else if test.Err != nil {
255 continue Tests
256 } else if test.Data[i] != b {
257 t.Errorf("test %d.%d: expecting to read byte %d, got %d", n+1, i+1, test.Data[0], b)
258 }
259 }
260
261 if test.Err != nil {
262 continue
263 }
264
265 b, err := f.ReadByte()
266 if !errors.Is(err, io.EOF) {
267 t.Errorf("test %d.%d: expecting error %s, got %s", n+1, len(test.Data)+1, io.EOF, err)
268 } else if b != 0 {
269 t.Errorf("test %d.%d: expecting to read byte %d, got %d", n+1, len(test.Data)+1, 0, b)
270 }
271 }
272 }
273
274 func TestUnreadByte(t *testing.T) {
275 f := file{
276 inode: &inode{
277 data: []byte("12345"),
278 },
279 opMode: opRead,
280 }
281
282 f.ReadByte()
283
284 err := f.UnreadByte()
285 if !reflect.DeepEqual(err, &fs.PathError{
286 Op: "unreadbyte",
287 Path: "",
288 Err: fs.ErrInvalid,
289 }) {
290 t.Errorf("test 1: expecting ErrClosed, got %s", err)
291
292 return
293 }
294
295 f.opMode |= opSeek
296
297 c, _ := f.ReadByte()
298 if c != '2' {
299 t.Errorf("test 2: expecting to read '2', read %q", c)
300
301 return
302 }
303
304 err = f.UnreadByte()
305 if !errors.Is(err, nil) {
306 t.Errorf("test 3: expecting nil error, got %s", err)
307
308 return
309 }
310
311 c, _ = f.ReadByte()
312 if c != '2' {
313 t.Errorf("test 4: expecting to read '2', read %q", c)
314
315 return
316 }
317
318 err = f.UnreadByte()
319 if !errors.Is(err, nil) {
320 t.Errorf("test 5: expecting nil error, got %s", err)
321
322 return
323 }
324
325 err = f.UnreadByte()
326 if !reflect.DeepEqual(err, &fs.PathError{
327 Op: "unreadbyte",
328 Path: "",
329 Err: fs.ErrInvalid,
330 }) {
331 t.Errorf("test 6: expecting ErrInvalid error, got %s", err)
332
333 return
334 }
335
336 c, _ = f.ReadByte()
337 if c != '2' {
338 t.Errorf("test 7: expecting to read '2', read %q", c)
339
340 return
341 }
342
343 f.ReadByte()
344
345 err = f.UnreadByte()
346 if !errors.Is(err, nil) {
347 t.Errorf("test 8: expecting nil error, got %s", err)
348
349 return
350 }
351
352 c, _ = f.ReadByte()
353 if c != '3' {
354 t.Errorf("test 9: expecting to read '3', read %q", c)
355
356 return
357 }
358
359 f.Read([]byte{0})
360
361 err = f.UnreadByte()
362 if !reflect.DeepEqual(err, &fs.PathError{
363 Op: "unreadbyte",
364 Path: "",
365 Err: fs.ErrInvalid,
366 }) {
367 t.Errorf("test 10: expecting nil error, got %s", err)
368
369 return
370 }
371 }
372
373 func TestReadRune(t *testing.T) {
374 f := file{
375 inode: &inode{
376 data: []byte("1ħᕗ🐶"),
377 },
378 }
379
380 _, _, err := f.ReadRune()
381 if !errors.Is(err, fs.ErrClosed) {
382 t.Errorf("test 1: expecting ErrClosed, got %s", err)
383
384 return
385 }
386
387 f.opMode = opRead
388
389 r, n, err := f.ReadRune()
390
391 if !errors.Is(err, nil) {
392 t.Errorf("test 2: expecting nil error, got %s", err)
393
394 return
395 } else if n != 1 {
396 t.Errorf("test 2: expecting to read 1 byte, read %d", n)
397
398 return
399 } else if r != '1' {
400 t.Errorf("test 2: expecting to read '1', read %q", r)
401
402 return
403 }
404
405 r, n, err = f.ReadRune()
406
407 if !errors.Is(err, nil) {
408 t.Errorf("test 3: expecting nil error, got %s", err)
409
410 return
411 } else if n != 2 {
412 t.Errorf("test 3: expecting to read 2 bytes, read %d", n)
413
414 return
415 } else if r != 'ħ' {
416 t.Errorf("test 3: expecting to read 'ħ', read %q", r)
417
418 return
419 }
420
421 r, n, err = f.ReadRune()
422
423 if !errors.Is(err, nil) {
424 t.Errorf("test 4: expecting nil error, got %s", err)
425
426 return
427 } else if n != 3 {
428 t.Errorf("test 4: expecting to read 3 bytes, read %d", n)
429
430 return
431 } else if r != 'ᕗ' {
432 t.Errorf("test 4: expecting to read 'ᕗ', read %q", r)
433
434 return
435 }
436
437 r, n, err = f.ReadRune()
438
439 if !errors.Is(err, nil) {
440 t.Errorf("test 5: expecting nil error, got %s", err)
441
442 return
443 } else if n != 4 {
444 t.Errorf("test 5: expecting to read 4 bytes, read %d", n)
445
446 return
447 } else if r != '🐶' {
448 t.Errorf("test 5: expecting to read '🐶', read %q", r)
449
450 return
451 }
452
453 r, n, err = f.ReadRune()
454 if !errors.Is(err, io.EOF) {
455 t.Errorf("test 6: expecting error EOF, got %s", err)
456
457 return
458 } else if n != 0 {
459 t.Errorf("test 6: expecting to read 0 bytes, read %d", n)
460
461 return
462 } else if r != 0 {
463 t.Errorf("test 6: expecting to read 0, read %q", r)
464
465 return
466 }
467 }
468
469 func TestUnreadRune(t *testing.T) {
470 f := file{
471 inode: &inode{
472 data: []byte("1ħᕗ🐶"),
473 },
474 opMode: opRead,
475 }
476
477 f.ReadRune()
478
479 err := f.UnreadRune()
480 if !reflect.DeepEqual(err, &fs.PathError{
481 Op: "unreadrune",
482 Path: "",
483 Err: fs.ErrInvalid,
484 }) {
485 t.Errorf("test 1: expecting ErrClosed, got %s", err)
486
487 return
488 }
489
490 f.opMode |= opSeek
491 f.pos = 0
492
493 c, _, _ := f.ReadRune()
494 if c != '1' {
495 t.Errorf("test 2: expecting to read '1', read %q", c)
496
497 return
498 }
499
500 err = f.UnreadRune()
501 if !errors.Is(err, nil) {
502 t.Errorf("test 3: expecting nil error, got %s", err)
503
504 return
505 }
506
507 c, _, _ = f.ReadRune()
508 if c != '1' {
509 t.Errorf("test 4: expecting to read '1', read %q", c)
510
511 return
512 }
513
514 err = f.UnreadRune()
515 if !errors.Is(err, nil) {
516 t.Errorf("test 5: expecting nil error, got %s", err)
517
518 return
519 }
520
521 err = f.UnreadRune()
522 if !reflect.DeepEqual(err, &fs.PathError{
523 Op: "unreadrune",
524 Path: "",
525 Err: fs.ErrInvalid,
526 }) {
527 t.Errorf("test 6: expecting ErrInvalid error, got %s", err)
528
529 return
530 }
531
532 c, _, _ = f.ReadRune()
533 if c != '1' {
534 t.Errorf("test 7: expecting to read '1', read %q", c)
535
536 return
537 }
538
539 err = f.UnreadRune()
540 if !errors.Is(err, nil) {
541 t.Errorf("test 8: expecting nil error, got %s", err)
542
543 return
544 }
545
546 c, _, _ = f.ReadRune()
547 if c != '1' {
548 t.Errorf("test 9: expecting to read '1', read %q", c)
549
550 return
551 }
552
553 f.Read([]byte{0})
554
555 err = f.UnreadRune()
556 if !reflect.DeepEqual(err, &fs.PathError{
557 Op: "unreadrune",
558 Path: "",
559 Err: fs.ErrInvalid,
560 }) {
561 t.Errorf("test 10: expecting nil error, got %s", err)
562
563 return
564 }
565
566 f.pos = 1
567
568 f.ReadRune()
569
570 f.UnreadRune()
571
572 c, _, _ = f.ReadRune()
573 if c != 'ħ' {
574 t.Errorf("test 11: expecting to read 'ħ', read %q", c)
575
576 return
577 }
578
579 f.ReadRune()
580 f.ReadRune()
581 f.UnreadRune()
582
583 c, _, _ = f.ReadRune()
584 if c != '🐶' {
585 t.Errorf("test 12: expecting to read '🐶', read %q", c)
586
587 return
588 }
589
590 f.UnreadRune()
591
592 c, _, _ = f.ReadRune()
593 if c != '🐶' {
594 t.Errorf("test 12: expecting to read '🐶', read %q", c)
595
596 return
597 }
598 }
599
600 func TestWriteTo(t *testing.T) {
601 f := file{
602 inode: &inode{
603 data: []byte("12345"),
604 },
605 }
606
607 var sb strings.Builder
608
609 _, err := f.WriteTo(&sb)
610 if !reflect.DeepEqual(err, &fs.PathError{
611 Op: "writeto",
612 Path: "",
613 Err: fs.ErrClosed,
614 }) {
615 t.Errorf("test 1: expecting to get error ErrClosed, got %s", err)
616 }
617
618 f.opMode = opRead
619
620 n, err := f.WriteTo(&sb)
621 if err != nil {
622 t.Errorf("test 2: expecting to get no error, got %s", err)
623 } else if n != 5 {
624 t.Errorf("test 2: expecting to read 5 bytes, read %d", n)
625 } else if str := sb.String(); str != "12345" {
626 t.Errorf("test 2: expecting to write %q, wrote %q", "12345", str)
627 }
628
629 _, err = f.WriteTo(&sb)
630 if !errors.Is(err, io.EOF) {
631 t.Errorf("test 3: expecting to get error EOF, got %s", err)
632 }
633
634 f.pos = 1
635
636 sb.Reset()
637
638 n, err = f.WriteTo(&sb)
639 if err != nil {
640 t.Errorf("test 4: expecting to get no error, got %s", err)
641 } else if n != 4 {
642 t.Errorf("test 4: expecting to read 5 bytes, read %d", n)
643 } else if str := sb.String(); str != "2345" {
644 t.Errorf("test 4: expecting to write %q, wrote %q", "2345", str)
645 }
646 }
647
648 func TestSeek(t *testing.T) {
649 f := file{
650 inode: &inode{
651 data: make([]byte, 100),
652 },
653 opMode: opSeek,
654 }
655 for n, test := range [...]struct {
656 Offset int64
657 Whence int
658 Pos int64
659 Err error
660 }{
661 {
662 Offset: 0,
663 Whence: io.SeekStart,
664 Pos: 0,
665 },
666 {
667 Offset: -1,
668 Whence: io.SeekStart,
669 Pos: 0,
670 Err: &fs.PathError{
671 Op: "seek",
672 Path: "",
673 Err: fs.ErrInvalid,
674 },
675 },
676 {
677 Offset: 10,
678 Whence: io.SeekStart,
679 Pos: 10,
680 },
681 {
682 Offset: 10,
683 Whence: io.SeekStart,
684 Pos: 10,
685 },
686 {
687 Offset: -1,
688 Whence: io.SeekStart,
689 Pos: 0,
690 Err: &fs.PathError{
691 Op: "seek",
692 Path: "",
693 Err: fs.ErrInvalid,
694 },
695 },
696 {
697 Offset: 10,
698 Whence: io.SeekStart,
699 Pos: 10,
700 },
701 {
702 Offset: 10,
703 Whence: io.SeekCurrent,
704 Pos: 20,
705 },
706 {
707 Offset: 10,
708 Whence: io.SeekCurrent,
709 Pos: 30,
710 },
711 {
712 Offset: -10,
713 Whence: io.SeekCurrent,
714 Pos: 20,
715 },
716 {
717 Offset: 0,
718 Whence: io.SeekCurrent,
719 Pos: 20,
720 },
721 {
722 Offset: 0,
723 Whence: io.SeekEnd,
724 Pos: 100,
725 },
726 {
727 Offset: 10,
728 Whence: io.SeekEnd,
729 Pos: 110,
730 },
731 {
732 Offset: -10,
733 Whence: io.SeekEnd,
734 Pos: 90,
735 },
736 } {
737 pos, err := f.Seek(test.Offset, test.Whence)
738 if !reflect.DeepEqual(err, test.Err) {
739 t.Errorf("test %d: expecting error %s, got %s", n+1, test.Err, err)
740 } else if pos != test.Pos {
741 t.Errorf("test %d: expecting pos %d, got %d", n+1, test.Pos, pos)
742 }
743 }
744 }
745
746 func TestClose(t *testing.T) {
747 f := file{
748 inode: &inode{
749 data: []byte("123"),
750 },
751 opMode: opWrite,
752 }
753
754 err := f.Close()
755 if err != nil {
756 t.Errorf("test 1: expecting nil error, got %s", err)
757 }
758
759 err = f.Close()
760 if !errors.Is(err, fs.ErrClosed) {
761 t.Errorf("test 2: expecting ErrClosed error, got %s", err)
762 }
763
764 _, err = f.Read([]byte{})
765 if !reflect.DeepEqual(err, &fs.PathError{
766 Op: "read",
767 Path: "",
768 Err: fs.ErrClosed,
769 }) {
770 t.Errorf("test 3: expecting ErrClosed error, got %s", err)
771 }
772
773 f.opMode = opRead
774
775 var buf [1]byte
776
777 _, err = f.Read(buf[:])
778 if err != nil {
779 t.Errorf("test 4: expecting nil error, got %s", err)
780 } else if buf[0] != '1' {
781 t.Errorf("test 4: expecting to read '1', read %s", buf[:1])
782 }
783
784 err = f.Close()
785 if err != nil {
786 t.Errorf("test 5: expecting nil error, got %s", err)
787 }
788
789 _, err = f.Read(buf[:])
790 if !reflect.DeepEqual(err, &fs.PathError{
791 Op: "read",
792 Path: "",
793 Err: fs.ErrClosed,
794 }) {
795 t.Errorf("test 6: expecting ErrClosed error, got %s", err)
796 }
797
798 f.opMode = opSeek
799
800 pos, err := f.Seek(1, io.SeekStart)
801 if err != nil {
802 t.Errorf("test 7: expecting nil error, got %s", err)
803 } else if pos != 1 {
804 t.Errorf("test 7: expecting to be at position 1, at %d", pos)
805 }
806
807 err = f.Close()
808 if err != nil {
809 t.Errorf("test 8: expecting nil error, got %s", err)
810 }
811
812 pos, err = f.Seek(1, io.SeekStart)
813 if !reflect.DeepEqual(err, &fs.PathError{
814 Op: "seek",
815 Path: "",
816 Err: fs.ErrClosed,
817 }) {
818 t.Errorf("test 9: expecting ErrClosed error, got %s", err)
819 }
820 }
821