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