Source file
src/os/os_test.go
1
2
3
4
5 package os_test
6
7 import (
8 "errors"
9 "flag"
10 "fmt"
11 "internal/testenv"
12 "io"
13 "io/fs"
14 . "os"
15 "os/exec"
16 "path/filepath"
17 "reflect"
18 "runtime"
19 "runtime/debug"
20 "sort"
21 "strings"
22 "sync"
23 "syscall"
24 "testing"
25 "testing/fstest"
26 "time"
27 )
28
29 func TestMain(m *testing.M) {
30 if Getenv("GO_OS_TEST_DRAIN_STDIN") == "1" {
31 Stdout.Close()
32 io.Copy(io.Discard, Stdin)
33 Exit(0)
34 }
35
36 Exit(m.Run())
37 }
38
39 var dot = []string{
40 "dir_unix.go",
41 "env.go",
42 "error.go",
43 "file.go",
44 "os_test.go",
45 "types.go",
46 "stat_darwin.go",
47 "stat_linux.go",
48 }
49
50 type sysDir struct {
51 name string
52 files []string
53 }
54
55 var sysdir = func() *sysDir {
56 switch runtime.GOOS {
57 case "android":
58 return &sysDir{
59 "/system/lib",
60 []string{
61 "libmedia.so",
62 "libpowermanager.so",
63 },
64 }
65 case "ios":
66 wd, err := syscall.Getwd()
67 if err != nil {
68 wd = err.Error()
69 }
70 sd := &sysDir{
71 filepath.Join(wd, "..", ".."),
72 []string{
73 "ResourceRules.plist",
74 "Info.plist",
75 },
76 }
77 found := true
78 for _, f := range sd.files {
79 path := filepath.Join(sd.name, f)
80 if _, err := Stat(path); err != nil {
81 found = false
82 break
83 }
84 }
85 if found {
86 return sd
87 }
88
89
90 case "windows":
91 return &sysDir{
92 Getenv("SystemRoot") + "\\system32\\drivers\\etc",
93 []string{
94 "networks",
95 "protocol",
96 "services",
97 },
98 }
99 case "plan9":
100 return &sysDir{
101 "/lib/ndb",
102 []string{
103 "common",
104 "local",
105 },
106 }
107 case "wasip1":
108
109
110
111 return &sysDir{
112 runtime.GOROOT(),
113 []string{
114 "go.env",
115 "LICENSE",
116 "CONTRIBUTING.md",
117 },
118 }
119 }
120 return &sysDir{
121 "/etc",
122 []string{
123 "group",
124 "hosts",
125 "passwd",
126 },
127 }
128 }()
129
130 func size(name string, t *testing.T) int64 {
131 file, err := Open(name)
132 if err != nil {
133 t.Fatal("open failed:", err)
134 }
135 defer func() {
136 if err := file.Close(); err != nil {
137 t.Error(err)
138 }
139 }()
140 n, err := io.Copy(io.Discard, file)
141 if err != nil {
142 t.Fatal(err)
143 }
144 return n
145 }
146
147 func equal(name1, name2 string) (r bool) {
148 switch runtime.GOOS {
149 case "windows":
150 r = strings.ToLower(name1) == strings.ToLower(name2)
151 default:
152 r = name1 == name2
153 }
154 return
155 }
156
157
158 func localTmp() string {
159 switch runtime.GOOS {
160 case "android", "ios", "windows":
161 return TempDir()
162 }
163 return "/tmp"
164 }
165
166 func newFile(testName string, t *testing.T) (f *File) {
167 f, err := CreateTemp(localTmp(), "_Go_"+testName)
168 if err != nil {
169 t.Fatalf("TempFile %s: %s", testName, err)
170 }
171 return
172 }
173
174 func newDir(testName string, t *testing.T) (name string) {
175 name, err := MkdirTemp(localTmp(), "_Go_"+testName)
176 if err != nil {
177 t.Fatalf("TempDir %s: %s", testName, err)
178 }
179 return
180 }
181
182 var sfdir = sysdir.name
183 var sfname = sysdir.files[0]
184
185 func TestStat(t *testing.T) {
186 t.Parallel()
187
188 path := sfdir + "/" + sfname
189 dir, err := Stat(path)
190 if err != nil {
191 t.Fatal("stat failed:", err)
192 }
193 if !equal(sfname, dir.Name()) {
194 t.Error("name should be ", sfname, "; is", dir.Name())
195 }
196 filesize := size(path, t)
197 if dir.Size() != filesize {
198 t.Error("size should be", filesize, "; is", dir.Size())
199 }
200 }
201
202 func TestStatError(t *testing.T) {
203 defer chtmpdir(t)()
204
205 path := "no-such-file"
206
207 fi, err := Stat(path)
208 if err == nil {
209 t.Fatal("got nil, want error")
210 }
211 if fi != nil {
212 t.Errorf("got %v, want nil", fi)
213 }
214 if perr, ok := err.(*PathError); !ok {
215 t.Errorf("got %T, want %T", err, perr)
216 }
217
218 testenv.MustHaveSymlink(t)
219
220 link := "symlink"
221 err = Symlink(path, link)
222 if err != nil {
223 t.Fatal(err)
224 }
225
226 fi, err = Stat(link)
227 if err == nil {
228 t.Fatal("got nil, want error")
229 }
230 if fi != nil {
231 t.Errorf("got %v, want nil", fi)
232 }
233 if perr, ok := err.(*PathError); !ok {
234 t.Errorf("got %T, want %T", err, perr)
235 }
236 }
237
238 func TestStatSymlinkLoop(t *testing.T) {
239 testenv.MustHaveSymlink(t)
240
241 defer chtmpdir(t)()
242
243 err := Symlink("x", "y")
244 if err != nil {
245 t.Fatal(err)
246 }
247 defer Remove("y")
248
249 err = Symlink("y", "x")
250 if err != nil {
251 t.Fatal(err)
252 }
253 defer Remove("x")
254
255 _, err = Stat("x")
256 if _, ok := err.(*fs.PathError); !ok {
257 t.Errorf("expected *PathError, got %T: %v\n", err, err)
258 }
259 }
260
261 func TestFstat(t *testing.T) {
262 t.Parallel()
263
264 path := sfdir + "/" + sfname
265 file, err1 := Open(path)
266 if err1 != nil {
267 t.Fatal("open failed:", err1)
268 }
269 defer file.Close()
270 dir, err2 := file.Stat()
271 if err2 != nil {
272 t.Fatal("fstat failed:", err2)
273 }
274 if !equal(sfname, dir.Name()) {
275 t.Error("name should be ", sfname, "; is", dir.Name())
276 }
277 filesize := size(path, t)
278 if dir.Size() != filesize {
279 t.Error("size should be", filesize, "; is", dir.Size())
280 }
281 }
282
283 func TestLstat(t *testing.T) {
284 t.Parallel()
285
286 path := sfdir + "/" + sfname
287 dir, err := Lstat(path)
288 if err != nil {
289 t.Fatal("lstat failed:", err)
290 }
291 if !equal(sfname, dir.Name()) {
292 t.Error("name should be ", sfname, "; is", dir.Name())
293 }
294 if dir.Mode()&ModeSymlink == 0 {
295 filesize := size(path, t)
296 if dir.Size() != filesize {
297 t.Error("size should be", filesize, "; is", dir.Size())
298 }
299 }
300 }
301
302
303 func TestRead0(t *testing.T) {
304 t.Parallel()
305
306 path := sfdir + "/" + sfname
307 f, err := Open(path)
308 if err != nil {
309 t.Fatal("open failed:", err)
310 }
311 defer f.Close()
312
313 b := make([]byte, 0)
314 n, err := f.Read(b)
315 if n != 0 || err != nil {
316 t.Errorf("Read(0) = %d, %v, want 0, nil", n, err)
317 }
318 b = make([]byte, 100)
319 n, err = f.Read(b)
320 if n <= 0 || err != nil {
321 t.Errorf("Read(100) = %d, %v, want >0, nil", n, err)
322 }
323 }
324
325
326 func TestReadClosed(t *testing.T) {
327 t.Parallel()
328
329 path := sfdir + "/" + sfname
330 file, err := Open(path)
331 if err != nil {
332 t.Fatal("open failed:", err)
333 }
334 file.Close()
335
336 b := make([]byte, 100)
337 _, err = file.Read(b)
338
339 e, ok := err.(*PathError)
340 if !ok || e.Err != ErrClosed {
341 t.Fatalf("Read: got %T(%v), want %T(%v)", err, err, e, ErrClosed)
342 }
343 }
344
345 func testReaddirnames(dir string, contents []string) func(*testing.T) {
346 return func(t *testing.T) {
347 t.Parallel()
348
349 file, err := Open(dir)
350 if err != nil {
351 t.Fatalf("open %q failed: %v", dir, err)
352 }
353 defer file.Close()
354 s, err2 := file.Readdirnames(-1)
355 if err2 != nil {
356 t.Fatalf("Readdirnames %q failed: %v", dir, err2)
357 }
358 for _, m := range contents {
359 found := false
360 for _, n := range s {
361 if n == "." || n == ".." {
362 t.Errorf("got %q in directory", n)
363 }
364 if !equal(m, n) {
365 continue
366 }
367 if found {
368 t.Error("present twice:", m)
369 }
370 found = true
371 }
372 if !found {
373 t.Error("could not find", m)
374 }
375 }
376 if s == nil {
377 t.Error("Readdirnames returned nil instead of empty slice")
378 }
379 }
380 }
381
382 func testReaddir(dir string, contents []string) func(*testing.T) {
383 return func(t *testing.T) {
384 t.Parallel()
385
386 file, err := Open(dir)
387 if err != nil {
388 t.Fatalf("open %q failed: %v", dir, err)
389 }
390 defer file.Close()
391 s, err2 := file.Readdir(-1)
392 if err2 != nil {
393 t.Fatalf("Readdir %q failed: %v", dir, err2)
394 }
395 for _, m := range contents {
396 found := false
397 for _, n := range s {
398 if n.Name() == "." || n.Name() == ".." {
399 t.Errorf("got %q in directory", n.Name())
400 }
401 if !equal(m, n.Name()) {
402 continue
403 }
404 if found {
405 t.Error("present twice:", m)
406 }
407 found = true
408 }
409 if !found {
410 t.Error("could not find", m)
411 }
412 }
413 if s == nil {
414 t.Error("Readdir returned nil instead of empty slice")
415 }
416 }
417 }
418
419 func testReadDir(dir string, contents []string) func(*testing.T) {
420 return func(t *testing.T) {
421 t.Parallel()
422
423 file, err := Open(dir)
424 if err != nil {
425 t.Fatalf("open %q failed: %v", dir, err)
426 }
427 defer file.Close()
428 s, err2 := file.ReadDir(-1)
429 if err2 != nil {
430 t.Fatalf("ReadDir %q failed: %v", dir, err2)
431 }
432 for _, m := range contents {
433 found := false
434 for _, n := range s {
435 if n.Name() == "." || n.Name() == ".." {
436 t.Errorf("got %q in directory", n)
437 }
438 if !equal(m, n.Name()) {
439 continue
440 }
441 if found {
442 t.Error("present twice:", m)
443 }
444 found = true
445 lstat, err := Lstat(dir + "/" + m)
446 if err != nil {
447 t.Fatal(err)
448 }
449 if n.IsDir() != lstat.IsDir() {
450 t.Errorf("%s: IsDir=%v, want %v", m, n.IsDir(), lstat.IsDir())
451 }
452 if n.Type() != lstat.Mode().Type() {
453 t.Errorf("%s: IsDir=%v, want %v", m, n.Type(), lstat.Mode().Type())
454 }
455 info, err := n.Info()
456 if err != nil {
457 t.Errorf("%s: Info: %v", m, err)
458 continue
459 }
460 if !SameFile(info, lstat) {
461 t.Errorf("%s: Info: SameFile(info, lstat) = false", m)
462 }
463 }
464 if !found {
465 t.Error("could not find", m)
466 }
467 }
468 if s == nil {
469 t.Error("ReadDir returned nil instead of empty slice")
470 }
471 }
472 }
473
474 func TestFileReaddirnames(t *testing.T) {
475 t.Parallel()
476
477 t.Run(".", testReaddirnames(".", dot))
478 t.Run("sysdir", testReaddirnames(sysdir.name, sysdir.files))
479 t.Run("TempDir", testReaddirnames(t.TempDir(), nil))
480 }
481
482 func TestFileReaddir(t *testing.T) {
483 t.Parallel()
484
485 t.Run(".", testReaddir(".", dot))
486 t.Run("sysdir", testReaddir(sysdir.name, sysdir.files))
487 t.Run("TempDir", testReaddir(t.TempDir(), nil))
488 }
489
490 func TestFileReadDir(t *testing.T) {
491 t.Parallel()
492
493 t.Run(".", testReadDir(".", dot))
494 t.Run("sysdir", testReadDir(sysdir.name, sysdir.files))
495 t.Run("TempDir", testReadDir(t.TempDir(), nil))
496 }
497
498 func benchmarkReaddirname(path string, b *testing.B) {
499 var nentries int
500 for i := 0; i < b.N; i++ {
501 f, err := Open(path)
502 if err != nil {
503 b.Fatalf("open %q failed: %v", path, err)
504 }
505 ns, err := f.Readdirnames(-1)
506 f.Close()
507 if err != nil {
508 b.Fatalf("readdirnames %q failed: %v", path, err)
509 }
510 nentries = len(ns)
511 }
512 b.Logf("benchmarkReaddirname %q: %d entries", path, nentries)
513 }
514
515 func benchmarkReaddir(path string, b *testing.B) {
516 var nentries int
517 for i := 0; i < b.N; i++ {
518 f, err := Open(path)
519 if err != nil {
520 b.Fatalf("open %q failed: %v", path, err)
521 }
522 fs, err := f.Readdir(-1)
523 f.Close()
524 if err != nil {
525 b.Fatalf("readdir %q failed: %v", path, err)
526 }
527 nentries = len(fs)
528 }
529 b.Logf("benchmarkReaddir %q: %d entries", path, nentries)
530 }
531
532 func benchmarkReadDir(path string, b *testing.B) {
533 var nentries int
534 for i := 0; i < b.N; i++ {
535 f, err := Open(path)
536 if err != nil {
537 b.Fatalf("open %q failed: %v", path, err)
538 }
539 fs, err := f.ReadDir(-1)
540 f.Close()
541 if err != nil {
542 b.Fatalf("readdir %q failed: %v", path, err)
543 }
544 nentries = len(fs)
545 }
546 b.Logf("benchmarkReadDir %q: %d entries", path, nentries)
547 }
548
549 func BenchmarkReaddirname(b *testing.B) {
550 benchmarkReaddirname(".", b)
551 }
552
553 func BenchmarkReaddir(b *testing.B) {
554 benchmarkReaddir(".", b)
555 }
556
557 func BenchmarkReadDir(b *testing.B) {
558 benchmarkReadDir(".", b)
559 }
560
561 func benchmarkStat(b *testing.B, path string) {
562 b.ResetTimer()
563 for i := 0; i < b.N; i++ {
564 _, err := Stat(path)
565 if err != nil {
566 b.Fatalf("Stat(%q) failed: %v", path, err)
567 }
568 }
569 }
570
571 func benchmarkLstat(b *testing.B, path string) {
572 b.ResetTimer()
573 for i := 0; i < b.N; i++ {
574 _, err := Lstat(path)
575 if err != nil {
576 b.Fatalf("Lstat(%q) failed: %v", path, err)
577 }
578 }
579 }
580
581 func BenchmarkStatDot(b *testing.B) {
582 benchmarkStat(b, ".")
583 }
584
585 func BenchmarkStatFile(b *testing.B) {
586 benchmarkStat(b, filepath.Join(runtime.GOROOT(), "src/os/os_test.go"))
587 }
588
589 func BenchmarkStatDir(b *testing.B) {
590 benchmarkStat(b, filepath.Join(runtime.GOROOT(), "src/os"))
591 }
592
593 func BenchmarkLstatDot(b *testing.B) {
594 benchmarkLstat(b, ".")
595 }
596
597 func BenchmarkLstatFile(b *testing.B) {
598 benchmarkLstat(b, filepath.Join(runtime.GOROOT(), "src/os/os_test.go"))
599 }
600
601 func BenchmarkLstatDir(b *testing.B) {
602 benchmarkLstat(b, filepath.Join(runtime.GOROOT(), "src/os"))
603 }
604
605
606 func smallReaddirnames(file *File, length int, t *testing.T) []string {
607 names := make([]string, length)
608 count := 0
609 for {
610 d, err := file.Readdirnames(1)
611 if err == io.EOF {
612 break
613 }
614 if err != nil {
615 t.Fatalf("readdirnames %q failed: %v", file.Name(), err)
616 }
617 if len(d) == 0 {
618 t.Fatalf("readdirnames %q returned empty slice and no error", file.Name())
619 }
620 names[count] = d[0]
621 count++
622 }
623 return names[0:count]
624 }
625
626
627
628 func TestReaddirnamesOneAtATime(t *testing.T) {
629 t.Parallel()
630
631
632 dir := "/usr/bin"
633 switch runtime.GOOS {
634 case "android":
635 dir = "/system/bin"
636 case "ios", "wasip1":
637 wd, err := Getwd()
638 if err != nil {
639 t.Fatal(err)
640 }
641 dir = wd
642 case "plan9":
643 dir = "/bin"
644 case "windows":
645 dir = Getenv("SystemRoot") + "\\system32"
646 }
647 file, err := Open(dir)
648 if err != nil {
649 t.Fatalf("open %q failed: %v", dir, err)
650 }
651 defer file.Close()
652 all, err1 := file.Readdirnames(-1)
653 if err1 != nil {
654 t.Fatalf("readdirnames %q failed: %v", dir, err1)
655 }
656 file1, err2 := Open(dir)
657 if err2 != nil {
658 t.Fatalf("open %q failed: %v", dir, err2)
659 }
660 defer file1.Close()
661 small := smallReaddirnames(file1, len(all)+100, t)
662 if len(small) < len(all) {
663 t.Fatalf("len(small) is %d, less than %d", len(small), len(all))
664 }
665 for i, n := range all {
666 if small[i] != n {
667 t.Errorf("small read %q mismatch: %v", small[i], n)
668 }
669 }
670 }
671
672 func TestReaddirNValues(t *testing.T) {
673 if testing.Short() {
674 t.Skip("test.short; skipping")
675 }
676 t.Parallel()
677
678 dir := t.TempDir()
679 for i := 1; i <= 105; i++ {
680 f, err := Create(filepath.Join(dir, fmt.Sprintf("%d", i)))
681 if err != nil {
682 t.Fatalf("Create: %v", err)
683 }
684 f.Write([]byte(strings.Repeat("X", i)))
685 f.Close()
686 }
687
688 var d *File
689 openDir := func() {
690 var err error
691 d, err = Open(dir)
692 if err != nil {
693 t.Fatalf("Open directory: %v", err)
694 }
695 }
696
697 readdirExpect := func(n, want int, wantErr error) {
698 t.Helper()
699 fi, err := d.Readdir(n)
700 if err != wantErr {
701 t.Fatalf("Readdir of %d got error %v, want %v", n, err, wantErr)
702 }
703 if g, e := len(fi), want; g != e {
704 t.Errorf("Readdir of %d got %d files, want %d", n, g, e)
705 }
706 }
707
708 readDirExpect := func(n, want int, wantErr error) {
709 t.Helper()
710 de, err := d.ReadDir(n)
711 if err != wantErr {
712 t.Fatalf("ReadDir of %d got error %v, want %v", n, err, wantErr)
713 }
714 if g, e := len(de), want; g != e {
715 t.Errorf("ReadDir of %d got %d files, want %d", n, g, e)
716 }
717 }
718
719 readdirnamesExpect := func(n, want int, wantErr error) {
720 t.Helper()
721 fi, err := d.Readdirnames(n)
722 if err != wantErr {
723 t.Fatalf("Readdirnames of %d got error %v, want %v", n, err, wantErr)
724 }
725 if g, e := len(fi), want; g != e {
726 t.Errorf("Readdirnames of %d got %d files, want %d", n, g, e)
727 }
728 }
729
730 for _, fn := range []func(int, int, error){readdirExpect, readdirnamesExpect, readDirExpect} {
731
732 openDir()
733 fn(0, 105, nil)
734 fn(0, 0, nil)
735 d.Close()
736
737
738 openDir()
739 fn(-1, 105, nil)
740 fn(-2, 0, nil)
741 fn(0, 0, nil)
742 d.Close()
743
744
745 openDir()
746 fn(1, 1, nil)
747 fn(2, 2, nil)
748 fn(105, 102, nil)
749 fn(3, 0, io.EOF)
750 d.Close()
751 }
752 }
753
754 func touch(t *testing.T, name string) {
755 f, err := Create(name)
756 if err != nil {
757 t.Fatal(err)
758 }
759 if err := f.Close(); err != nil {
760 t.Fatal(err)
761 }
762 }
763
764 func TestReaddirStatFailures(t *testing.T) {
765 switch runtime.GOOS {
766 case "windows", "plan9":
767
768
769
770
771 t.Skipf("skipping test on %v", runtime.GOOS)
772 }
773
774 var xerr error
775 *LstatP = func(path string) (FileInfo, error) {
776 if xerr != nil && strings.HasSuffix(path, "x") {
777 return nil, xerr
778 }
779 return Lstat(path)
780 }
781 defer func() { *LstatP = Lstat }()
782
783 dir := t.TempDir()
784 touch(t, filepath.Join(dir, "good1"))
785 touch(t, filepath.Join(dir, "x"))
786 touch(t, filepath.Join(dir, "good2"))
787 readDir := func() ([]FileInfo, error) {
788 d, err := Open(dir)
789 if err != nil {
790 t.Fatal(err)
791 }
792 defer d.Close()
793 return d.Readdir(-1)
794 }
795 mustReadDir := func(testName string) []FileInfo {
796 fis, err := readDir()
797 if err != nil {
798 t.Fatalf("%s: Readdir: %v", testName, err)
799 }
800 return fis
801 }
802 names := func(fis []FileInfo) []string {
803 s := make([]string, len(fis))
804 for i, fi := range fis {
805 s[i] = fi.Name()
806 }
807 sort.Strings(s)
808 return s
809 }
810
811 if got, want := names(mustReadDir("initial readdir")),
812 []string{"good1", "good2", "x"}; !reflect.DeepEqual(got, want) {
813 t.Errorf("initial readdir got %q; want %q", got, want)
814 }
815
816 xerr = ErrNotExist
817 if got, want := names(mustReadDir("with x disappearing")),
818 []string{"good1", "good2"}; !reflect.DeepEqual(got, want) {
819 t.Errorf("with x disappearing, got %q; want %q", got, want)
820 }
821
822 xerr = errors.New("some real error")
823 if _, err := readDir(); err != xerr {
824 t.Errorf("with a non-ErrNotExist error, got error %v; want %v", err, xerr)
825 }
826 }
827
828
829 func TestReaddirOfFile(t *testing.T) {
830 t.Parallel()
831
832 f, err := CreateTemp(t.TempDir(), "_Go_ReaddirOfFile")
833 if err != nil {
834 t.Fatal(err)
835 }
836 f.Write([]byte("foo"))
837 f.Close()
838 reg, err := Open(f.Name())
839 if err != nil {
840 t.Fatal(err)
841 }
842 defer reg.Close()
843
844 names, err := reg.Readdirnames(-1)
845 if err == nil {
846 t.Error("Readdirnames succeeded; want non-nil error")
847 }
848 var pe *PathError
849 if !errors.As(err, &pe) || pe.Path != f.Name() {
850 t.Errorf("Readdirnames returned %q; want a PathError with path %q", err, f.Name())
851 }
852 if len(names) > 0 {
853 t.Errorf("unexpected dir names in regular file: %q", names)
854 }
855 }
856
857 func TestHardLink(t *testing.T) {
858 testenv.MustHaveLink(t)
859
860 defer chtmpdir(t)()
861 from, to := "hardlinktestfrom", "hardlinktestto"
862 file, err := Create(to)
863 if err != nil {
864 t.Fatalf("open %q failed: %v", to, err)
865 }
866 if err = file.Close(); err != nil {
867 t.Errorf("close %q failed: %v", to, err)
868 }
869 err = Link(to, from)
870 if err != nil {
871 t.Fatalf("link %q, %q failed: %v", to, from, err)
872 }
873
874 none := "hardlinktestnone"
875 err = Link(none, none)
876
877 if lerr, ok := err.(*LinkError); !ok || lerr.Error() == "" {
878 t.Errorf("link %q, %q failed to return a valid error", none, none)
879 }
880
881 tostat, err := Stat(to)
882 if err != nil {
883 t.Fatalf("stat %q failed: %v", to, err)
884 }
885 fromstat, err := Stat(from)
886 if err != nil {
887 t.Fatalf("stat %q failed: %v", from, err)
888 }
889 if !SameFile(tostat, fromstat) {
890 t.Errorf("link %q, %q did not create hard link", to, from)
891 }
892
893 err = Link(to, from)
894 switch err := err.(type) {
895 case *LinkError:
896 if err.Op != "link" {
897 t.Errorf("Link(%q, %q) err.Op = %q; want %q", to, from, err.Op, "link")
898 }
899 if err.Old != to {
900 t.Errorf("Link(%q, %q) err.Old = %q; want %q", to, from, err.Old, to)
901 }
902 if err.New != from {
903 t.Errorf("Link(%q, %q) err.New = %q; want %q", to, from, err.New, from)
904 }
905 if !IsExist(err.Err) {
906 t.Errorf("Link(%q, %q) err.Err = %q; want %q", to, from, err.Err, "file exists error")
907 }
908 case nil:
909 t.Errorf("link %q, %q: expected error, got nil", from, to)
910 default:
911 t.Errorf("link %q, %q: expected %T, got %T %v", from, to, new(LinkError), err, err)
912 }
913 }
914
915
916
917 func chtmpdir(t *testing.T) func() {
918 oldwd, err := Getwd()
919 if err != nil {
920 t.Fatalf("chtmpdir: %v", err)
921 }
922 d, err := MkdirTemp("", "test")
923 if err != nil {
924 t.Fatalf("chtmpdir: %v", err)
925 }
926 if err := Chdir(d); err != nil {
927 t.Fatalf("chtmpdir: %v", err)
928 }
929 return func() {
930 if err := Chdir(oldwd); err != nil {
931 t.Fatalf("chtmpdir: %v", err)
932 }
933 RemoveAll(d)
934 }
935 }
936
937 func TestSymlink(t *testing.T) {
938 testenv.MustHaveSymlink(t)
939
940 defer chtmpdir(t)()
941 from, to := "symlinktestfrom", "symlinktestto"
942 file, err := Create(to)
943 if err != nil {
944 t.Fatalf("Create(%q) failed: %v", to, err)
945 }
946 if err = file.Close(); err != nil {
947 t.Errorf("Close(%q) failed: %v", to, err)
948 }
949 err = Symlink(to, from)
950 if err != nil {
951 t.Fatalf("Symlink(%q, %q) failed: %v", to, from, err)
952 }
953 tostat, err := Lstat(to)
954 if err != nil {
955 t.Fatalf("Lstat(%q) failed: %v", to, err)
956 }
957 if tostat.Mode()&ModeSymlink != 0 {
958 t.Fatalf("Lstat(%q).Mode()&ModeSymlink = %v, want 0", to, tostat.Mode()&ModeSymlink)
959 }
960 fromstat, err := Stat(from)
961 if err != nil {
962 t.Fatalf("Stat(%q) failed: %v", from, err)
963 }
964 if !SameFile(tostat, fromstat) {
965 t.Errorf("Symlink(%q, %q) did not create symlink", to, from)
966 }
967 fromstat, err = Lstat(from)
968 if err != nil {
969 t.Fatalf("Lstat(%q) failed: %v", from, err)
970 }
971 if fromstat.Mode()&ModeSymlink == 0 {
972 t.Fatalf("Lstat(%q).Mode()&ModeSymlink = 0, want %v", from, ModeSymlink)
973 }
974 fromstat, err = Stat(from)
975 if err != nil {
976 t.Fatalf("Stat(%q) failed: %v", from, err)
977 }
978 if fromstat.Name() != from {
979 t.Errorf("Stat(%q).Name() = %q, want %q", from, fromstat.Name(), from)
980 }
981 if fromstat.Mode()&ModeSymlink != 0 {
982 t.Fatalf("Stat(%q).Mode()&ModeSymlink = %v, want 0", from, fromstat.Mode()&ModeSymlink)
983 }
984 s, err := Readlink(from)
985 if err != nil {
986 t.Fatalf("Readlink(%q) failed: %v", from, err)
987 }
988 if s != to {
989 t.Fatalf("Readlink(%q) = %q, want %q", from, s, to)
990 }
991 file, err = Open(from)
992 if err != nil {
993 t.Fatalf("Open(%q) failed: %v", from, err)
994 }
995 file.Close()
996 }
997
998 func TestLongSymlink(t *testing.T) {
999 testenv.MustHaveSymlink(t)
1000
1001 defer chtmpdir(t)()
1002 s := "0123456789abcdef"
1003
1004 s = s + s + s + s + s + s + s + s + s + s + s + s + s + s + s
1005 from := "longsymlinktestfrom"
1006 err := Symlink(s, from)
1007 if err != nil {
1008 t.Fatalf("symlink %q, %q failed: %v", s, from, err)
1009 }
1010 r, err := Readlink(from)
1011 if err != nil {
1012 t.Fatalf("readlink %q failed: %v", from, err)
1013 }
1014 if r != s {
1015 t.Fatalf("after symlink %q != %q", r, s)
1016 }
1017 }
1018
1019 func TestRename(t *testing.T) {
1020 defer chtmpdir(t)()
1021 from, to := "renamefrom", "renameto"
1022
1023 file, err := Create(from)
1024 if err != nil {
1025 t.Fatalf("open %q failed: %v", from, err)
1026 }
1027 if err = file.Close(); err != nil {
1028 t.Errorf("close %q failed: %v", from, err)
1029 }
1030 err = Rename(from, to)
1031 if err != nil {
1032 t.Fatalf("rename %q, %q failed: %v", to, from, err)
1033 }
1034 _, err = Stat(to)
1035 if err != nil {
1036 t.Errorf("stat %q failed: %v", to, err)
1037 }
1038 }
1039
1040 func TestRenameOverwriteDest(t *testing.T) {
1041 defer chtmpdir(t)()
1042 from, to := "renamefrom", "renameto"
1043
1044 toData := []byte("to")
1045 fromData := []byte("from")
1046
1047 err := WriteFile(to, toData, 0777)
1048 if err != nil {
1049 t.Fatalf("write file %q failed: %v", to, err)
1050 }
1051
1052 err = WriteFile(from, fromData, 0777)
1053 if err != nil {
1054 t.Fatalf("write file %q failed: %v", from, err)
1055 }
1056 err = Rename(from, to)
1057 if err != nil {
1058 t.Fatalf("rename %q, %q failed: %v", to, from, err)
1059 }
1060
1061 _, err = Stat(from)
1062 if err == nil {
1063 t.Errorf("from file %q still exists", from)
1064 }
1065 if err != nil && !IsNotExist(err) {
1066 t.Fatalf("stat from: %v", err)
1067 }
1068 toFi, err := Stat(to)
1069 if err != nil {
1070 t.Fatalf("stat %q failed: %v", to, err)
1071 }
1072 if toFi.Size() != int64(len(fromData)) {
1073 t.Errorf(`"to" size = %d; want %d (old "from" size)`, toFi.Size(), len(fromData))
1074 }
1075 }
1076
1077 func TestRenameFailed(t *testing.T) {
1078 defer chtmpdir(t)()
1079 from, to := "renamefrom", "renameto"
1080
1081 err := Rename(from, to)
1082 switch err := err.(type) {
1083 case *LinkError:
1084 if err.Op != "rename" {
1085 t.Errorf("rename %q, %q: err.Op: want %q, got %q", from, to, "rename", err.Op)
1086 }
1087 if err.Old != from {
1088 t.Errorf("rename %q, %q: err.Old: want %q, got %q", from, to, from, err.Old)
1089 }
1090 if err.New != to {
1091 t.Errorf("rename %q, %q: err.New: want %q, got %q", from, to, to, err.New)
1092 }
1093 case nil:
1094 t.Errorf("rename %q, %q: expected error, got nil", from, to)
1095 default:
1096 t.Errorf("rename %q, %q: expected %T, got %T %v", from, to, new(LinkError), err, err)
1097 }
1098 }
1099
1100 func TestRenameNotExisting(t *testing.T) {
1101 defer chtmpdir(t)()
1102 from, to := "doesnt-exist", "dest"
1103
1104 Mkdir(to, 0777)
1105
1106 if err := Rename(from, to); !IsNotExist(err) {
1107 t.Errorf("Rename(%q, %q) = %v; want an IsNotExist error", from, to, err)
1108 }
1109 }
1110
1111 func TestRenameToDirFailed(t *testing.T) {
1112 defer chtmpdir(t)()
1113 from, to := "renamefrom", "renameto"
1114
1115 Mkdir(from, 0777)
1116 Mkdir(to, 0777)
1117
1118 err := Rename(from, to)
1119 switch err := err.(type) {
1120 case *LinkError:
1121 if err.Op != "rename" {
1122 t.Errorf("rename %q, %q: err.Op: want %q, got %q", from, to, "rename", err.Op)
1123 }
1124 if err.Old != from {
1125 t.Errorf("rename %q, %q: err.Old: want %q, got %q", from, to, from, err.Old)
1126 }
1127 if err.New != to {
1128 t.Errorf("rename %q, %q: err.New: want %q, got %q", from, to, to, err.New)
1129 }
1130 case nil:
1131 t.Errorf("rename %q, %q: expected error, got nil", from, to)
1132 default:
1133 t.Errorf("rename %q, %q: expected %T, got %T %v", from, to, new(LinkError), err, err)
1134 }
1135 }
1136
1137 func TestRenameCaseDifference(pt *testing.T) {
1138 from, to := "renameFROM", "RENAMEfrom"
1139 tests := []struct {
1140 name string
1141 create func() error
1142 }{
1143 {"dir", func() error {
1144 return Mkdir(from, 0777)
1145 }},
1146 {"file", func() error {
1147 fd, err := Create(from)
1148 if err != nil {
1149 return err
1150 }
1151 return fd.Close()
1152 }},
1153 }
1154
1155 for _, test := range tests {
1156 pt.Run(test.name, func(t *testing.T) {
1157 defer chtmpdir(t)()
1158
1159 if err := test.create(); err != nil {
1160 t.Fatalf("failed to create test file: %s", err)
1161 }
1162
1163 if _, err := Stat(to); err != nil {
1164
1165 if IsNotExist(err) {
1166 t.Skipf("case sensitive filesystem")
1167 }
1168 t.Fatalf("stat %q, got: %q", to, err)
1169 }
1170
1171 if err := Rename(from, to); err != nil {
1172 t.Fatalf("unexpected error when renaming from %q to %q: %s", from, to, err)
1173 }
1174
1175 fd, err := Open(".")
1176 if err != nil {
1177 t.Fatalf("Open .: %s", err)
1178 }
1179
1180
1181
1182 dirNames, err := fd.Readdirnames(-1)
1183 if err != nil {
1184 t.Fatalf("readdirnames: %s", err)
1185 }
1186
1187 if dirNamesLen := len(dirNames); dirNamesLen != 1 {
1188 t.Fatalf("unexpected dirNames len, got %q, want %q", dirNamesLen, 1)
1189 }
1190
1191 if dirNames[0] != to {
1192 t.Errorf("unexpected name, got %q, want %q", dirNames[0], to)
1193 }
1194 })
1195 }
1196 }
1197
1198 func testStartProcess(dir, cmd string, args []string, expect string) func(t *testing.T) {
1199 return func(t *testing.T) {
1200 t.Parallel()
1201
1202 r, w, err := Pipe()
1203 if err != nil {
1204 t.Fatalf("Pipe: %v", err)
1205 }
1206 defer r.Close()
1207 attr := &ProcAttr{Dir: dir, Files: []*File{nil, w, Stderr}}
1208 p, err := StartProcess(cmd, args, attr)
1209 if err != nil {
1210 t.Fatalf("StartProcess: %v", err)
1211 }
1212 w.Close()
1213
1214 var b strings.Builder
1215 io.Copy(&b, r)
1216 output := b.String()
1217
1218 fi1, _ := Stat(strings.TrimSpace(output))
1219 fi2, _ := Stat(expect)
1220 if !SameFile(fi1, fi2) {
1221 t.Errorf("exec %q returned %q wanted %q",
1222 strings.Join(append([]string{cmd}, args...), " "), output, expect)
1223 }
1224 p.Wait()
1225 }
1226 }
1227
1228 func TestStartProcess(t *testing.T) {
1229 testenv.MustHaveExec(t)
1230 t.Parallel()
1231
1232 var dir, cmd string
1233 var args []string
1234 switch runtime.GOOS {
1235 case "android":
1236 t.Skip("android doesn't have /bin/pwd")
1237 case "windows":
1238 cmd = Getenv("COMSPEC")
1239 dir = Getenv("SystemRoot")
1240 args = []string{"/c", "cd"}
1241 default:
1242 var err error
1243 cmd, err = exec.LookPath("pwd")
1244 if err != nil {
1245 t.Fatalf("Can't find pwd: %v", err)
1246 }
1247 dir = "/"
1248 args = []string{}
1249 t.Logf("Testing with %v", cmd)
1250 }
1251 cmddir, cmdbase := filepath.Split(cmd)
1252 args = append([]string{cmdbase}, args...)
1253 t.Run("absolute", testStartProcess(dir, cmd, args, dir))
1254 t.Run("relative", testStartProcess(cmddir, cmdbase, args, cmddir))
1255 }
1256
1257 func checkMode(t *testing.T, path string, mode FileMode) {
1258 dir, err := Stat(path)
1259 if err != nil {
1260 t.Fatalf("Stat %q (looking for mode %#o): %s", path, mode, err)
1261 }
1262 if dir.Mode()&ModePerm != mode {
1263 t.Errorf("Stat %q: mode %#o want %#o", path, dir.Mode(), mode)
1264 }
1265 }
1266
1267 func TestChmod(t *testing.T) {
1268
1269 if runtime.GOOS == "wasip1" {
1270 t.Skip("Chmod is not supported on " + runtime.GOOS)
1271 }
1272 t.Parallel()
1273
1274 f := newFile("TestChmod", t)
1275 defer Remove(f.Name())
1276 defer f.Close()
1277
1278
1279 fm := FileMode(0456)
1280 if runtime.GOOS == "windows" {
1281 fm = FileMode(0444)
1282 }
1283 if err := Chmod(f.Name(), fm); err != nil {
1284 t.Fatalf("chmod %s %#o: %s", f.Name(), fm, err)
1285 }
1286 checkMode(t, f.Name(), fm)
1287
1288 fm = FileMode(0123)
1289 if runtime.GOOS == "windows" {
1290 fm = FileMode(0666)
1291 }
1292 if err := f.Chmod(fm); err != nil {
1293 t.Fatalf("chmod %s %#o: %s", f.Name(), fm, err)
1294 }
1295 checkMode(t, f.Name(), fm)
1296 }
1297
1298 func checkSize(t *testing.T, f *File, size int64) {
1299 t.Helper()
1300 dir, err := f.Stat()
1301 if err != nil {
1302 t.Fatalf("Stat %q (looking for size %d): %s", f.Name(), size, err)
1303 }
1304 if dir.Size() != size {
1305 t.Errorf("Stat %q: size %d want %d", f.Name(), dir.Size(), size)
1306 }
1307 }
1308
1309 func TestFTruncate(t *testing.T) {
1310 t.Parallel()
1311
1312 f := newFile("TestFTruncate", t)
1313 defer Remove(f.Name())
1314 defer f.Close()
1315
1316 checkSize(t, f, 0)
1317 f.Write([]byte("hello, world\n"))
1318 checkSize(t, f, 13)
1319 f.Truncate(10)
1320 checkSize(t, f, 10)
1321 f.Truncate(1024)
1322 checkSize(t, f, 1024)
1323 f.Truncate(0)
1324 checkSize(t, f, 0)
1325 _, err := f.Write([]byte("surprise!"))
1326 if err == nil {
1327 checkSize(t, f, 13+9)
1328 }
1329 }
1330
1331 func TestTruncate(t *testing.T) {
1332 t.Parallel()
1333
1334 f := newFile("TestTruncate", t)
1335 defer Remove(f.Name())
1336 defer f.Close()
1337
1338 checkSize(t, f, 0)
1339 f.Write([]byte("hello, world\n"))
1340 checkSize(t, f, 13)
1341 Truncate(f.Name(), 10)
1342 checkSize(t, f, 10)
1343 Truncate(f.Name(), 1024)
1344 checkSize(t, f, 1024)
1345 Truncate(f.Name(), 0)
1346 checkSize(t, f, 0)
1347 _, err := f.Write([]byte("surprise!"))
1348 if err == nil {
1349 checkSize(t, f, 13+9)
1350 }
1351 }
1352
1353 func TestTruncateNonexistentFile(t *testing.T) {
1354 t.Parallel()
1355
1356 assertPathError := func(t testing.TB, path string, err error) {
1357 t.Helper()
1358 if pe, ok := err.(*PathError); !ok || !IsNotExist(err) || pe.Path != path {
1359 t.Errorf("got error: %v\nwant an ErrNotExist PathError with path %q", err, path)
1360 }
1361 }
1362
1363 path := filepath.Join(t.TempDir(), "nonexistent")
1364
1365 err := Truncate(path, 1)
1366 assertPathError(t, path, err)
1367
1368
1369 _, err = Stat(path)
1370 assertPathError(t, path, err)
1371 }
1372
1373
1374
1375
1376
1377 func TestChtimes(t *testing.T) {
1378 t.Parallel()
1379
1380 f := newFile("TestChtimes", t)
1381 defer Remove(f.Name())
1382
1383 f.Write([]byte("hello, world\n"))
1384 f.Close()
1385
1386 testChtimes(t, f.Name())
1387 }
1388
1389 func TestChtimesWithZeroTimes(t *testing.T) {
1390 file := newFile("chtimes-with-zero", t)
1391 _, err := file.Write([]byte("hello, world\n"))
1392 if err != nil {
1393 t.Fatalf("Write: %s", err)
1394 }
1395 fName := file.Name()
1396 defer Remove(file.Name())
1397 err = file.Close()
1398 if err != nil {
1399 t.Errorf("%v", err)
1400 }
1401 fs, err := Stat(fName)
1402 if err != nil {
1403 t.Fatal(err)
1404 }
1405 startAtime := Atime(fs)
1406 startMtime := fs.ModTime()
1407 switch runtime.GOOS {
1408 case "js":
1409 startAtime = startAtime.Truncate(time.Second)
1410 startMtime = startMtime.Truncate(time.Second)
1411 }
1412 at0 := startAtime
1413 mt0 := startMtime
1414 t0 := startMtime.Truncate(time.Second).Add(1 * time.Hour)
1415
1416 tests := []struct {
1417 aTime time.Time
1418 mTime time.Time
1419 wantATime time.Time
1420 wantMTime time.Time
1421 }{
1422 {
1423 aTime: time.Time{},
1424 mTime: time.Time{},
1425 wantATime: startAtime,
1426 wantMTime: startMtime,
1427 },
1428 {
1429 aTime: t0.Add(200 * time.Second),
1430 mTime: time.Time{},
1431 wantATime: t0.Add(200 * time.Second),
1432 wantMTime: startMtime,
1433 },
1434 {
1435 aTime: time.Time{},
1436 mTime: t0.Add(100 * time.Second),
1437 wantATime: t0.Add(200 * time.Second),
1438 wantMTime: t0.Add(100 * time.Second),
1439 },
1440 {
1441 aTime: t0.Add(300 * time.Second),
1442 mTime: t0.Add(100 * time.Second),
1443 wantATime: t0.Add(300 * time.Second),
1444 wantMTime: t0.Add(100 * time.Second),
1445 },
1446 }
1447
1448 for _, tt := range tests {
1449
1450 if err := Chtimes(fName, tt.aTime, tt.mTime); err != nil {
1451 t.Error(err)
1452 }
1453
1454
1455 fs, err = Stat(fName)
1456 if err != nil {
1457 t.Error(err)
1458 }
1459 at0 = Atime(fs)
1460 mt0 = fs.ModTime()
1461
1462 if got, want := at0, tt.wantATime; !got.Equal(want) {
1463 errormsg := fmt.Sprintf("AccessTime mismatch with values ATime:%q-MTime:%q\ngot: %q\nwant: %q", tt.aTime, tt.mTime, got, want)
1464 switch runtime.GOOS {
1465 case "plan9":
1466
1467
1468
1469
1470 case "windows":
1471 t.Error(errormsg)
1472 default:
1473 if got, want := at0, tt.wantATime; !got.Equal(want) {
1474 mounts, err := ReadFile("/bin/mounts")
1475 if err != nil {
1476 mounts, err = ReadFile("/etc/mtab")
1477 }
1478 if strings.Contains(string(mounts), "noatime") {
1479 t.Log(errormsg)
1480 t.Log("A filesystem is mounted with noatime; ignoring.")
1481 } else {
1482 switch runtime.GOOS {
1483 case "netbsd", "dragonfly":
1484
1485
1486
1487 if strings.Contains(runtime.GOARCH, "64") {
1488 t.Log(errormsg)
1489 t.Log("Filesystem might not support atime changes; ignoring.")
1490 }
1491 default:
1492 t.Error(errormsg)
1493 }
1494 }
1495 }
1496 }
1497 }
1498 if got, want := mt0, tt.wantMTime; !got.Equal(want) {
1499 errormsg := fmt.Sprintf("ModTime mismatch with values ATime:%q-MTime:%q\ngot: %q\nwant: %q", tt.aTime, tt.mTime, got, want)
1500 switch runtime.GOOS {
1501 case "dragonfly":
1502 t.Log(errormsg)
1503 t.Log("Mtime is always updated; ignoring.")
1504 default:
1505 t.Error(errormsg)
1506 }
1507 }
1508 }
1509 }
1510
1511
1512
1513
1514
1515 func TestChtimesDir(t *testing.T) {
1516 t.Parallel()
1517
1518 name := newDir("TestChtimes", t)
1519 defer RemoveAll(name)
1520
1521 testChtimes(t, name)
1522 }
1523
1524 func testChtimes(t *testing.T, name string) {
1525 st, err := Stat(name)
1526 if err != nil {
1527 t.Fatalf("Stat %s: %s", name, err)
1528 }
1529 preStat := st
1530
1531
1532 at := Atime(preStat)
1533 mt := preStat.ModTime()
1534 err = Chtimes(name, at.Add(-time.Second), mt.Add(-time.Second))
1535 if err != nil {
1536 t.Fatalf("Chtimes %s: %s", name, err)
1537 }
1538
1539 st, err = Stat(name)
1540 if err != nil {
1541 t.Fatalf("second Stat %s: %s", name, err)
1542 }
1543 postStat := st
1544
1545 pat := Atime(postStat)
1546 pmt := postStat.ModTime()
1547 if !pat.Before(at) {
1548 switch runtime.GOOS {
1549 case "plan9":
1550
1551
1552
1553
1554 case "netbsd":
1555 mounts, _ := ReadFile("/proc/mounts")
1556 if strings.Contains(string(mounts), "noatime") {
1557 t.Logf("AccessTime didn't go backwards, but see a filesystem mounted noatime; ignoring. Issue 19293.")
1558 } else {
1559 t.Logf("AccessTime didn't go backwards; was=%v, after=%v (Ignoring on NetBSD, assuming noatime, Issue 19293)", at, pat)
1560 }
1561 default:
1562 t.Errorf("AccessTime didn't go backwards; was=%v, after=%v", at, pat)
1563 }
1564 }
1565
1566 if !pmt.Before(mt) {
1567 t.Errorf("ModTime didn't go backwards; was=%v, after=%v", mt, pmt)
1568 }
1569 }
1570
1571 func TestChtimesToUnixZero(t *testing.T) {
1572 file := newFile("chtimes-to-unix-zero", t)
1573 fn := file.Name()
1574 defer Remove(fn)
1575 if _, err := file.Write([]byte("hi")); err != nil {
1576 t.Fatal(err)
1577 }
1578 if err := file.Close(); err != nil {
1579 t.Fatal(err)
1580 }
1581
1582 unixZero := time.Unix(0, 0)
1583 if err := Chtimes(fn, unixZero, unixZero); err != nil {
1584 t.Fatalf("Chtimes failed: %v", err)
1585 }
1586
1587 st, err := Stat(fn)
1588 if err != nil {
1589 t.Fatal(err)
1590 }
1591
1592 if mt := st.ModTime(); mt != unixZero {
1593 t.Errorf("mtime is %v, want %v", mt, unixZero)
1594 }
1595 }
1596
1597 func TestFileChdir(t *testing.T) {
1598 wd, err := Getwd()
1599 if err != nil {
1600 t.Fatalf("Getwd: %s", err)
1601 }
1602 defer Chdir(wd)
1603
1604 fd, err := Open(".")
1605 if err != nil {
1606 t.Fatalf("Open .: %s", err)
1607 }
1608 defer fd.Close()
1609
1610 if err := Chdir("/"); err != nil {
1611 t.Fatalf("Chdir /: %s", err)
1612 }
1613
1614 if err := fd.Chdir(); err != nil {
1615 t.Fatalf("fd.Chdir: %s", err)
1616 }
1617
1618 wdNew, err := Getwd()
1619 if err != nil {
1620 t.Fatalf("Getwd: %s", err)
1621 }
1622 if !equal(wdNew, wd) {
1623 t.Fatalf("fd.Chdir failed, got %s, want %s", wdNew, wd)
1624 }
1625 }
1626
1627 func TestChdirAndGetwd(t *testing.T) {
1628 fd, err := Open(".")
1629 if err != nil {
1630 t.Fatalf("Open .: %s", err)
1631 }
1632
1633
1634 dirs := []string{"/", "/usr/bin", "/tmp"}
1635
1636 switch runtime.GOOS {
1637 case "android":
1638 dirs = []string{"/system/bin"}
1639 case "plan9":
1640 dirs = []string{"/", "/usr"}
1641 case "ios", "windows", "wasip1":
1642 dirs = nil
1643 for _, dir := range []string{t.TempDir(), t.TempDir()} {
1644
1645 dir, err = filepath.EvalSymlinks(dir)
1646 if err != nil {
1647 t.Fatalf("EvalSymlinks: %v", err)
1648 }
1649 dirs = append(dirs, dir)
1650 }
1651 }
1652 oldwd := Getenv("PWD")
1653 for mode := 0; mode < 2; mode++ {
1654 for _, d := range dirs {
1655 if mode == 0 {
1656 err = Chdir(d)
1657 } else {
1658 fd1, err1 := Open(d)
1659 if err1 != nil {
1660 t.Errorf("Open %s: %s", d, err1)
1661 continue
1662 }
1663 err = fd1.Chdir()
1664 fd1.Close()
1665 }
1666 if d == "/tmp" {
1667 Setenv("PWD", "/tmp")
1668 }
1669 pwd, err1 := Getwd()
1670 Setenv("PWD", oldwd)
1671 err2 := fd.Chdir()
1672 if err2 != nil {
1673
1674
1675
1676 fmt.Fprintf(Stderr, "fchdir back to dot failed: %s\n", err2)
1677 Exit(1)
1678 }
1679 if err != nil {
1680 fd.Close()
1681 t.Fatalf("Chdir %s: %s", d, err)
1682 }
1683 if err1 != nil {
1684 fd.Close()
1685 t.Fatalf("Getwd in %s: %s", d, err1)
1686 }
1687 if !equal(pwd, d) {
1688 fd.Close()
1689 t.Fatalf("Getwd returned %q want %q", pwd, d)
1690 }
1691 }
1692 }
1693 fd.Close()
1694 }
1695
1696
1697 func TestProgWideChdir(t *testing.T) {
1698 const N = 10
1699 var wg sync.WaitGroup
1700 hold := make(chan struct{})
1701 done := make(chan struct{})
1702
1703 d := t.TempDir()
1704 oldwd, err := Getwd()
1705 if err != nil {
1706 t.Fatalf("Getwd: %v", err)
1707 }
1708 defer func() {
1709 if err := Chdir(oldwd); err != nil {
1710
1711
1712 panic(err)
1713 }
1714 }()
1715
1716
1717
1718
1719
1720
1721 defer wg.Wait()
1722 defer close(done)
1723
1724 for i := 0; i < N; i++ {
1725 wg.Add(1)
1726 go func(i int) {
1727 defer wg.Done()
1728
1729
1730 if i%2 == 1 {
1731
1732
1733
1734
1735
1736 runtime.LockOSThread()
1737 }
1738 select {
1739 case <-done:
1740 return
1741 case <-hold:
1742 }
1743
1744 f0, err := Stat(".")
1745 if err != nil {
1746 t.Error(err)
1747 return
1748 }
1749 pwd, err := Getwd()
1750 if err != nil {
1751 t.Errorf("Getwd: %v", err)
1752 return
1753 }
1754 if pwd != d {
1755 t.Errorf("Getwd() = %q, want %q", pwd, d)
1756 return
1757 }
1758 f1, err := Stat(pwd)
1759 if err != nil {
1760 t.Error(err)
1761 return
1762 }
1763 if !SameFile(f0, f1) {
1764 t.Errorf(`Samefile(Stat("."), Getwd()) reports false (%s != %s)`, f0.Name(), f1.Name())
1765 return
1766 }
1767 }(i)
1768 }
1769 if err = Chdir(d); err != nil {
1770 t.Fatalf("Chdir: %v", err)
1771 }
1772
1773
1774 d, err = Getwd()
1775 if err != nil {
1776 t.Fatalf("Getwd: %v", err)
1777 }
1778 close(hold)
1779 wg.Wait()
1780 }
1781
1782 func TestSeek(t *testing.T) {
1783 t.Parallel()
1784
1785 f := newFile("TestSeek", t)
1786 defer Remove(f.Name())
1787 defer f.Close()
1788
1789 const data = "hello, world\n"
1790 io.WriteString(f, data)
1791
1792 type test struct {
1793 in int64
1794 whence int
1795 out int64
1796 }
1797 var tests = []test{
1798 {0, io.SeekCurrent, int64(len(data))},
1799 {0, io.SeekStart, 0},
1800 {5, io.SeekStart, 5},
1801 {0, io.SeekEnd, int64(len(data))},
1802 {0, io.SeekStart, 0},
1803 {-1, io.SeekEnd, int64(len(data)) - 1},
1804 {1 << 33, io.SeekStart, 1 << 33},
1805 {1 << 33, io.SeekEnd, 1<<33 + int64(len(data))},
1806
1807
1808 {1<<32 - 1, io.SeekStart, 1<<32 - 1},
1809 {0, io.SeekCurrent, 1<<32 - 1},
1810 {2<<32 - 1, io.SeekStart, 2<<32 - 1},
1811 {0, io.SeekCurrent, 2<<32 - 1},
1812 }
1813 for i, tt := range tests {
1814 off, err := f.Seek(tt.in, tt.whence)
1815 if off != tt.out || err != nil {
1816 if e, ok := err.(*PathError); ok && e.Err == syscall.EINVAL && tt.out > 1<<32 && runtime.GOOS == "linux" {
1817 mounts, _ := ReadFile("/proc/mounts")
1818 if strings.Contains(string(mounts), "reiserfs") {
1819
1820 t.Skipf("skipping test known to fail on reiserfs; https://golang.org/issue/91")
1821 }
1822 }
1823 t.Errorf("#%d: Seek(%v, %v) = %v, %v want %v, nil", i, tt.in, tt.whence, off, err, tt.out)
1824 }
1825 }
1826 }
1827
1828 func TestSeekError(t *testing.T) {
1829 switch runtime.GOOS {
1830 case "js", "plan9", "wasip1":
1831 t.Skipf("skipping test on %v", runtime.GOOS)
1832 }
1833 t.Parallel()
1834
1835 r, w, err := Pipe()
1836 if err != nil {
1837 t.Fatal(err)
1838 }
1839 _, err = r.Seek(0, 0)
1840 if err == nil {
1841 t.Fatal("Seek on pipe should fail")
1842 }
1843 if perr, ok := err.(*PathError); !ok || perr.Err != syscall.ESPIPE {
1844 t.Errorf("Seek returned error %v, want &PathError{Err: syscall.ESPIPE}", err)
1845 }
1846 _, err = w.Seek(0, 0)
1847 if err == nil {
1848 t.Fatal("Seek on pipe should fail")
1849 }
1850 if perr, ok := err.(*PathError); !ok || perr.Err != syscall.ESPIPE {
1851 t.Errorf("Seek returned error %v, want &PathError{Err: syscall.ESPIPE}", err)
1852 }
1853 }
1854
1855 type openErrorTest struct {
1856 path string
1857 mode int
1858 error error
1859 }
1860
1861 var openErrorTests = []openErrorTest{
1862 {
1863 sfdir + "/no-such-file",
1864 O_RDONLY,
1865 syscall.ENOENT,
1866 },
1867 {
1868 sfdir,
1869 O_WRONLY,
1870 syscall.EISDIR,
1871 },
1872 {
1873 sfdir + "/" + sfname + "/no-such-file",
1874 O_WRONLY,
1875 syscall.ENOTDIR,
1876 },
1877 }
1878
1879 func TestOpenError(t *testing.T) {
1880 t.Parallel()
1881
1882 for _, tt := range openErrorTests {
1883 f, err := OpenFile(tt.path, tt.mode, 0)
1884 if err == nil {
1885 t.Errorf("Open(%q, %d) succeeded", tt.path, tt.mode)
1886 f.Close()
1887 continue
1888 }
1889 perr, ok := err.(*PathError)
1890 if !ok {
1891 t.Errorf("Open(%q, %d) returns error of %T type; want *PathError", tt.path, tt.mode, err)
1892 }
1893 if perr.Err != tt.error {
1894 if runtime.GOOS == "plan9" {
1895 syscallErrStr := perr.Err.Error()
1896 expectedErrStr := strings.Replace(tt.error.Error(), "file ", "", 1)
1897 if !strings.HasSuffix(syscallErrStr, expectedErrStr) {
1898
1899
1900
1901 if tt.error == syscall.EISDIR && strings.HasSuffix(syscallErrStr, syscall.EACCES.Error()) {
1902 continue
1903 }
1904 t.Errorf("Open(%q, %d) = _, %q; want suffix %q", tt.path, tt.mode, syscallErrStr, expectedErrStr)
1905 }
1906 continue
1907 }
1908 if runtime.GOOS == "dragonfly" {
1909
1910
1911 if tt.error == syscall.EISDIR && perr.Err == syscall.EACCES {
1912 continue
1913 }
1914 }
1915 t.Errorf("Open(%q, %d) = _, %q; want %q", tt.path, tt.mode, perr.Err.Error(), tt.error.Error())
1916 }
1917 }
1918 }
1919
1920 func TestOpenNoName(t *testing.T) {
1921 f, err := Open("")
1922 if err == nil {
1923 f.Close()
1924 t.Fatal(`Open("") succeeded`)
1925 }
1926 }
1927
1928 func runBinHostname(t *testing.T) string {
1929
1930 r, w, err := Pipe()
1931 if err != nil {
1932 t.Fatal(err)
1933 }
1934 defer r.Close()
1935
1936 path, err := exec.LookPath("hostname")
1937 if err != nil {
1938 if errors.Is(err, exec.ErrNotFound) {
1939 t.Skip("skipping test; test requires hostname but it does not exist")
1940 }
1941 t.Fatal(err)
1942 }
1943
1944 argv := []string{"hostname"}
1945 if runtime.GOOS == "aix" {
1946 argv = []string{"hostname", "-s"}
1947 }
1948 p, err := StartProcess(path, argv, &ProcAttr{Files: []*File{nil, w, Stderr}})
1949 if err != nil {
1950 t.Fatal(err)
1951 }
1952 w.Close()
1953
1954 var b strings.Builder
1955 io.Copy(&b, r)
1956 _, err = p.Wait()
1957 if err != nil {
1958 t.Fatalf("run hostname Wait: %v", err)
1959 }
1960 err = p.Kill()
1961 if err == nil {
1962 t.Errorf("expected an error from Kill running 'hostname'")
1963 }
1964 output := b.String()
1965 if n := len(output); n > 0 && output[n-1] == '\n' {
1966 output = output[0 : n-1]
1967 }
1968 if output == "" {
1969 t.Fatalf("/bin/hostname produced no output")
1970 }
1971
1972 return output
1973 }
1974
1975 func testWindowsHostname(t *testing.T, hostname string) {
1976 cmd := testenv.Command(t, "hostname")
1977 out, err := cmd.Output()
1978 if err != nil {
1979 t.Fatalf("Failed to execute hostname command: %v %s", err, out)
1980 }
1981 want := strings.Trim(string(out), "\r\n")
1982 if hostname != want {
1983 t.Fatalf("Hostname() = %q != system hostname of %q", hostname, want)
1984 }
1985 }
1986
1987 func TestHostname(t *testing.T) {
1988 t.Parallel()
1989
1990 hostname, err := Hostname()
1991 if err != nil {
1992 t.Fatal(err)
1993 }
1994 if hostname == "" {
1995 t.Fatal("Hostname returned empty string and no error")
1996 }
1997 if strings.Contains(hostname, "\x00") {
1998 t.Fatalf("unexpected zero byte in hostname: %q", hostname)
1999 }
2000
2001
2002
2003 switch runtime.GOOS {
2004 case "android", "plan9":
2005
2006 return
2007 case "windows":
2008 testWindowsHostname(t, hostname)
2009 return
2010 }
2011
2012 testenv.MustHaveExec(t)
2013
2014
2015
2016
2017 want := runBinHostname(t)
2018 if hostname != want {
2019 host, _, ok := strings.Cut(hostname, ".")
2020 if !ok || host != want {
2021 t.Errorf("Hostname() = %q, want %q", hostname, want)
2022 }
2023 }
2024 }
2025
2026 func TestReadAt(t *testing.T) {
2027 t.Parallel()
2028
2029 f := newFile("TestReadAt", t)
2030 defer Remove(f.Name())
2031 defer f.Close()
2032
2033 const data = "hello, world\n"
2034 io.WriteString(f, data)
2035
2036 b := make([]byte, 5)
2037 n, err := f.ReadAt(b, 7)
2038 if err != nil || n != len(b) {
2039 t.Fatalf("ReadAt 7: %d, %v", n, err)
2040 }
2041 if string(b) != "world" {
2042 t.Fatalf("ReadAt 7: have %q want %q", string(b), "world")
2043 }
2044 }
2045
2046
2047
2048
2049
2050 func TestReadAtOffset(t *testing.T) {
2051 t.Parallel()
2052
2053 f := newFile("TestReadAtOffset", t)
2054 defer Remove(f.Name())
2055 defer f.Close()
2056
2057 const data = "hello, world\n"
2058 io.WriteString(f, data)
2059
2060 f.Seek(0, 0)
2061 b := make([]byte, 5)
2062
2063 n, err := f.ReadAt(b, 7)
2064 if err != nil || n != len(b) {
2065 t.Fatalf("ReadAt 7: %d, %v", n, err)
2066 }
2067 if string(b) != "world" {
2068 t.Fatalf("ReadAt 7: have %q want %q", string(b), "world")
2069 }
2070
2071 n, err = f.Read(b)
2072 if err != nil || n != len(b) {
2073 t.Fatalf("Read: %d, %v", n, err)
2074 }
2075 if string(b) != "hello" {
2076 t.Fatalf("Read: have %q want %q", string(b), "hello")
2077 }
2078 }
2079
2080
2081 func TestReadAtNegativeOffset(t *testing.T) {
2082 t.Parallel()
2083
2084 f := newFile("TestReadAtNegativeOffset", t)
2085 defer Remove(f.Name())
2086 defer f.Close()
2087
2088 const data = "hello, world\n"
2089 io.WriteString(f, data)
2090
2091 f.Seek(0, 0)
2092 b := make([]byte, 5)
2093
2094 n, err := f.ReadAt(b, -10)
2095
2096 const wantsub = "negative offset"
2097 if !strings.Contains(fmt.Sprint(err), wantsub) || n != 0 {
2098 t.Errorf("ReadAt(-10) = %v, %v; want 0, ...%q...", n, err, wantsub)
2099 }
2100 }
2101
2102 func TestWriteAt(t *testing.T) {
2103 t.Parallel()
2104
2105 f := newFile("TestWriteAt", t)
2106 defer Remove(f.Name())
2107 defer f.Close()
2108
2109 const data = "hello, world\n"
2110 io.WriteString(f, data)
2111
2112 n, err := f.WriteAt([]byte("WORLD"), 7)
2113 if err != nil || n != 5 {
2114 t.Fatalf("WriteAt 7: %d, %v", n, err)
2115 }
2116
2117 b, err := ReadFile(f.Name())
2118 if err != nil {
2119 t.Fatalf("ReadFile %s: %v", f.Name(), err)
2120 }
2121 if string(b) != "hello, WORLD\n" {
2122 t.Fatalf("after write: have %q want %q", string(b), "hello, WORLD\n")
2123 }
2124 }
2125
2126
2127 func TestWriteAtNegativeOffset(t *testing.T) {
2128 t.Parallel()
2129
2130 f := newFile("TestWriteAtNegativeOffset", t)
2131 defer Remove(f.Name())
2132 defer f.Close()
2133
2134 n, err := f.WriteAt([]byte("WORLD"), -10)
2135
2136 const wantsub = "negative offset"
2137 if !strings.Contains(fmt.Sprint(err), wantsub) || n != 0 {
2138 t.Errorf("WriteAt(-10) = %v, %v; want 0, ...%q...", n, err, wantsub)
2139 }
2140 }
2141
2142
2143 func TestWriteAtInAppendMode(t *testing.T) {
2144 defer chtmpdir(t)()
2145 f, err := OpenFile("write_at_in_append_mode.txt", O_APPEND|O_CREATE, 0666)
2146 if err != nil {
2147 t.Fatalf("OpenFile: %v", err)
2148 }
2149 defer f.Close()
2150
2151 _, err = f.WriteAt([]byte(""), 1)
2152 if err != ErrWriteAtInAppendMode {
2153 t.Fatalf("f.WriteAt returned %v, expected %v", err, ErrWriteAtInAppendMode)
2154 }
2155 }
2156
2157 func writeFile(t *testing.T, fname string, flag int, text string) string {
2158 f, err := OpenFile(fname, flag, 0666)
2159 if err != nil {
2160 t.Fatalf("Open: %v", err)
2161 }
2162 n, err := io.WriteString(f, text)
2163 if err != nil {
2164 t.Fatalf("WriteString: %d, %v", n, err)
2165 }
2166 f.Close()
2167 data, err := ReadFile(fname)
2168 if err != nil {
2169 t.Fatalf("ReadFile: %v", err)
2170 }
2171 return string(data)
2172 }
2173
2174 func TestAppend(t *testing.T) {
2175 defer chtmpdir(t)()
2176 const f = "append.txt"
2177 s := writeFile(t, f, O_CREATE|O_TRUNC|O_RDWR, "new")
2178 if s != "new" {
2179 t.Fatalf("writeFile: have %q want %q", s, "new")
2180 }
2181 s = writeFile(t, f, O_APPEND|O_RDWR, "|append")
2182 if s != "new|append" {
2183 t.Fatalf("writeFile: have %q want %q", s, "new|append")
2184 }
2185 s = writeFile(t, f, O_CREATE|O_APPEND|O_RDWR, "|append")
2186 if s != "new|append|append" {
2187 t.Fatalf("writeFile: have %q want %q", s, "new|append|append")
2188 }
2189 err := Remove(f)
2190 if err != nil {
2191 t.Fatalf("Remove: %v", err)
2192 }
2193 s = writeFile(t, f, O_CREATE|O_APPEND|O_RDWR, "new&append")
2194 if s != "new&append" {
2195 t.Fatalf("writeFile: after append have %q want %q", s, "new&append")
2196 }
2197 s = writeFile(t, f, O_CREATE|O_RDWR, "old")
2198 if s != "old&append" {
2199 t.Fatalf("writeFile: after create have %q want %q", s, "old&append")
2200 }
2201 s = writeFile(t, f, O_CREATE|O_TRUNC|O_RDWR, "new")
2202 if s != "new" {
2203 t.Fatalf("writeFile: after truncate have %q want %q", s, "new")
2204 }
2205 }
2206
2207 func TestStatDirWithTrailingSlash(t *testing.T) {
2208 t.Parallel()
2209
2210
2211 path := t.TempDir()
2212
2213
2214 if _, err := Stat(path); err != nil {
2215 t.Fatalf("stat %s failed: %s", path, err)
2216 }
2217
2218
2219 path += "/"
2220 if _, err := Stat(path); err != nil {
2221 t.Fatalf("stat %s failed: %s", path, err)
2222 }
2223 }
2224
2225 func TestNilProcessStateString(t *testing.T) {
2226 var ps *ProcessState
2227 s := ps.String()
2228 if s != "<nil>" {
2229 t.Errorf("(*ProcessState)(nil).String() = %q, want %q", s, "<nil>")
2230 }
2231 }
2232
2233 func TestSameFile(t *testing.T) {
2234 defer chtmpdir(t)()
2235 fa, err := Create("a")
2236 if err != nil {
2237 t.Fatalf("Create(a): %v", err)
2238 }
2239 fa.Close()
2240 fb, err := Create("b")
2241 if err != nil {
2242 t.Fatalf("Create(b): %v", err)
2243 }
2244 fb.Close()
2245
2246 ia1, err := Stat("a")
2247 if err != nil {
2248 t.Fatalf("Stat(a): %v", err)
2249 }
2250 ia2, err := Stat("a")
2251 if err != nil {
2252 t.Fatalf("Stat(a): %v", err)
2253 }
2254 if !SameFile(ia1, ia2) {
2255 t.Errorf("files should be same")
2256 }
2257
2258 ib, err := Stat("b")
2259 if err != nil {
2260 t.Fatalf("Stat(b): %v", err)
2261 }
2262 if SameFile(ia1, ib) {
2263 t.Errorf("files should be different")
2264 }
2265 }
2266
2267 func testDevNullFileInfo(t *testing.T, statname, devNullName string, fi FileInfo) {
2268 pre := fmt.Sprintf("%s(%q): ", statname, devNullName)
2269 if fi.Size() != 0 {
2270 t.Errorf(pre+"wrong file size have %d want 0", fi.Size())
2271 }
2272 if fi.Mode()&ModeDevice == 0 {
2273 t.Errorf(pre+"wrong file mode %q: ModeDevice is not set", fi.Mode())
2274 }
2275 if fi.Mode()&ModeCharDevice == 0 {
2276 t.Errorf(pre+"wrong file mode %q: ModeCharDevice is not set", fi.Mode())
2277 }
2278 if fi.Mode().IsRegular() {
2279 t.Errorf(pre+"wrong file mode %q: IsRegular returns true", fi.Mode())
2280 }
2281 }
2282
2283 func testDevNullFile(t *testing.T, devNullName string) {
2284 f, err := Open(devNullName)
2285 if err != nil {
2286 t.Fatalf("Open(%s): %v", devNullName, err)
2287 }
2288 defer f.Close()
2289
2290 fi, err := f.Stat()
2291 if err != nil {
2292 t.Fatalf("Stat(%s): %v", devNullName, err)
2293 }
2294 testDevNullFileInfo(t, "f.Stat", devNullName, fi)
2295
2296 fi, err = Stat(devNullName)
2297 if err != nil {
2298 t.Fatalf("Stat(%s): %v", devNullName, err)
2299 }
2300 testDevNullFileInfo(t, "Stat", devNullName, fi)
2301 }
2302
2303 func TestDevNullFile(t *testing.T) {
2304 t.Parallel()
2305
2306 testDevNullFile(t, DevNull)
2307 if runtime.GOOS == "windows" {
2308 testDevNullFile(t, "./nul")
2309 testDevNullFile(t, "//./nul")
2310 }
2311 }
2312
2313 var testLargeWrite = flag.Bool("large_write", false, "run TestLargeWriteToConsole test that floods console with output")
2314
2315 func TestLargeWriteToConsole(t *testing.T) {
2316 if !*testLargeWrite {
2317 t.Skip("skipping console-flooding test; enable with -large_write")
2318 }
2319 b := make([]byte, 32000)
2320 for i := range b {
2321 b[i] = '.'
2322 }
2323 b[len(b)-1] = '\n'
2324 n, err := Stdout.Write(b)
2325 if err != nil {
2326 t.Fatalf("Write to os.Stdout failed: %v", err)
2327 }
2328 if n != len(b) {
2329 t.Errorf("Write to os.Stdout should return %d; got %d", len(b), n)
2330 }
2331 n, err = Stderr.Write(b)
2332 if err != nil {
2333 t.Fatalf("Write to os.Stderr failed: %v", err)
2334 }
2335 if n != len(b) {
2336 t.Errorf("Write to os.Stderr should return %d; got %d", len(b), n)
2337 }
2338 }
2339
2340 func TestStatDirModeExec(t *testing.T) {
2341 if runtime.GOOS == "wasip1" {
2342 t.Skip("Chmod is not supported on " + runtime.GOOS)
2343 }
2344 t.Parallel()
2345
2346 const mode = 0111
2347
2348 path := t.TempDir()
2349 if err := Chmod(path, 0777); err != nil {
2350 t.Fatalf("Chmod %q 0777: %v", path, err)
2351 }
2352
2353 dir, err := Stat(path)
2354 if err != nil {
2355 t.Fatalf("Stat %q (looking for mode %#o): %s", path, mode, err)
2356 }
2357 if dir.Mode()&mode != mode {
2358 t.Errorf("Stat %q: mode %#o want %#o", path, dir.Mode()&mode, mode)
2359 }
2360 }
2361
2362 func TestStatStdin(t *testing.T) {
2363 switch runtime.GOOS {
2364 case "android", "plan9":
2365 t.Skipf("%s doesn't have /bin/sh", runtime.GOOS)
2366 }
2367
2368 if Getenv("GO_WANT_HELPER_PROCESS") == "1" {
2369 st, err := Stdin.Stat()
2370 if err != nil {
2371 t.Fatalf("Stat failed: %v", err)
2372 }
2373 fmt.Println(st.Mode() & ModeNamedPipe)
2374 Exit(0)
2375 }
2376
2377 testenv.MustHaveExec(t)
2378 t.Parallel()
2379
2380 fi, err := Stdin.Stat()
2381 if err != nil {
2382 t.Fatal(err)
2383 }
2384 switch mode := fi.Mode(); {
2385 case mode&ModeCharDevice != 0 && mode&ModeDevice != 0:
2386 case mode&ModeNamedPipe != 0:
2387 default:
2388 t.Fatalf("unexpected Stdin mode (%v), want ModeCharDevice or ModeNamedPipe", mode)
2389 }
2390
2391 var cmd *exec.Cmd
2392 if runtime.GOOS == "windows" {
2393 cmd = testenv.Command(t, "cmd", "/c", "echo output | "+Args[0]+" -test.run=TestStatStdin")
2394 } else {
2395 cmd = testenv.Command(t, "/bin/sh", "-c", "echo output | "+Args[0]+" -test.run=TestStatStdin")
2396 }
2397 cmd.Env = append(Environ(), "GO_WANT_HELPER_PROCESS=1")
2398
2399 output, err := cmd.CombinedOutput()
2400 if err != nil {
2401 t.Fatalf("Failed to spawn child process: %v %q", err, string(output))
2402 }
2403
2404
2405 if len(output) < 1 || output[0] != 'p' {
2406 t.Fatalf("Child process reports stdin is not pipe '%v'", string(output))
2407 }
2408 }
2409
2410 func TestStatRelativeSymlink(t *testing.T) {
2411 testenv.MustHaveSymlink(t)
2412 t.Parallel()
2413
2414 tmpdir := t.TempDir()
2415 target := filepath.Join(tmpdir, "target")
2416 f, err := Create(target)
2417 if err != nil {
2418 t.Fatal(err)
2419 }
2420 defer f.Close()
2421
2422 st, err := f.Stat()
2423 if err != nil {
2424 t.Fatal(err)
2425 }
2426
2427 link := filepath.Join(tmpdir, "link")
2428 err = Symlink(filepath.Base(target), link)
2429 if err != nil {
2430 t.Fatal(err)
2431 }
2432
2433 st1, err := Stat(link)
2434 if err != nil {
2435 t.Fatal(err)
2436 }
2437
2438 if !SameFile(st, st1) {
2439 t.Error("Stat doesn't follow relative symlink")
2440 }
2441
2442 if runtime.GOOS == "windows" {
2443 Remove(link)
2444 err = Symlink(target[len(filepath.VolumeName(target)):], link)
2445 if err != nil {
2446 t.Fatal(err)
2447 }
2448
2449 st1, err := Stat(link)
2450 if err != nil {
2451 t.Fatal(err)
2452 }
2453
2454 if !SameFile(st, st1) {
2455 t.Error("Stat doesn't follow relative symlink")
2456 }
2457 }
2458 }
2459
2460 func TestReadAtEOF(t *testing.T) {
2461 t.Parallel()
2462
2463 f := newFile("TestReadAtEOF", t)
2464 defer Remove(f.Name())
2465 defer f.Close()
2466
2467 _, err := f.ReadAt(make([]byte, 10), 0)
2468 switch err {
2469 case io.EOF:
2470
2471 case nil:
2472 t.Fatalf("ReadAt succeeded")
2473 default:
2474 t.Fatalf("ReadAt failed: %s", err)
2475 }
2476 }
2477
2478 func TestLongPath(t *testing.T) {
2479 t.Parallel()
2480
2481 tmpdir := newDir("TestLongPath", t)
2482 defer func(d string) {
2483 if err := RemoveAll(d); err != nil {
2484 t.Fatalf("RemoveAll failed: %v", err)
2485 }
2486 }(tmpdir)
2487
2488
2489 sizes := []int{247, 248, 249, 400}
2490 for len(tmpdir) < 400 {
2491 tmpdir += "/dir3456789"
2492 }
2493 for _, sz := range sizes {
2494 t.Run(fmt.Sprintf("length=%d", sz), func(t *testing.T) {
2495 sizedTempDir := tmpdir[:sz-1] + "x"
2496
2497
2498
2499 if err := MkdirAll(sizedTempDir, 0755); err != nil {
2500 t.Fatalf("MkdirAll failed: %v", err)
2501 }
2502 data := []byte("hello world\n")
2503 if err := WriteFile(sizedTempDir+"/foo.txt", data, 0644); err != nil {
2504 t.Fatalf("os.WriteFile() failed: %v", err)
2505 }
2506 if err := Rename(sizedTempDir+"/foo.txt", sizedTempDir+"/bar.txt"); err != nil {
2507 t.Fatalf("Rename failed: %v", err)
2508 }
2509 mtime := time.Now().Truncate(time.Minute)
2510 if err := Chtimes(sizedTempDir+"/bar.txt", mtime, mtime); err != nil {
2511 t.Fatalf("Chtimes failed: %v", err)
2512 }
2513 names := []string{"bar.txt"}
2514 if testenv.HasSymlink() {
2515 if err := Symlink(sizedTempDir+"/bar.txt", sizedTempDir+"/symlink.txt"); err != nil {
2516 t.Fatalf("Symlink failed: %v", err)
2517 }
2518 names = append(names, "symlink.txt")
2519 }
2520 if testenv.HasLink() {
2521 if err := Link(sizedTempDir+"/bar.txt", sizedTempDir+"/link.txt"); err != nil {
2522 t.Fatalf("Link failed: %v", err)
2523 }
2524 names = append(names, "link.txt")
2525 }
2526 for _, wantSize := range []int64{int64(len(data)), 0} {
2527 for _, name := range names {
2528 path := sizedTempDir + "/" + name
2529 dir, err := Stat(path)
2530 if err != nil {
2531 t.Fatalf("Stat(%q) failed: %v", path, err)
2532 }
2533 filesize := size(path, t)
2534 if dir.Size() != filesize || filesize != wantSize {
2535 t.Errorf("Size(%q) is %d, len(ReadFile()) is %d, want %d", path, dir.Size(), filesize, wantSize)
2536 }
2537 if runtime.GOOS != "wasip1" {
2538 err = Chmod(path, dir.Mode())
2539 if err != nil {
2540 t.Fatalf("Chmod(%q) failed: %v", path, err)
2541 }
2542 }
2543 }
2544 if err := Truncate(sizedTempDir+"/bar.txt", 0); err != nil {
2545 t.Fatalf("Truncate failed: %v", err)
2546 }
2547 }
2548 })
2549 }
2550 }
2551
2552 func testKillProcess(t *testing.T, processKiller func(p *Process)) {
2553 testenv.MustHaveExec(t)
2554 t.Parallel()
2555
2556
2557 cmd := testenv.Command(t, Args[0])
2558 cmd.Env = append(cmd.Environ(), "GO_OS_TEST_DRAIN_STDIN=1")
2559 stdout, err := cmd.StdoutPipe()
2560 if err != nil {
2561 t.Fatal(err)
2562 }
2563 stdin, err := cmd.StdinPipe()
2564 if err != nil {
2565 t.Fatal(err)
2566 }
2567 err = cmd.Start()
2568 if err != nil {
2569 t.Fatalf("Failed to start test process: %v", err)
2570 }
2571
2572 defer func() {
2573 if err := cmd.Wait(); err == nil {
2574 t.Errorf("Test process succeeded, but expected to fail")
2575 }
2576 stdin.Close()
2577 }()
2578
2579
2580
2581 io.Copy(io.Discard, stdout)
2582
2583 processKiller(cmd.Process)
2584 }
2585
2586 func TestKillStartProcess(t *testing.T) {
2587 testKillProcess(t, func(p *Process) {
2588 err := p.Kill()
2589 if err != nil {
2590 t.Fatalf("Failed to kill test process: %v", err)
2591 }
2592 })
2593 }
2594
2595 func TestGetppid(t *testing.T) {
2596 if runtime.GOOS == "plan9" {
2597
2598 t.Skipf("skipping test on plan9; see issue 8206")
2599 }
2600
2601 if Getenv("GO_WANT_HELPER_PROCESS") == "1" {
2602 fmt.Print(Getppid())
2603 Exit(0)
2604 }
2605
2606 testenv.MustHaveExec(t)
2607 t.Parallel()
2608
2609 cmd := testenv.Command(t, Args[0], "-test.run=TestGetppid")
2610 cmd.Env = append(Environ(), "GO_WANT_HELPER_PROCESS=1")
2611
2612
2613 output, err := cmd.CombinedOutput()
2614 if err != nil {
2615 t.Fatalf("Failed to spawn child process: %v %q", err, string(output))
2616 }
2617
2618 childPpid := string(output)
2619 ourPid := fmt.Sprintf("%d", Getpid())
2620 if childPpid != ourPid {
2621 t.Fatalf("Child process reports parent process id '%v', expected '%v'", childPpid, ourPid)
2622 }
2623 }
2624
2625 func TestKillFindProcess(t *testing.T) {
2626 testKillProcess(t, func(p *Process) {
2627 p2, err := FindProcess(p.Pid)
2628 if err != nil {
2629 t.Fatalf("Failed to find test process: %v", err)
2630 }
2631 err = p2.Kill()
2632 if err != nil {
2633 t.Fatalf("Failed to kill test process: %v", err)
2634 }
2635 })
2636 }
2637
2638 var nilFileMethodTests = []struct {
2639 name string
2640 f func(*File) error
2641 }{
2642 {"Chdir", func(f *File) error { return f.Chdir() }},
2643 {"Close", func(f *File) error { return f.Close() }},
2644 {"Chmod", func(f *File) error { return f.Chmod(0) }},
2645 {"Chown", func(f *File) error { return f.Chown(0, 0) }},
2646 {"Read", func(f *File) error { _, err := f.Read(make([]byte, 0)); return err }},
2647 {"ReadAt", func(f *File) error { _, err := f.ReadAt(make([]byte, 0), 0); return err }},
2648 {"Readdir", func(f *File) error { _, err := f.Readdir(1); return err }},
2649 {"Readdirnames", func(f *File) error { _, err := f.Readdirnames(1); return err }},
2650 {"Seek", func(f *File) error { _, err := f.Seek(0, io.SeekStart); return err }},
2651 {"Stat", func(f *File) error { _, err := f.Stat(); return err }},
2652 {"Sync", func(f *File) error { return f.Sync() }},
2653 {"Truncate", func(f *File) error { return f.Truncate(0) }},
2654 {"Write", func(f *File) error { _, err := f.Write(make([]byte, 0)); return err }},
2655 {"WriteAt", func(f *File) error { _, err := f.WriteAt(make([]byte, 0), 0); return err }},
2656 {"WriteString", func(f *File) error { _, err := f.WriteString(""); return err }},
2657 }
2658
2659
2660 func TestNilFileMethods(t *testing.T) {
2661 t.Parallel()
2662
2663 for _, tt := range nilFileMethodTests {
2664 var file *File
2665 got := tt.f(file)
2666 if got != ErrInvalid {
2667 t.Errorf("%v should fail when f is nil; got %v", tt.name, got)
2668 }
2669 }
2670 }
2671
2672 func mkdirTree(t *testing.T, root string, level, max int) {
2673 if level >= max {
2674 return
2675 }
2676 level++
2677 for i := 'a'; i < 'c'; i++ {
2678 dir := filepath.Join(root, string(i))
2679 if err := Mkdir(dir, 0700); err != nil {
2680 t.Fatal(err)
2681 }
2682 mkdirTree(t, dir, level, max)
2683 }
2684 }
2685
2686
2687
2688 func TestRemoveAllRace(t *testing.T) {
2689 if runtime.GOOS == "windows" {
2690
2691
2692
2693
2694 t.Skip("skipping on windows")
2695 }
2696 if runtime.GOOS == "dragonfly" {
2697 testenv.SkipFlaky(t, 52301)
2698 }
2699
2700 n := runtime.GOMAXPROCS(16)
2701 defer runtime.GOMAXPROCS(n)
2702 root, err := MkdirTemp("", "issue")
2703 if err != nil {
2704 t.Fatal(err)
2705 }
2706 mkdirTree(t, root, 1, 6)
2707 hold := make(chan struct{})
2708 var wg sync.WaitGroup
2709 for i := 0; i < 4; i++ {
2710 wg.Add(1)
2711 go func() {
2712 defer wg.Done()
2713 <-hold
2714 err := RemoveAll(root)
2715 if err != nil {
2716 t.Errorf("unexpected error: %T, %q", err, err)
2717 }
2718 }()
2719 }
2720 close(hold)
2721 wg.Wait()
2722 }
2723
2724
2725 func TestPipeThreads(t *testing.T) {
2726 switch runtime.GOOS {
2727 case "illumos", "solaris":
2728 t.Skip("skipping on Solaris and illumos; issue 19111")
2729 case "windows":
2730 t.Skip("skipping on Windows; issue 19098")
2731 case "plan9":
2732 t.Skip("skipping on Plan 9; does not support runtime poller")
2733 case "js":
2734 t.Skip("skipping on js; no support for os.Pipe")
2735 case "wasip1":
2736 t.Skip("skipping on wasip1; no support for os.Pipe")
2737 }
2738
2739 threads := 100
2740
2741
2742 if runtime.GOOS == "openbsd" {
2743 threads = 50
2744 }
2745
2746 r := make([]*File, threads)
2747 w := make([]*File, threads)
2748 for i := 0; i < threads; i++ {
2749 rp, wp, err := Pipe()
2750 if err != nil {
2751 for j := 0; j < i; j++ {
2752 r[j].Close()
2753 w[j].Close()
2754 }
2755 t.Fatal(err)
2756 }
2757 r[i] = rp
2758 w[i] = wp
2759 }
2760
2761 defer debug.SetMaxThreads(debug.SetMaxThreads(threads / 2))
2762
2763 creading := make(chan bool, threads)
2764 cdone := make(chan bool, threads)
2765 for i := 0; i < threads; i++ {
2766 go func(i int) {
2767 var b [1]byte
2768 creading <- true
2769 if _, err := r[i].Read(b[:]); err != nil {
2770 t.Error(err)
2771 }
2772 if err := r[i].Close(); err != nil {
2773 t.Error(err)
2774 }
2775 cdone <- true
2776 }(i)
2777 }
2778
2779 for i := 0; i < threads; i++ {
2780 <-creading
2781 }
2782
2783
2784
2785
2786 for i := 0; i < threads; i++ {
2787 if _, err := w[i].Write([]byte{0}); err != nil {
2788 t.Error(err)
2789 }
2790 if err := w[i].Close(); err != nil {
2791 t.Error(err)
2792 }
2793 <-cdone
2794 }
2795 }
2796
2797 func testDoubleCloseError(path string) func(*testing.T) {
2798 return func(t *testing.T) {
2799 t.Parallel()
2800
2801 file, err := Open(path)
2802 if err != nil {
2803 t.Fatal(err)
2804 }
2805 if err := file.Close(); err != nil {
2806 t.Fatalf("unexpected error from Close: %v", err)
2807 }
2808 if err := file.Close(); err == nil {
2809 t.Error("second Close did not fail")
2810 } else if pe, ok := err.(*PathError); !ok {
2811 t.Errorf("second Close: got %T, want %T", err, pe)
2812 } else if pe.Err != ErrClosed {
2813 t.Errorf("second Close: got %q, want %q", pe.Err, ErrClosed)
2814 } else {
2815 t.Logf("second close returned expected error %q", err)
2816 }
2817 }
2818 }
2819
2820 func TestDoubleCloseError(t *testing.T) {
2821 t.Parallel()
2822 t.Run("file", testDoubleCloseError(filepath.Join(sfdir, sfname)))
2823 t.Run("dir", testDoubleCloseError(sfdir))
2824 }
2825
2826 func TestUserHomeDir(t *testing.T) {
2827 t.Parallel()
2828
2829 dir, err := UserHomeDir()
2830 if dir == "" && err == nil {
2831 t.Fatal("UserHomeDir returned an empty string but no error")
2832 }
2833 if err != nil {
2834
2835
2836 t.Skipf("skipping: %v", err)
2837 }
2838
2839 fi, err := Stat(dir)
2840 if err != nil {
2841 if IsNotExist(err) {
2842
2843
2844
2845 t.Log(err)
2846 return
2847 }
2848 t.Fatal(err)
2849 }
2850 if !fi.IsDir() {
2851 t.Fatalf("dir %s is not directory; type = %v", dir, fi.Mode())
2852 }
2853 }
2854
2855 func TestDirSeek(t *testing.T) {
2856 t.Parallel()
2857
2858 wd, err := Getwd()
2859 if err != nil {
2860 t.Fatal(err)
2861 }
2862 f, err := Open(wd)
2863 if err != nil {
2864 t.Fatal(err)
2865 }
2866 dirnames1, err := f.Readdirnames(0)
2867 if err != nil {
2868 t.Fatal(err)
2869 }
2870
2871 ret, err := f.Seek(0, 0)
2872 if err != nil {
2873 t.Fatal(err)
2874 }
2875 if ret != 0 {
2876 t.Fatalf("seek result not zero: %d", ret)
2877 }
2878
2879 dirnames2, err := f.Readdirnames(0)
2880 if err != nil {
2881 t.Fatal(err)
2882 }
2883
2884 if len(dirnames1) != len(dirnames2) {
2885 t.Fatalf("listings have different lengths: %d and %d\n", len(dirnames1), len(dirnames2))
2886 }
2887 for i, n1 := range dirnames1 {
2888 n2 := dirnames2[i]
2889 if n1 != n2 {
2890 t.Fatalf("different name i=%d n1=%s n2=%s\n", i, n1, n2)
2891 }
2892 }
2893 }
2894
2895 func TestReaddirSmallSeek(t *testing.T) {
2896
2897
2898
2899 t.Parallel()
2900
2901 wd, err := Getwd()
2902 if err != nil {
2903 t.Fatal(err)
2904 }
2905 df, err := Open(filepath.Join(wd, "testdata", "issue37161"))
2906 if err != nil {
2907 t.Fatal(err)
2908 }
2909 names1, err := df.Readdirnames(1)
2910 if err != nil {
2911 t.Fatal(err)
2912 }
2913 if _, err = df.Seek(0, 0); err != nil {
2914 t.Fatal(err)
2915 }
2916 names2, err := df.Readdirnames(0)
2917 if err != nil {
2918 t.Fatal(err)
2919 }
2920 if len(names2) != 3 {
2921 t.Fatalf("first names: %v, second names: %v", names1, names2)
2922 }
2923 }
2924
2925
2926
2927 func isDeadlineExceeded(err error) bool {
2928 if !IsTimeout(err) {
2929 return false
2930 }
2931 if !errors.Is(err, ErrDeadlineExceeded) {
2932 return false
2933 }
2934 return true
2935 }
2936
2937
2938 func TestOpenFileKeepsPermissions(t *testing.T) {
2939 t.Parallel()
2940
2941 dir := t.TempDir()
2942 name := filepath.Join(dir, "x")
2943 f, err := Create(name)
2944 if err != nil {
2945 t.Fatal(err)
2946 }
2947 if err := f.Close(); err != nil {
2948 t.Error(err)
2949 }
2950 f, err = OpenFile(name, O_WRONLY|O_CREATE|O_TRUNC, 0)
2951 if err != nil {
2952 t.Fatal(err)
2953 }
2954 if fi, err := f.Stat(); err != nil {
2955 t.Error(err)
2956 } else if fi.Mode()&0222 == 0 {
2957 t.Errorf("f.Stat.Mode after OpenFile is %v, should be writable", fi.Mode())
2958 }
2959 if err := f.Close(); err != nil {
2960 t.Error(err)
2961 }
2962 if fi, err := Stat(name); err != nil {
2963 t.Error(err)
2964 } else if fi.Mode()&0222 == 0 {
2965 t.Errorf("Stat after OpenFile is %v, should be writable", fi.Mode())
2966 }
2967 }
2968
2969 func TestDirFS(t *testing.T) {
2970 t.Parallel()
2971
2972
2973
2974 if runtime.GOOS == "windows" {
2975 if err := filepath.WalkDir("./testdata/dirfs", func(path string, d fs.DirEntry, err error) error {
2976 if err != nil {
2977 t.Fatal(err)
2978 }
2979 info, err := d.Info()
2980 if err != nil {
2981 t.Fatal(err)
2982 }
2983 stat, err := Stat(path)
2984 if err != nil {
2985 t.Fatal(err)
2986 }
2987 if stat.ModTime() == info.ModTime() {
2988 return nil
2989 }
2990 if err := Chtimes(path, stat.ModTime(), stat.ModTime()); err != nil {
2991 t.Log(err)
2992 }
2993 return nil
2994 }); err != nil {
2995 t.Fatal(err)
2996 }
2997 }
2998 fsys := DirFS("./testdata/dirfs")
2999 if err := fstest.TestFS(fsys, "a", "b", "dir/x"); err != nil {
3000 t.Fatal(err)
3001 }
3002
3003 rdfs, ok := fsys.(fs.ReadDirFS)
3004 if !ok {
3005 t.Error("expected DirFS result to implement fs.ReadDirFS")
3006 }
3007 if _, err := rdfs.ReadDir("nonexistent"); err == nil {
3008 t.Error("fs.ReadDir of nonexistent directory succeeded")
3009 }
3010
3011
3012
3013 const nonesuch = "dir/nonesuch"
3014 _, err := fsys.Open(nonesuch)
3015 if err == nil {
3016 t.Error("fs.Open of nonexistent file succeeded")
3017 } else {
3018 if !strings.Contains(err.Error(), nonesuch) {
3019 t.Errorf("error %q does not contain %q", err, nonesuch)
3020 }
3021 if strings.Contains(err.(*PathError).Path, "testdata") {
3022 t.Errorf("error %q contains %q", err, "testdata")
3023 }
3024 }
3025
3026
3027 d := DirFS(".")
3028 _, err = d.Open(`testdata\dirfs`)
3029 if err == nil {
3030 t.Fatalf(`Open testdata\dirfs succeeded`)
3031 }
3032
3033
3034 _, err = d.Open(`NUL`)
3035 if err == nil {
3036 t.Errorf(`Open NUL succeeded`)
3037 }
3038 }
3039
3040 func TestDirFSRootDir(t *testing.T) {
3041 t.Parallel()
3042
3043 cwd, err := Getwd()
3044 if err != nil {
3045 t.Fatal(err)
3046 }
3047 cwd = cwd[len(filepath.VolumeName(cwd)):]
3048 cwd = filepath.ToSlash(cwd)
3049 cwd = strings.TrimPrefix(cwd, "/")
3050
3051
3052 d := DirFS("/")
3053 f, err := d.Open(cwd + "/testdata/dirfs/a")
3054 if err != nil {
3055 t.Fatal(err)
3056 }
3057 f.Close()
3058 }
3059
3060 func TestDirFSEmptyDir(t *testing.T) {
3061 t.Parallel()
3062
3063 d := DirFS("")
3064 cwd, _ := Getwd()
3065 for _, path := range []string{
3066 "testdata/dirfs/a",
3067 filepath.ToSlash(cwd) + "/testdata/dirfs/a",
3068 } {
3069 _, err := d.Open(path)
3070 if err == nil {
3071 t.Fatalf(`DirFS("").Open(%q) succeeded`, path)
3072 }
3073 }
3074 }
3075
3076 func TestDirFSPathsValid(t *testing.T) {
3077 if runtime.GOOS == "windows" {
3078 t.Skipf("skipping on Windows")
3079 }
3080 t.Parallel()
3081
3082 d := t.TempDir()
3083 if err := WriteFile(filepath.Join(d, "control.txt"), []byte(string("Hello, world!")), 0644); err != nil {
3084 t.Fatal(err)
3085 }
3086 if err := WriteFile(filepath.Join(d, `e:xperi\ment.txt`), []byte(string("Hello, colon and backslash!")), 0644); err != nil {
3087 t.Fatal(err)
3088 }
3089
3090 fsys := DirFS(d)
3091 err := fs.WalkDir(fsys, ".", func(path string, e fs.DirEntry, err error) error {
3092 if fs.ValidPath(e.Name()) {
3093 t.Logf("%q ok", e.Name())
3094 } else {
3095 t.Errorf("%q INVALID", e.Name())
3096 }
3097 return nil
3098 })
3099 if err != nil {
3100 t.Fatal(err)
3101 }
3102 }
3103
3104 func TestReadFileProc(t *testing.T) {
3105 t.Parallel()
3106
3107
3108
3109
3110
3111
3112 name := "/proc/sys/fs/pipe-max-size"
3113 if _, err := Stat(name); err != nil {
3114 t.Skip(err)
3115 }
3116 data, err := ReadFile(name)
3117 if err != nil {
3118 t.Fatal(err)
3119 }
3120 if len(data) == 0 || data[len(data)-1] != '\n' {
3121 t.Fatalf("read %s: not newline-terminated: %q", name, data)
3122 }
3123 }
3124
3125 func TestDirFSReadFileProc(t *testing.T) {
3126 t.Parallel()
3127
3128 fsys := DirFS("/")
3129 name := "proc/sys/fs/pipe-max-size"
3130 if _, err := fs.Stat(fsys, name); err != nil {
3131 t.Skip()
3132 }
3133 data, err := fs.ReadFile(fsys, name)
3134 if err != nil {
3135 t.Fatal(err)
3136 }
3137 if len(data) == 0 || data[len(data)-1] != '\n' {
3138 t.Fatalf("read %s: not newline-terminated: %q", name, data)
3139 }
3140 }
3141
3142 func TestWriteStringAlloc(t *testing.T) {
3143 if runtime.GOOS == "js" {
3144 t.Skip("js allocates a lot during File.WriteString")
3145 }
3146 d := t.TempDir()
3147 f, err := Create(filepath.Join(d, "whiteboard.txt"))
3148 if err != nil {
3149 t.Fatal(err)
3150 }
3151 defer f.Close()
3152 allocs := testing.AllocsPerRun(100, func() {
3153 f.WriteString("I will not allocate when passed a string longer than 32 bytes.\n")
3154 })
3155 if allocs != 0 {
3156 t.Errorf("expected 0 allocs for File.WriteString, got %v", allocs)
3157 }
3158 }
3159
3160
3161 func TestPipeIOCloseRace(t *testing.T) {
3162
3163 if runtime.GOOS == "js" || runtime.GOOS == "wasip1" {
3164 t.Skipf("skipping on %s: no pipes", runtime.GOOS)
3165 }
3166 t.Parallel()
3167
3168 r, w, err := Pipe()
3169 if err != nil {
3170 t.Fatal(err)
3171 }
3172
3173 var wg sync.WaitGroup
3174 wg.Add(3)
3175
3176 go func() {
3177 defer wg.Done()
3178 for {
3179 n, err := w.Write([]byte("hi"))
3180 if err != nil {
3181
3182
3183 switch {
3184 case errors.Is(err, ErrClosed),
3185 strings.Contains(err.Error(), "broken pipe"),
3186 strings.Contains(err.Error(), "pipe is being closed"),
3187 strings.Contains(err.Error(), "hungup channel"):
3188
3189 default:
3190
3191 t.Error(err)
3192 }
3193 return
3194 }
3195 if n != 2 {
3196 t.Errorf("wrote %d bytes, expected 2", n)
3197 return
3198 }
3199 }
3200 }()
3201
3202 go func() {
3203 defer wg.Done()
3204 for {
3205 var buf [2]byte
3206 n, err := r.Read(buf[:])
3207 if err != nil {
3208 if err != io.EOF && !errors.Is(err, ErrClosed) {
3209 t.Error(err)
3210 }
3211 return
3212 }
3213 if n != 2 {
3214 t.Errorf("read %d bytes, want 2", n)
3215 }
3216 }
3217 }()
3218
3219 go func() {
3220 defer wg.Done()
3221
3222
3223
3224
3225 time.Sleep(time.Millisecond)
3226
3227 if err := r.Close(); err != nil {
3228 t.Error(err)
3229 }
3230 if err := w.Close(); err != nil {
3231 t.Error(err)
3232 }
3233 }()
3234
3235 wg.Wait()
3236 }
3237
3238
3239 func TestPipeCloseRace(t *testing.T) {
3240
3241 if runtime.GOOS == "js" || runtime.GOOS == "wasip1" {
3242 t.Skipf("skipping on %s: no pipes", runtime.GOOS)
3243 }
3244 t.Parallel()
3245
3246 r, w, err := Pipe()
3247 if err != nil {
3248 t.Fatal(err)
3249 }
3250 var wg sync.WaitGroup
3251 c := make(chan error, 4)
3252 f := func() {
3253 defer wg.Done()
3254 c <- r.Close()
3255 c <- w.Close()
3256 }
3257 wg.Add(2)
3258 go f()
3259 go f()
3260 nils, errs := 0, 0
3261 for i := 0; i < 4; i++ {
3262 err := <-c
3263 if err == nil {
3264 nils++
3265 } else {
3266 errs++
3267 }
3268 }
3269 if nils != 2 || errs != 2 {
3270 t.Errorf("got nils %d errs %d, want 2 2", nils, errs)
3271 }
3272 }
3273
View as plain text