1 package sysfs
2
3 import (
4 "embed"
5 "io"
6 "io/fs"
7 "os"
8 "path"
9 "runtime"
10 "testing"
11 gofstest "testing/fstest"
12
13 experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
14 "github.com/tetratelabs/wazero/internal/fsapi"
15 "github.com/tetratelabs/wazero/internal/platform"
16 "github.com/tetratelabs/wazero/internal/testing/require"
17 "github.com/tetratelabs/wazero/sys"
18 )
19
20
21 var embedFS embed.FS
22
23 var (
24
25 testdata embed.FS
26 wazeroFile = "wazero.txt"
27 emptyFile = "empty.txt"
28 )
29
30 func TestStdioFileSetNonblock(t *testing.T) {
31
32 r, w, err := os.Pipe()
33 require.NoError(t, err)
34 defer r.Close()
35 defer w.Close()
36
37 rF, err := NewStdioFile(true, r)
38 require.NoError(t, err)
39
40 errno := rF.SetNonblock(true)
41 require.EqualErrno(t, 0, errno)
42 require.True(t, rF.IsNonblock())
43
44 errno = rF.SetNonblock(false)
45 require.EqualErrno(t, 0, errno)
46 require.False(t, rF.IsNonblock())
47 }
48
49 func TestRegularFileSetNonblock(t *testing.T) {
50
51 r, w, err := os.Pipe()
52 require.NoError(t, err)
53 defer r.Close()
54 defer w.Close()
55
56 rF := newOsFile("", experimentalsys.O_RDONLY, 0, r)
57
58 errno := rF.SetNonblock(true)
59 require.EqualErrno(t, 0, errno)
60 require.True(t, rF.IsNonblock())
61
62
63 buf := make([]byte, 8)
64 _, e := rF.Read(buf)
65 require.EqualErrno(t, experimentalsys.EAGAIN, e)
66
67 errno = rF.SetNonblock(false)
68 require.EqualErrno(t, 0, errno)
69 require.False(t, rF.IsNonblock())
70 }
71
72 func TestReadFdNonblock(t *testing.T) {
73
74 r, w, err := os.Pipe()
75 require.NoError(t, err)
76 defer r.Close()
77 defer w.Close()
78
79 fd := r.Fd()
80 errno := setNonblock(fd, true)
81 require.EqualErrno(t, 0, errno)
82
83
84 buf := make([]byte, 8)
85 _, errno = readFd(fd, buf)
86 require.EqualErrno(t, experimentalsys.EAGAIN, errno)
87 }
88
89 func TestWriteFdNonblock(t *testing.T) {
90
91 r, w, err := os.Pipe()
92 require.NoError(t, err)
93 defer r.Close()
94 defer w.Close()
95
96 fd := w.Fd()
97 errno := setNonblock(fd, true)
98
99 require.EqualErrno(t, 0, errno)
100
101
102 buf := make([]byte, 1024)
103
104 numWrites := 100
105 for i := 0; i < numWrites; i++ {
106 _, e := writeFd(fd, buf)
107 if e != 0 {
108 if runtime.GOOS == "windows" {
109
110 require.EqualErrno(t, experimentalsys.ENOSYS, e)
111 } else {
112 require.EqualErrno(t, experimentalsys.EAGAIN, e)
113 }
114 return
115 }
116 }
117 t.Fatal("writeFd should return EAGAIN at some point")
118 }
119
120 func TestFileSetAppend(t *testing.T) {
121 tmpDir := t.TempDir()
122
123 fPath := path.Join(tmpDir, "file")
124 require.NoError(t, os.WriteFile(fPath, []byte("0123456789"), 0o600))
125
126
127 f, errno := OpenOSFile(fPath, experimentalsys.O_RDWR, 0o600)
128 require.EqualErrno(t, 0, errno)
129 require.False(t, f.IsAppend())
130
131
132 require.EqualErrno(t, 0, f.SetAppend(true))
133 require.True(t, f.IsAppend())
134
135 requireFileContent := func(exp string) {
136 buf, err := os.ReadFile(fPath)
137 require.NoError(t, err)
138 require.Equal(t, exp, string(buf))
139 }
140
141
142 _, errno = f.Write([]byte("wazero"))
143 require.EqualErrno(t, 0, errno)
144 requireFileContent("0123456789wazero")
145
146
147 require.EqualErrno(t, 0, f.SetAppend(false))
148 require.False(t, f.IsAppend())
149
150 _, errno = f.Seek(0, 0)
151 require.EqualErrno(t, 0, errno)
152
153
154 _, errno = f.Write([]byte("wazero"))
155 require.EqualErrno(t, 0, errno)
156 requireFileContent("wazero6789wazero")
157 }
158
159 func TestStdioFile_SetAppend(t *testing.T) {
160
161 file, err := NewStdioFile(false, os.Stdout)
162 require.NoError(t, err)
163 errno := file.SetAppend(true)
164 require.EqualErrno(t, 0, errno)
165 _, errno = file.Write([]byte{})
166 require.EqualErrno(t, 0, errno)
167 }
168
169 func TestFileIno(t *testing.T) {
170 tmpDir := t.TempDir()
171 dirFS, embedFS, mapFS := dirEmbedMapFS(t, tmpDir)
172
173
174 st, errno := stat(tmpDir)
175 require.EqualErrno(t, 0, errno)
176
177 tests := []struct {
178 name string
179 fs fs.FS
180 expectedIno sys.Inode
181 }{
182 {name: "os.DirFS", fs: dirFS, expectedIno: st.Ino},
183 {name: "embed.api.FS", fs: embedFS},
184 {name: "fstest.MapFS", fs: mapFS},
185 }
186
187 for _, tc := range tests {
188 tc := tc
189
190 t.Run(tc.name, func(t *testing.T) {
191 d, errno := OpenFSFile(tc.fs, ".", experimentalsys.O_RDONLY, 0)
192 require.EqualErrno(t, 0, errno)
193 defer d.Close()
194
195 ino, errno := d.Ino()
196 require.EqualErrno(t, 0, errno)
197
198 if statSetsIno() {
199 require.Equal(t, tc.expectedIno, ino)
200 }
201 })
202 }
203
204 t.Run("OS", func(t *testing.T) {
205 d, errno := OpenOSFile(tmpDir, experimentalsys.O_RDONLY, 0)
206 require.EqualErrno(t, 0, errno)
207 defer d.Close()
208
209 ino, errno := d.Ino()
210 require.EqualErrno(t, 0, errno)
211
212 if statSetsIno() {
213 require.Equal(t, st.Ino, ino)
214 }
215 })
216 }
217
218
219
220
221 func statSetsIno() bool {
222 if runtime.GOOS != "windows" {
223 return true
224 } else {
225
226
227 return platform.IsAtLeastGo120
228 }
229 }
230
231 func TestFileIsDir(t *testing.T) {
232 dirFS, embedFS, mapFS := dirEmbedMapFS(t, t.TempDir())
233
234 tests := []struct {
235 name string
236 fs fs.FS
237 }{
238 {name: "os.DirFS", fs: dirFS},
239 {name: "embed.api.FS", fs: embedFS},
240 {name: "fstest.MapFS", fs: mapFS},
241 }
242
243 for _, tc := range tests {
244 tc := tc
245
246 t.Run(tc.name, func(t *testing.T) {
247 t.Run("file", func(t *testing.T) {
248 f, errno := OpenFSFile(tc.fs, wazeroFile, experimentalsys.O_RDONLY, 0)
249 require.EqualErrno(t, 0, errno)
250 defer f.Close()
251
252 isDir, errno := f.IsDir()
253 require.EqualErrno(t, 0, errno)
254 require.False(t, isDir)
255 })
256
257 t.Run("dir", func(t *testing.T) {
258 d, errno := OpenFSFile(tc.fs, ".", experimentalsys.O_RDONLY, 0)
259 require.EqualErrno(t, 0, errno)
260 defer d.Close()
261
262 isDir, errno := d.IsDir()
263 require.EqualErrno(t, 0, errno)
264 require.True(t, isDir)
265 })
266 })
267 }
268
269 t.Run("OS dir", func(t *testing.T) {
270 d, errno := OpenOSFile(t.TempDir(), experimentalsys.O_RDONLY, 0)
271 require.EqualErrno(t, 0, errno)
272 defer d.Close()
273
274 isDir, errno := d.IsDir()
275 require.EqualErrno(t, 0, errno)
276 require.True(t, isDir)
277 })
278 }
279
280 func TestFileReadAndPread(t *testing.T) {
281 dirFS, embedFS, mapFS := dirEmbedMapFS(t, t.TempDir())
282
283 tests := []struct {
284 name string
285 fs fs.FS
286 }{
287 {name: "os.DirFS", fs: dirFS},
288 {name: "embed.api.FS", fs: embedFS},
289 {name: "fstest.MapFS", fs: mapFS},
290 }
291
292 buf := make([]byte, 3)
293
294 for _, tc := range tests {
295 tc := tc
296
297 t.Run(tc.name, func(t *testing.T) {
298 f, errno := OpenFSFile(tc.fs, wazeroFile, experimentalsys.O_RDONLY, 0)
299 require.EqualErrno(t, 0, errno)
300 defer f.Close()
301
302
303 requireRead(t, f, buf)
304 require.Equal(t, "waz", string(buf))
305 buf = buf[:]
306
307
308 requirePread(t, f, buf, 0)
309 require.Equal(t, "waz", string(buf))
310 buf = buf[:]
311
312
313 requireRead(t, f, buf)
314 require.Equal(t, "ero", string(buf))
315 buf = buf[:]
316
317
318 requirePread(t, f, buf, 2)
319 require.Equal(t, "zer", string(buf))
320 })
321 }
322 }
323
324 func TestFilePoll_POLLIN(t *testing.T) {
325 pflag := fsapi.POLLIN
326
327
328 r, w, err := os.Pipe()
329 require.NoError(t, err)
330 defer r.Close()
331 defer w.Close()
332
333 rF, err := NewStdioFile(true, r)
334 require.NoError(t, err)
335 buf := make([]byte, 10)
336 timeout := int32(0)
337
338
339 ready, errno := rF.Poll(pflag, timeout)
340 require.EqualErrno(t, 0, errno)
341 require.False(t, ready)
342
343
344 expected := []byte("wazero")
345 _, err = w.Write([]byte("wazero"))
346 require.NoError(t, err)
347
348
349 ready, errno = rF.Poll(pflag, timeout)
350 require.EqualErrno(t, 0, errno)
351 require.True(t, ready)
352
353
354 n, errno := rF.Read(buf)
355 require.EqualErrno(t, 0, errno)
356 require.Equal(t, len(expected), n)
357 require.Equal(t, expected, buf[:len(expected)])
358 }
359
360 func TestFilePoll_POLLOUT(t *testing.T) {
361 pflag := fsapi.POLLOUT
362
363
364 r, w, err := os.Pipe()
365 require.NoError(t, err)
366 defer r.Close()
367 defer w.Close()
368
369 wF, err := NewStdioFile(false, w)
370 require.NoError(t, err)
371 timeout := int32(0)
372
373
374 ready, errno := wF.Poll(pflag, timeout)
375 require.EqualErrno(t, experimentalsys.ENOTSUP, errno)
376 require.False(t, ready)
377 }
378
379 func requireRead(t *testing.T, f experimentalsys.File, buf []byte) {
380 n, errno := f.Read(buf)
381 require.EqualErrno(t, 0, errno)
382 require.Equal(t, len(buf), n)
383 }
384
385 func requirePread(t *testing.T, f experimentalsys.File, buf []byte, off int64) {
386 n, errno := f.Pread(buf, off)
387 require.EqualErrno(t, 0, errno)
388 require.Equal(t, len(buf), n)
389 }
390
391 func TestFileRead_empty(t *testing.T) {
392 dirFS, embedFS, mapFS := dirEmbedMapFS(t, t.TempDir())
393
394 tests := []struct {
395 name string
396 fs fs.FS
397 }{
398 {name: "os.DirFS", fs: dirFS},
399 {name: "embed.api.FS", fs: embedFS},
400 {name: "fstest.MapFS", fs: mapFS},
401 }
402
403 buf := make([]byte, 3)
404
405 for _, tc := range tests {
406 tc := tc
407
408 t.Run(tc.name, func(t *testing.T) {
409 f, errno := OpenFSFile(tc.fs, emptyFile, experimentalsys.O_RDONLY, 0)
410 require.EqualErrno(t, 0, errno)
411 defer f.Close()
412
413 t.Run("Read", func(t *testing.T) {
414
415 n, errno := f.Read(buf)
416 require.EqualErrno(t, 0, errno)
417 require.Zero(t, n)
418 })
419
420 t.Run("Pread", func(t *testing.T) {
421 n, errno := f.Pread(buf, 0)
422 require.EqualErrno(t, 0, errno)
423 require.Zero(t, n)
424 })
425 })
426 }
427 }
428
429 type maskFS struct {
430 fs.FS
431 }
432
433 func (m *maskFS) Open(name string) (fs.File, error) {
434 f, err := m.FS.Open(name)
435 return struct{ fs.File }{f}, err
436 }
437
438 func TestFilePread_Unsupported(t *testing.T) {
439 embedFS, err := fs.Sub(testdata, "testdata")
440 require.NoError(t, err)
441
442 f, errno := OpenFSFile(&maskFS{embedFS}, emptyFile, experimentalsys.O_RDONLY, 0)
443 require.EqualErrno(t, 0, errno)
444 defer f.Close()
445
446 buf := make([]byte, 3)
447 _, errno = f.Pread(buf, 0)
448 require.EqualErrno(t, experimentalsys.ENOSYS, errno)
449 }
450
451 func TestFileRead_Errors(t *testing.T) {
452
453 path := path.Join(t.TempDir(), emptyFile)
454
455
456 flag := experimentalsys.O_WRONLY | experimentalsys.O_CREAT
457 f := requireOpenFile(t, path, flag, 0o600)
458 defer f.Close()
459 buf := make([]byte, 5)
460
461 tests := []struct {
462 name string
463 fn func(experimentalsys.File) experimentalsys.Errno
464 }{
465 {name: "Read", fn: func(f experimentalsys.File) experimentalsys.Errno {
466 _, errno := f.Read(buf)
467 return errno
468 }},
469 {name: "Pread", fn: func(f experimentalsys.File) experimentalsys.Errno {
470 _, errno := f.Pread(buf, 0)
471 return errno
472 }},
473 }
474
475 for _, tc := range tests {
476 tc := tc
477
478 t.Run(tc.name, func(t *testing.T) {
479 t.Run("EBADF when not open for reading", func(t *testing.T) {
480
481 errno := tc.fn(f)
482 require.EqualErrno(t, experimentalsys.EBADF, errno)
483 })
484 testEISDIR(t, tc.fn)
485 })
486 }
487 }
488
489 func TestFileSeek(t *testing.T) {
490 tmpDir := t.TempDir()
491 dirFS, embedFS, mapFS := dirEmbedMapFS(t, tmpDir)
492
493 tests := []struct {
494 name string
495 openFile func(string) (experimentalsys.File, experimentalsys.Errno)
496 }{
497 {name: "fsFile os.DirFS", openFile: func(name string) (experimentalsys.File, experimentalsys.Errno) {
498 return OpenFSFile(dirFS, name, experimentalsys.O_RDONLY, 0)
499 }},
500 {name: "fsFile embed.api.FS", openFile: func(name string) (experimentalsys.File, experimentalsys.Errno) {
501 return OpenFSFile(embedFS, name, experimentalsys.O_RDONLY, 0)
502 }},
503 {name: "fsFile fstest.MapFS", openFile: func(name string) (experimentalsys.File, experimentalsys.Errno) {
504 return OpenFSFile(mapFS, name, experimentalsys.O_RDONLY, 0)
505 }},
506 {name: "osFile", openFile: func(name string) (experimentalsys.File, experimentalsys.Errno) {
507 return OpenOSFile(path.Join(tmpDir, name), experimentalsys.O_RDONLY, 0o666)
508 }},
509 }
510
511 buf := make([]byte, 3)
512
513 for _, tc := range tests {
514 tc := tc
515
516 t.Run(tc.name, func(t *testing.T) {
517 f, errno := tc.openFile(wazeroFile)
518 require.EqualErrno(t, 0, errno)
519 defer f.Close()
520
521
522 _, errno = f.Seek(0, io.SeekEnd+1)
523 require.EqualErrno(t, experimentalsys.EINVAL, errno)
524 _, errno = f.Seek(0, -1)
525 require.EqualErrno(t, experimentalsys.EINVAL, errno)
526
527
528 _, errno = f.Seek(-1, io.SeekStart)
529 require.EqualErrno(t, experimentalsys.EINVAL, errno)
530
531 requireRead(t, f, buf)
532
533
534 newOffset, errno := f.Seek(0, io.SeekStart)
535 require.EqualErrno(t, 0, errno)
536
537
538 require.Zero(t, newOffset)
539 requireRead(t, f, buf)
540 require.Equal(t, "waz", string(buf))
541 buf = buf[:]
542
543
544 newOffset, errno = f.Seek(0, io.SeekCurrent)
545 require.EqualErrno(t, 0, errno)
546 require.Equal(t, int64(3), newOffset)
547
548
549 newOffset, errno = f.Seek(-2, io.SeekEnd)
550 require.EqualErrno(t, 0, errno)
551
552
553 require.Equal(t, int64(5), newOffset)
554 n, errno := f.Read(buf)
555 require.EqualErrno(t, 0, errno)
556 require.Equal(t, 2, n)
557 require.Equal(t, "o\n", string(buf[:2]))
558
559 t.Run("directory seek to zero", func(t *testing.T) {
560 dotF, errno := tc.openFile(".")
561 require.EqualErrno(t, 0, errno)
562 defer dotF.Close()
563
564 dirents, errno := dotF.Readdir(-1)
565 require.EqualErrno(t, 0, errno)
566 direntCount := len(dirents)
567 require.False(t, direntCount == 0)
568
569
570 newOffset, errno := dotF.Seek(0, io.SeekStart)
571 require.EqualErrno(t, 0, errno)
572 require.Zero(t, newOffset)
573
574
575 newOffset, errno = dotF.Seek(0, io.SeekStart)
576 require.EqualErrno(t, 0, errno)
577 require.Zero(t, newOffset)
578
579
580 dirents, errno = dotF.Readdir(-1)
581 require.EqualErrno(t, 0, errno)
582 require.Equal(t, direntCount, len(dirents))
583 })
584
585 seekToZero := func(f experimentalsys.File) experimentalsys.Errno {
586 _, errno := f.Seek(0, io.SeekStart)
587 return errno
588 }
589 testEBADFIfFileClosed(t, seekToZero)
590 })
591 }
592 }
593
594 func requireSeek(t *testing.T, f experimentalsys.File, off int64, whence int) int64 {
595 n, errno := f.Seek(off, whence)
596 require.EqualErrno(t, 0, errno)
597 return n
598 }
599
600 func TestFileSeek_empty(t *testing.T) {
601 dirFS, embedFS, mapFS := dirEmbedMapFS(t, t.TempDir())
602
603 tests := []struct {
604 name string
605 fs fs.FS
606 }{
607 {name: "os.DirFS", fs: dirFS},
608 {name: "embed.api.FS", fs: embedFS},
609 {name: "fstest.MapFS", fs: mapFS},
610 }
611
612 for _, tc := range tests {
613 tc := tc
614
615 t.Run(tc.name, func(t *testing.T) {
616 f, errno := OpenFSFile(tc.fs, emptyFile, experimentalsys.O_RDONLY, 0)
617 require.EqualErrno(t, 0, errno)
618 defer f.Close()
619
620 t.Run("Start", func(t *testing.T) {
621 require.Zero(t, requireSeek(t, f, 0, io.SeekStart))
622 })
623
624 t.Run("Current", func(t *testing.T) {
625 require.Zero(t, requireSeek(t, f, 0, io.SeekCurrent))
626 })
627
628 t.Run("End", func(t *testing.T) {
629 require.Zero(t, requireSeek(t, f, 0, io.SeekEnd))
630 })
631 })
632 }
633 }
634
635 func TestFileSeek_Unsupported(t *testing.T) {
636 embedFS, err := fs.Sub(testdata, "testdata")
637 require.NoError(t, err)
638
639 f, errno := OpenFSFile(&maskFS{embedFS}, emptyFile, experimentalsys.O_RDONLY, 0)
640 require.EqualErrno(t, 0, errno)
641 defer f.Close()
642
643 _, errno = f.Seek(0, io.SeekCurrent)
644 require.EqualErrno(t, experimentalsys.ENOSYS, errno)
645 }
646
647 func TestFileWriteAndPwrite(t *testing.T) {
648
649
650 path := path.Join(t.TempDir(), wazeroFile)
651 f := requireOpenFile(t, path, experimentalsys.O_RDWR|experimentalsys.O_CREAT, 0o600)
652 defer f.Close()
653
654 text := "wazero"
655 buf := make([]byte, 3)
656 copy(buf, text[:3])
657
658
659 requireWrite(t, f, buf)
660
661
662 requirePwrite(t, f, buf, 6)
663
664 copy(buf, text[3:])
665
666
667
668 requireWrite(t, f, buf)
669
670
671 requirePwrite(t, f, buf, 9)
672
673
674 requirePwrite(t, f, buf, 12)
675
676 b, err := os.ReadFile(path)
677 require.NoError(t, err)
678
679
680
681
682
683
684
685 require.Equal(t, "wazerowazeroero", string(b))
686 }
687
688 func requireWrite(t *testing.T, f experimentalsys.File, buf []byte) {
689 n, errno := f.Write(buf)
690 require.EqualErrno(t, 0, errno)
691 require.Equal(t, len(buf), n)
692 }
693
694 func requirePwrite(t *testing.T, f experimentalsys.File, buf []byte, off int64) {
695 n, errno := f.Pwrite(buf, off)
696 require.EqualErrno(t, 0, errno)
697 require.Equal(t, len(buf), n)
698 }
699
700 func TestFileWrite_empty(t *testing.T) {
701
702
703 path := path.Join(t.TempDir(), emptyFile)
704 f := requireOpenFile(t, path, experimentalsys.O_RDWR|experimentalsys.O_CREAT, 0o600)
705 defer f.Close()
706
707 tests := []struct {
708 name string
709 fn func(experimentalsys.File, []byte) (int, experimentalsys.Errno)
710 }{
711 {name: "Write", fn: func(f experimentalsys.File, buf []byte) (int, experimentalsys.Errno) {
712 return f.Write(buf)
713 }},
714 {name: "Pwrite from zero", fn: func(f experimentalsys.File, buf []byte) (int, experimentalsys.Errno) {
715 return f.Pwrite(buf, 0)
716 }},
717 {name: "Pwrite from 3", fn: func(f experimentalsys.File, buf []byte) (int, experimentalsys.Errno) {
718 return f.Pwrite(buf, 3)
719 }},
720 }
721
722 var emptyBuf []byte
723
724 for _, tc := range tests {
725 tc := tc
726
727 t.Run(tc.name, func(t *testing.T) {
728 n, errno := tc.fn(f, emptyBuf)
729 require.EqualErrno(t, 0, errno)
730 require.Zero(t, n)
731
732
733 b, err := os.ReadFile(path)
734 require.NoError(t, err)
735 require.Zero(t, len(b))
736 })
737 }
738 }
739
740 func TestFileWrite_Unsupported(t *testing.T) {
741 embedFS, err := fs.Sub(testdata, "testdata")
742 require.NoError(t, err)
743
744
745 f, errno := OpenFSFile(&maskFS{embedFS}, wazeroFile, experimentalsys.O_RDWR, 0)
746 require.EqualErrno(t, 0, errno)
747 defer f.Close()
748
749 tests := []struct {
750 name string
751 fn func(experimentalsys.File, []byte) (int, experimentalsys.Errno)
752 }{
753 {name: "Write", fn: func(f experimentalsys.File, buf []byte) (int, experimentalsys.Errno) {
754 return f.Write(buf)
755 }},
756 {name: "Pwrite", fn: func(f experimentalsys.File, buf []byte) (int, experimentalsys.Errno) {
757 return f.Pwrite(buf, 0)
758 }},
759 }
760
761 buf := []byte("wazero")
762
763 for _, tc := range tests {
764 tc := tc
765
766 t.Run(tc.name, func(t *testing.T) {
767 _, errno := tc.fn(f, buf)
768 require.EqualErrno(t, experimentalsys.ENOSYS, errno)
769 })
770 }
771 }
772
773 func TestFileWrite_Errors(t *testing.T) {
774
775 path := path.Join(t.TempDir(), emptyFile)
776 of, err := os.Create(path)
777 require.NoError(t, err)
778 require.NoError(t, of.Close())
779
780
781 flag := experimentalsys.O_RDONLY
782 f := requireOpenFile(t, path, flag, 0o600)
783 defer f.Close()
784 buf := []byte("wazero")
785
786 tests := []struct {
787 name string
788 fn func(experimentalsys.File) experimentalsys.Errno
789 }{
790 {name: "Write", fn: func(f experimentalsys.File) experimentalsys.Errno {
791 _, errno := f.Write(buf)
792 return errno
793 }},
794 {name: "Pwrite", fn: func(f experimentalsys.File) experimentalsys.Errno {
795 _, errno := f.Pwrite(buf, 0)
796 return errno
797 }},
798 }
799
800 for _, tc := range tests {
801 tc := tc
802
803 t.Run(tc.name, func(t *testing.T) {
804 t.Run("EBADF when not open for writing", func(t *testing.T) {
805
806 errno := tc.fn(f)
807 require.EqualErrno(t, experimentalsys.EBADF, errno)
808 })
809 testEISDIR(t, tc.fn)
810 })
811 }
812 }
813
814 func TestFileSync_NoError(t *testing.T) {
815 testSync_NoError(t, experimentalsys.File.Sync)
816 }
817
818 func TestFileDatasync_NoError(t *testing.T) {
819 testSync_NoError(t, experimentalsys.File.Datasync)
820 }
821
822 func testSync_NoError(t *testing.T, sync func(experimentalsys.File) experimentalsys.Errno) {
823 roPath := "file_test.go"
824 ro, errno := OpenFSFile(embedFS, roPath, experimentalsys.O_RDONLY, 0)
825 require.EqualErrno(t, 0, errno)
826 defer ro.Close()
827
828 rwPath := path.Join(t.TempDir(), "datasync")
829 rw, errno := OpenOSFile(rwPath, experimentalsys.O_CREAT|experimentalsys.O_RDWR, 0o600)
830 require.EqualErrno(t, 0, errno)
831 defer rw.Close()
832
833 tests := []struct {
834 name string
835 f experimentalsys.File
836 }{
837 {name: "UnimplementedFile", f: experimentalsys.UnimplementedFile{}},
838 {name: "File of read-only FS.File", f: ro},
839 {name: "File of os.File", f: rw},
840 }
841
842 for _, tt := range tests {
843 tc := tt
844
845 t.Run(tc.name, func(t *testing.T) {
846 require.EqualErrno(t, 0, sync(tc.f))
847 })
848 }
849 }
850
851 func TestFileSync(t *testing.T) {
852 testSync(t, experimentalsys.File.Sync)
853 }
854
855 func TestFileDatasync(t *testing.T) {
856 testSync(t, experimentalsys.File.Datasync)
857 }
858
859
860
861
862 func testSync(t *testing.T, sync func(experimentalsys.File) experimentalsys.Errno) {
863
864 dPath := t.TempDir()
865 d := requireOpenFile(t, dPath, experimentalsys.O_RDONLY, 0)
866 defer d.Close()
867
868 errno := sync(d)
869 require.EqualErrno(t, 0, errno)
870
871 fPath := path.Join(dPath, t.Name())
872
873 f := requireOpenFile(t, fPath, experimentalsys.O_RDWR|experimentalsys.O_CREAT, 0o600)
874 defer f.Close()
875
876 expected := "hello world!"
877
878
879 _, errno = f.Write([]byte(expected))
880 require.EqualErrno(t, 0, errno)
881
882
883 errno = sync(f)
884 require.EqualErrno(t, 0, errno)
885
886
887 _, errno = f.Seek(0, io.SeekStart)
888 require.EqualErrno(t, 0, errno)
889
890
891 buf := make([]byte, 50)
892 n, errno := f.Read(buf)
893 require.EqualErrno(t, 0, errno)
894
895
896 require.Equal(t, expected, string(buf[:n]))
897
898
899 if runtime.GOOS != "windows" {
900 testEBADFIfFileClosed(t, sync)
901 testEBADFIfDirClosed(t, sync)
902 }
903 }
904
905 func TestFileTruncate(t *testing.T) {
906 content := []byte("123456")
907
908 tests := []struct {
909 name string
910 size int64
911 expectedContent []byte
912 expectedErr error
913 }{
914 {
915 name: "one less",
916 size: 5,
917 expectedContent: []byte("12345"),
918 },
919 {
920 name: "same",
921 size: 6,
922 expectedContent: content,
923 },
924 {
925 name: "zero",
926 size: 0,
927 expectedContent: []byte(""),
928 },
929 {
930 name: "larger",
931 size: 106,
932 expectedContent: append(content, make([]byte, 100)...),
933 },
934 }
935
936 for _, tt := range tests {
937 tc := tt
938 t.Run(tc.name, func(t *testing.T) {
939 tmpDir := t.TempDir()
940
941 fPath := path.Join(tmpDir, tc.name)
942 f := openForWrite(t, fPath, content)
943 defer f.Close()
944
945 errno := f.Truncate(tc.size)
946 require.EqualErrno(t, 0, errno)
947
948 actual, err := os.ReadFile(fPath)
949 require.NoError(t, err)
950 require.Equal(t, tc.expectedContent, actual)
951 })
952 }
953
954 truncateToZero := func(f experimentalsys.File) experimentalsys.Errno {
955 return f.Truncate(0)
956 }
957
958 if runtime.GOOS != "windows" {
959
960 testEBADFIfFileClosed(t, truncateToZero)
961 }
962
963 testEISDIR(t, truncateToZero)
964
965 t.Run("negative", func(t *testing.T) {
966 tmpDir := t.TempDir()
967
968 f := openForWrite(t, path.Join(tmpDir, "truncate"), content)
969 defer f.Close()
970
971 errno := f.Truncate(-1)
972 require.EqualErrno(t, experimentalsys.EINVAL, errno)
973 })
974 }
975
976 func TestFileUtimens(t *testing.T) {
977 switch runtime.GOOS {
978 case "linux", "darwin":
979 case "freebsd":
980 case "windows":
981 if !platform.IsAtLeastGo120 {
982 t.Skip("windows only works after Go 1.20")
983 }
984 default:
985 t.Skip("unsupported GOOS", runtime.GOOS)
986 }
987
988 testUtimens(t, true)
989
990 testEBADFIfFileClosed(t, func(f experimentalsys.File) experimentalsys.Errno {
991 return f.Utimens(experimentalsys.UTIME_OMIT, experimentalsys.UTIME_OMIT)
992 })
993 testEBADFIfDirClosed(t, func(d experimentalsys.File) experimentalsys.Errno {
994 return d.Utimens(experimentalsys.UTIME_OMIT, experimentalsys.UTIME_OMIT)
995 })
996 }
997
998 func TestNewStdioFile(t *testing.T) {
999
1000 f, err := os.CreateTemp(t.TempDir(), "somefile")
1001 require.NoError(t, err)
1002 defer f.Close()
1003
1004 stdin, err := NewStdioFile(true, os.Stdin)
1005 require.NoError(t, err)
1006 stdinStat, err := os.Stdin.Stat()
1007 require.NoError(t, err)
1008
1009 stdinFile, err := NewStdioFile(true, f)
1010 require.NoError(t, err)
1011
1012 stdout, err := NewStdioFile(false, os.Stdout)
1013 require.NoError(t, err)
1014 stdoutStat, err := os.Stdout.Stat()
1015 require.NoError(t, err)
1016
1017 stdoutFile, err := NewStdioFile(false, f)
1018 require.NoError(t, err)
1019
1020 tests := []struct {
1021 name string
1022 f experimentalsys.File
1023
1024
1025 expectedType fs.FileMode
1026 }{
1027 {
1028 name: "stdin",
1029 f: stdin,
1030 expectedType: stdinStat.Mode().Type(),
1031 },
1032 {
1033 name: "stdin file",
1034 f: stdinFile,
1035 expectedType: 0,
1036 },
1037 {
1038 name: "stdout",
1039 f: stdout,
1040 expectedType: stdoutStat.Mode().Type(),
1041 },
1042 {
1043 name: "stdout file",
1044 f: stdoutFile,
1045 expectedType: 0,
1046 },
1047 }
1048
1049 for _, tt := range tests {
1050 tc := tt
1051
1052 t.Run(tc.name+" Stat", func(t *testing.T) {
1053 st, errno := tc.f.Stat()
1054 require.EqualErrno(t, 0, errno)
1055 require.Equal(t, tc.expectedType, st.Mode&fs.ModeType)
1056 require.Equal(t, uint64(1), st.Nlink)
1057
1058
1059
1060 require.Zero(t, st.Ctim)
1061 require.Zero(t, st.Mtim)
1062 require.Zero(t, st.Atim)
1063 })
1064 }
1065 }
1066
1067 func testEBADFIfDirClosed(t *testing.T, fn func(experimentalsys.File) experimentalsys.Errno) bool {
1068 return t.Run("EBADF if dir closed", func(t *testing.T) {
1069 d := requireOpenFile(t, t.TempDir(), experimentalsys.O_RDONLY, 0o755)
1070
1071
1072 require.EqualErrno(t, 0, d.Close())
1073
1074 require.EqualErrno(t, experimentalsys.EBADF, fn(d))
1075 })
1076 }
1077
1078 func testEBADFIfFileClosed(t *testing.T, fn func(experimentalsys.File) experimentalsys.Errno) bool {
1079 return t.Run("EBADF if file closed", func(t *testing.T) {
1080 tmpDir := t.TempDir()
1081
1082 f := openForWrite(t, path.Join(tmpDir, "EBADF"), []byte{1, 2, 3, 4})
1083
1084
1085 require.EqualErrno(t, 0, f.Close())
1086
1087 require.EqualErrno(t, experimentalsys.EBADF, fn(f))
1088 })
1089 }
1090
1091 func testEISDIR(t *testing.T, fn func(experimentalsys.File) experimentalsys.Errno) bool {
1092 return t.Run("EISDIR if directory", func(t *testing.T) {
1093 f := requireOpenFile(t, os.TempDir(), experimentalsys.O_RDONLY|experimentalsys.O_DIRECTORY, 0o666)
1094 defer f.Close()
1095
1096 require.EqualErrno(t, experimentalsys.EISDIR, fn(f))
1097 })
1098 }
1099
1100 func openForWrite(t *testing.T, path string, content []byte) experimentalsys.File {
1101 require.NoError(t, os.WriteFile(path, content, 0o0666))
1102 f := requireOpenFile(t, path, experimentalsys.O_RDWR, 0o666)
1103 _, errno := f.Write(content)
1104 require.EqualErrno(t, 0, errno)
1105 return f
1106 }
1107
1108 func requireOpenFile(t *testing.T, path string, flag experimentalsys.Oflag, perm fs.FileMode) experimentalsys.File {
1109 f, errno := OpenOSFile(path, flag, perm)
1110 require.EqualErrno(t, 0, errno)
1111 return f
1112 }
1113
1114 func dirEmbedMapFS(t *testing.T, tmpDir string) (fs.FS, fs.FS, fs.FS) {
1115 embedFS, err := fs.Sub(testdata, "testdata")
1116 require.NoError(t, err)
1117
1118 f, err := embedFS.Open(wazeroFile)
1119 require.NoError(t, err)
1120 defer f.Close()
1121
1122 bytes, err := io.ReadAll(f)
1123 require.NoError(t, err)
1124
1125 mapFS := gofstest.MapFS{
1126 emptyFile: &gofstest.MapFile{},
1127 wazeroFile: &gofstest.MapFile{Data: bytes},
1128 }
1129
1130
1131
1132 require.NoError(t, os.WriteFile(path.Join(tmpDir, emptyFile), nil, 0o600))
1133 require.NoError(t, os.WriteFile(path.Join(tmpDir, wazeroFile), bytes, 0o600))
1134 dirFS := os.DirFS(tmpDir)
1135 return dirFS, embedFS, mapFS
1136 }
1137
View as plain text