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