github.com/akaros/go-akaros@v0.0.0-20181004170632-85005d477eab/src/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 "bytes" 9 "errors" 10 "flag" 11 "fmt" 12 "io" 13 "io/ioutil" 14 . "os" 15 osexec "os/exec" 16 "path/filepath" 17 "reflect" 18 "runtime" 19 "sort" 20 "strings" 21 "sync" 22 "syscall" 23 "testing" 24 "text/template" 25 "time" 26 ) 27 28 var supportsSymlinks = true 29 30 var dot = []string{ 31 "dir_unix.go", 32 "env.go", 33 "error.go", 34 "file.go", 35 "os_test.go", 36 "types.go", 37 "stat_darwin.go", 38 "stat_linux.go", 39 "stat_akaros.go", 40 } 41 42 type sysDir struct { 43 name string 44 files []string 45 } 46 47 var sysdir = func() (sd *sysDir) { 48 switch runtime.GOOS { 49 case "android": 50 sd = &sysDir{ 51 "/system/etc", 52 []string{ 53 "audio_policy.conf", 54 "system_fonts.xml", 55 }, 56 } 57 case "windows": 58 sd = &sysDir{ 59 Getenv("SystemRoot") + "\\system32\\drivers\\etc", 60 []string{ 61 "networks", 62 "protocol", 63 "services", 64 }, 65 } 66 case "plan9": 67 case "akaros": 68 sd = &sysDir{ 69 "/lib/ndb", 70 []string{ 71 "common", 72 "local", 73 }, 74 } 75 default: 76 sd = &sysDir{ 77 "/etc", 78 []string{ 79 "group", 80 "hosts", 81 "passwd", 82 }, 83 } 84 } 85 return 86 }() 87 88 func size(name string, t *testing.T) int64 { 89 file, err := Open(name) 90 if err != nil { 91 t.Fatal("open failed:", err) 92 } 93 defer file.Close() 94 var buf [100]byte 95 len := 0 96 for { 97 n, e := file.Read(buf[0:]) 98 len += n 99 if e == io.EOF { 100 break 101 } 102 if e != nil { 103 t.Fatal("read failed:", err) 104 } 105 } 106 return int64(len) 107 } 108 109 func equal(name1, name2 string) (r bool) { 110 switch runtime.GOOS { 111 case "windows": 112 r = strings.ToLower(name1) == strings.ToLower(name2) 113 default: 114 r = name1 == name2 115 } 116 return 117 } 118 119 func newFile(testName string, t *testing.T) (f *File) { 120 // Use a local file system, not NFS. 121 // On Unix, override $TMPDIR in case the user 122 // has it set to an NFS-mounted directory. 123 dir := "" 124 if runtime.GOOS != "android" && runtime.GOOS != "windows" { 125 dir = "/tmp" 126 } 127 f, err := ioutil.TempFile(dir, "_Go_"+testName) 128 if err != nil { 129 t.Fatalf("TempFile %s: %s", testName, err) 130 } 131 return 132 } 133 134 func newDir(testName string, t *testing.T) (name string) { 135 // Use a local file system, not NFS. 136 // On Unix, override $TMPDIR in case the user 137 // has it set to an NFS-mounted directory. 138 dir := "" 139 if runtime.GOOS != "android" && runtime.GOOS != "windows" { 140 dir = "/tmp" 141 } 142 name, err := ioutil.TempDir(dir, "_Go_"+testName) 143 if err != nil { 144 t.Fatalf("TempDir %s: %s", testName, err) 145 } 146 return 147 } 148 149 var sfdir = sysdir.name 150 var sfname = sysdir.files[0] 151 152 func TestStat(t *testing.T) { 153 path := sfdir + "/" + sfname 154 dir, err := Stat(path) 155 if err != nil { 156 t.Fatal("stat failed:", err) 157 } 158 if !equal(sfname, dir.Name()) { 159 t.Error("name should be ", sfname, "; is", dir.Name()) 160 } 161 filesize := size(path, t) 162 if dir.Size() != filesize { 163 t.Error("size should be", filesize, "; is", dir.Size()) 164 } 165 } 166 167 func TestFstat(t *testing.T) { 168 path := sfdir + "/" + sfname 169 file, err1 := Open(path) 170 if err1 != nil { 171 t.Fatal("open failed:", err1) 172 } 173 defer file.Close() 174 dir, err2 := file.Stat() 175 if err2 != nil { 176 t.Fatal("fstat failed:", err2) 177 } 178 if !equal(sfname, dir.Name()) { 179 t.Error("name should be ", sfname, "; is", dir.Name()) 180 } 181 filesize := size(path, t) 182 if dir.Size() != filesize { 183 t.Error("size should be", filesize, "; is", dir.Size()) 184 } 185 } 186 187 func TestLstat(t *testing.T) { 188 path := sfdir + "/" + sfname 189 dir, err := Lstat(path) 190 if err != nil { 191 t.Fatal("lstat failed:", err) 192 } 193 if !equal(sfname, dir.Name()) { 194 t.Error("name should be ", sfname, "; is", dir.Name()) 195 } 196 filesize := size(path, t) 197 if dir.Size() != filesize { 198 t.Error("size should be", filesize, "; is", dir.Size()) 199 } 200 } 201 202 // Read with length 0 should not return EOF. 203 func TestRead0(t *testing.T) { 204 path := sfdir + "/" + sfname 205 f, err := Open(path) 206 if err != nil { 207 t.Fatal("open failed:", err) 208 } 209 defer f.Close() 210 211 b := make([]byte, 0) 212 n, err := f.Read(b) 213 if n != 0 || err != nil { 214 t.Errorf("Read(0) = %d, %v, want 0, nil", n, err) 215 } 216 b = make([]byte, 100) 217 n, err = f.Read(b) 218 if n <= 0 || err != nil { 219 t.Errorf("Read(100) = %d, %v, want >0, nil", n, err) 220 } 221 } 222 223 func testReaddirnames(dir string, contents []string, t *testing.T) { 224 file, err := Open(dir) 225 if err != nil { 226 t.Fatalf("open %q failed: %v", dir, err) 227 } 228 defer file.Close() 229 s, err2 := file.Readdirnames(-1) 230 if err2 != nil { 231 t.Fatalf("readdirnames %q failed: %v", dir, err2) 232 } 233 for _, m := range contents { 234 found := false 235 for _, n := range s { 236 if n == "." || n == ".." { 237 t.Errorf("got %s in directory", n) 238 } 239 if equal(m, n) { 240 if found { 241 t.Error("present twice:", m) 242 } 243 found = true 244 } 245 } 246 if !found { 247 t.Error("could not find", m) 248 } 249 } 250 } 251 252 func testReaddir(dir string, contents []string, t *testing.T) { 253 file, err := Open(dir) 254 if err != nil { 255 t.Fatalf("open %q failed: %v", dir, err) 256 } 257 defer file.Close() 258 s, err2 := file.Readdir(-1) 259 if err2 != nil { 260 t.Fatalf("readdir %q failed: %v", dir, err2) 261 } 262 for _, m := range contents { 263 found := false 264 for _, n := range s { 265 if equal(m, n.Name()) { 266 if found { 267 t.Error("present twice:", m) 268 } 269 found = true 270 } 271 } 272 if !found { 273 t.Error("could not find", m) 274 } 275 } 276 } 277 278 func TestReaddirnames(t *testing.T) { 279 testReaddirnames(".", dot, t) 280 testReaddirnames(sysdir.name, sysdir.files, t) 281 } 282 283 func TestReaddir(t *testing.T) { 284 testReaddir(".", dot, t) 285 testReaddir(sysdir.name, sysdir.files, t) 286 } 287 288 // Read the directory one entry at a time. 289 func smallReaddirnames(file *File, length int, t *testing.T) []string { 290 names := make([]string, length) 291 count := 0 292 for { 293 d, err := file.Readdirnames(1) 294 if err == io.EOF { 295 break 296 } 297 if err != nil { 298 t.Fatalf("readdirnames %q failed: %v", file.Name(), err) 299 } 300 if len(d) == 0 { 301 t.Fatalf("readdirnames %q returned empty slice and no error", file.Name()) 302 } 303 names[count] = d[0] 304 count++ 305 } 306 return names[0:count] 307 } 308 309 // Check that reading a directory one entry at a time gives the same result 310 // as reading it all at once. 311 func TestReaddirnamesOneAtATime(t *testing.T) { 312 // big directory that doesn't change often. 313 dir := "/usr/bin" 314 switch runtime.GOOS { 315 case "android": 316 dir = "/system/bin" 317 case "plan9": 318 case "akaros": 319 dir = "/bin" 320 case "windows": 321 dir = Getenv("SystemRoot") + "\\system32" 322 } 323 file, err := Open(dir) 324 if err != nil { 325 t.Fatalf("open %q failed: %v", dir, err) 326 } 327 defer file.Close() 328 all, err1 := file.Readdirnames(-1) 329 if err1 != nil { 330 t.Fatalf("readdirnames %q failed: %v", dir, err1) 331 } 332 file1, err2 := Open(dir) 333 if err2 != nil { 334 t.Fatalf("open %q failed: %v", dir, err2) 335 } 336 defer file1.Close() 337 small := smallReaddirnames(file1, len(all)+100, t) // +100 in case we screw up 338 if len(small) < len(all) { 339 t.Fatalf("len(small) is %d, less than %d", len(small), len(all)) 340 } 341 for i, n := range all { 342 if small[i] != n { 343 t.Errorf("small read %q mismatch: %v", small[i], n) 344 } 345 } 346 } 347 348 func TestReaddirNValues(t *testing.T) { 349 if testing.Short() { 350 t.Skip("test.short; skipping") 351 } 352 dir, err := ioutil.TempDir("", "") 353 if err != nil { 354 t.Fatalf("TempDir: %v", err) 355 } 356 defer RemoveAll(dir) 357 for i := 1; i <= 105; i++ { 358 f, err := Create(filepath.Join(dir, fmt.Sprintf("%d", i))) 359 if err != nil { 360 t.Fatalf("Create: %v", err) 361 } 362 f.Write([]byte(strings.Repeat("X", i))) 363 f.Close() 364 } 365 366 var d *File 367 openDir := func() { 368 var err error 369 d, err = Open(dir) 370 if err != nil { 371 t.Fatalf("Open directory: %v", err) 372 } 373 } 374 375 readDirExpect := func(n, want int, wantErr error) { 376 fi, err := d.Readdir(n) 377 if err != wantErr { 378 t.Fatalf("Readdir of %d got error %v, want %v", n, err, wantErr) 379 } 380 if g, e := len(fi), want; g != e { 381 t.Errorf("Readdir of %d got %d files, want %d", n, g, e) 382 } 383 } 384 385 readDirNamesExpect := func(n, want int, wantErr error) { 386 fi, err := d.Readdirnames(n) 387 if err != wantErr { 388 t.Fatalf("Readdirnames of %d got error %v, want %v", n, err, wantErr) 389 } 390 if g, e := len(fi), want; g != e { 391 t.Errorf("Readdirnames of %d got %d files, want %d", n, g, e) 392 } 393 } 394 395 for _, fn := range []func(int, int, error){readDirExpect, readDirNamesExpect} { 396 // Test the slurp case 397 openDir() 398 fn(0, 105, nil) 399 fn(0, 0, nil) 400 d.Close() 401 402 // Slurp with -1 instead 403 openDir() 404 fn(-1, 105, nil) 405 fn(-2, 0, nil) 406 fn(0, 0, nil) 407 d.Close() 408 409 // Test the bounded case 410 openDir() 411 fn(1, 1, nil) 412 fn(2, 2, nil) 413 fn(105, 102, nil) // and tests buffer >100 case 414 fn(3, 0, io.EOF) 415 d.Close() 416 } 417 } 418 419 func touch(t *testing.T, name string) { 420 f, err := Create(name) 421 if err != nil { 422 t.Fatal(err) 423 } 424 if err := f.Close(); err != nil { 425 t.Fatal(err) 426 } 427 } 428 429 func TestReaddirStatFailures(t *testing.T) { 430 switch runtime.GOOS { 431 case "windows", "plan9": 432 // Windows and Plan 9 already do this correctly, 433 // but are structured with different syscalls such 434 // that they don't use Lstat, so the hook below for 435 // testing it wouldn't work. 436 t.Skipf("skipping test on %v", runtime.GOOS) 437 } 438 dir, err := ioutil.TempDir("", "") 439 if err != nil { 440 t.Fatalf("TempDir: %v", err) 441 } 442 defer RemoveAll(dir) 443 touch(t, filepath.Join(dir, "good1")) 444 touch(t, filepath.Join(dir, "x")) // will disappear or have an error 445 touch(t, filepath.Join(dir, "good2")) 446 defer func() { 447 *LstatP = Lstat 448 }() 449 var xerr error // error to return for x 450 *LstatP = func(path string) (FileInfo, error) { 451 if xerr != nil && strings.HasSuffix(path, "x") { 452 return nil, xerr 453 } 454 return Lstat(path) 455 } 456 readDir := func() ([]FileInfo, error) { 457 d, err := Open(dir) 458 if err != nil { 459 t.Fatal(err) 460 } 461 defer d.Close() 462 return d.Readdir(-1) 463 } 464 mustReadDir := func(testName string) []FileInfo { 465 fis, err := readDir() 466 if err != nil { 467 t.Fatalf("%s: Readdir: %v", testName, err) 468 } 469 return fis 470 } 471 names := func(fis []FileInfo) []string { 472 s := make([]string, len(fis)) 473 for i, fi := range fis { 474 s[i] = fi.Name() 475 } 476 sort.Strings(s) 477 return s 478 } 479 480 if got, want := names(mustReadDir("inital readdir")), 481 []string{"good1", "good2", "x"}; !reflect.DeepEqual(got, want) { 482 t.Errorf("initial readdir got %q; want %q", got, want) 483 } 484 485 xerr = ErrNotExist 486 if got, want := names(mustReadDir("with x disappearing")), 487 []string{"good1", "good2"}; !reflect.DeepEqual(got, want) { 488 t.Errorf("with x disappearing, got %q; want %q", got, want) 489 } 490 491 xerr = errors.New("some real error") 492 if _, err := readDir(); err != xerr { 493 t.Errorf("with a non-ErrNotExist error, got error %v; want %v", err, xerr) 494 } 495 } 496 497 func TestHardLink(t *testing.T) { 498 // Hardlinks are not supported under Plan 9 or akaros. 499 if runtime.GOOS == "plan9" || runtime.GOOS == "akaros" { 500 return 501 } 502 from, to := "hardlinktestfrom", "hardlinktestto" 503 Remove(from) // Just in case. 504 file, err := Create(to) 505 if err != nil { 506 t.Fatalf("open %q failed: %v", to, err) 507 } 508 defer Remove(to) 509 if err = file.Close(); err != nil { 510 t.Errorf("close %q failed: %v", to, err) 511 } 512 err = Link(to, from) 513 if err != nil { 514 t.Fatalf("link %q, %q failed: %v", to, from, err) 515 } 516 defer Remove(from) 517 tostat, err := Stat(to) 518 if err != nil { 519 t.Fatalf("stat %q failed: %v", to, err) 520 } 521 fromstat, err := Stat(from) 522 if err != nil { 523 t.Fatalf("stat %q failed: %v", from, err) 524 } 525 if !SameFile(tostat, fromstat) { 526 t.Errorf("link %q, %q did not create hard link", to, from) 527 } 528 } 529 530 func TestSymlink(t *testing.T) { 531 switch runtime.GOOS { 532 case "android", "nacl", "plan9", "akaros": 533 t.Skipf("skipping on %s", runtime.GOOS) 534 case "windows": 535 if !supportsSymlinks { 536 t.Skipf("skipping on %s", runtime.GOOS) 537 } 538 } 539 from, to := "symlinktestfrom", "symlinktestto" 540 Remove(from) // Just in case. 541 file, err := Create(to) 542 if err != nil { 543 t.Fatalf("open %q failed: %v", to, err) 544 } 545 defer Remove(to) 546 if err = file.Close(); err != nil { 547 t.Errorf("close %q failed: %v", to, err) 548 } 549 err = Symlink(to, from) 550 if err != nil { 551 t.Fatalf("symlink %q, %q failed: %v", to, from, err) 552 } 553 defer Remove(from) 554 tostat, err := Lstat(to) 555 if err != nil { 556 t.Fatalf("stat %q failed: %v", to, err) 557 } 558 if tostat.Mode()&ModeSymlink != 0 { 559 t.Fatalf("stat %q claims to have found a symlink", to) 560 } 561 fromstat, err := Stat(from) 562 if err != nil { 563 t.Fatalf("stat %q failed: %v", from, err) 564 } 565 if !SameFile(tostat, fromstat) { 566 t.Errorf("symlink %q, %q did not create symlink", to, from) 567 } 568 fromstat, err = Lstat(from) 569 if err != nil { 570 t.Fatalf("lstat %q failed: %v", from, err) 571 } 572 if fromstat.Mode()&ModeSymlink == 0 { 573 t.Fatalf("symlink %q, %q did not create symlink", to, from) 574 } 575 fromstat, err = Stat(from) 576 if err != nil { 577 t.Fatalf("stat %q failed: %v", from, err) 578 } 579 if fromstat.Mode()&ModeSymlink != 0 { 580 t.Fatalf("stat %q did not follow symlink", from) 581 } 582 s, err := Readlink(from) 583 if err != nil { 584 t.Fatalf("readlink %q failed: %v", from, err) 585 } 586 if s != to { 587 t.Fatalf("after symlink %q != %q", s, to) 588 } 589 file, err = Open(from) 590 if err != nil { 591 t.Fatalf("open %q failed: %v", from, err) 592 } 593 file.Close() 594 } 595 596 func TestLongSymlink(t *testing.T) { 597 switch runtime.GOOS { 598 case "plan9", "nacl", "akaros": 599 t.Skipf("skipping on %s", runtime.GOOS) 600 case "windows": 601 if !supportsSymlinks { 602 t.Skipf("skipping on %s", runtime.GOOS) 603 } 604 } 605 s := "0123456789abcdef" 606 // Long, but not too long: a common limit is 255. 607 s = s + s + s + s + s + s + s + s + s + s + s + s + s + s + s 608 from := "longsymlinktestfrom" 609 Remove(from) // Just in case. 610 err := Symlink(s, from) 611 if err != nil { 612 t.Fatalf("symlink %q, %q failed: %v", s, from, err) 613 } 614 defer Remove(from) 615 r, err := Readlink(from) 616 if err != nil { 617 t.Fatalf("readlink %q failed: %v", from, err) 618 } 619 if r != s { 620 t.Fatalf("after symlink %q != %q", r, s) 621 } 622 } 623 624 func TestRename(t *testing.T) { 625 from, to := "renamefrom", "renameto" 626 Remove(to) // Just in case. 627 file, err := Create(from) 628 if err != nil { 629 t.Fatalf("open %q failed: %v", to, err) 630 } 631 if err = file.Close(); err != nil { 632 t.Errorf("close %q failed: %v", to, err) 633 } 634 err = Rename(from, to) 635 if err != nil { 636 t.Fatalf("rename %q, %q failed: %v", to, from, err) 637 } 638 defer Remove(to) 639 _, err = Stat(to) 640 if err != nil { 641 t.Errorf("stat %q failed: %v", to, err) 642 } 643 } 644 645 func exec(t *testing.T, dir, cmd string, args []string, expect string) { 646 r, w, err := Pipe() 647 if err != nil { 648 t.Fatalf("Pipe: %v", err) 649 } 650 defer r.Close() 651 attr := &ProcAttr{Dir: dir, Files: []*File{nil, w, Stderr}} 652 p, err := StartProcess(cmd, args, attr) 653 if err != nil { 654 t.Fatalf("StartProcess: %v", err) 655 } 656 w.Close() 657 658 var b bytes.Buffer 659 io.Copy(&b, r) 660 output := b.String() 661 662 fi1, _ := Stat(strings.TrimSpace(output)) 663 fi2, _ := Stat(expect) 664 if !SameFile(fi1, fi2) { 665 t.Errorf("exec %q returned %q wanted %q", 666 strings.Join(append([]string{cmd}, args...), " "), output, expect) 667 } 668 p.Wait() 669 } 670 671 func TestStartProcess(t *testing.T) { 672 switch runtime.GOOS { 673 case "android", "nacl": 674 t.Skipf("skipping on %s", runtime.GOOS) 675 } 676 677 var dir, cmd string 678 var args []string 679 if runtime.GOOS == "windows" { 680 cmd = Getenv("COMSPEC") 681 dir = Getenv("SystemRoot") 682 args = []string{"/c", "cd"} 683 } else { 684 cmd = "/bin/pwd" 685 dir = "/" 686 args = []string{} 687 } 688 cmddir, cmdbase := filepath.Split(cmd) 689 args = append([]string{cmdbase}, args...) 690 // Test absolute executable path. 691 exec(t, dir, cmd, args, dir) 692 // Test relative executable path. 693 exec(t, cmddir, cmdbase, args, cmddir) 694 } 695 696 func checkMode(t *testing.T, path string, mode FileMode) { 697 dir, err := Stat(path) 698 if err != nil { 699 t.Fatalf("Stat %q (looking for mode %#o): %s", path, mode, err) 700 } 701 if dir.Mode()&0777 != mode { 702 t.Errorf("Stat %q: mode %#o want %#o", path, dir.Mode(), mode) 703 } 704 } 705 706 func TestChmod(t *testing.T) { 707 // Chmod is not supported under windows. 708 if runtime.GOOS == "windows" { 709 return 710 } 711 f := newFile("TestChmod", t) 712 defer Remove(f.Name()) 713 defer f.Close() 714 715 if err := Chmod(f.Name(), 0456); err != nil { 716 t.Fatalf("chmod %s 0456: %s", f.Name(), err) 717 } 718 checkMode(t, f.Name(), 0456) 719 720 if err := f.Chmod(0123); err != nil { 721 t.Fatalf("chmod %s 0123: %s", f.Name(), err) 722 } 723 checkMode(t, f.Name(), 0123) 724 } 725 726 func checkSize(t *testing.T, f *File, size int64) { 727 dir, err := f.Stat() 728 if err != nil { 729 t.Fatalf("Stat %q (looking for size %d): %s", f.Name(), size, err) 730 } 731 if dir.Size() != size { 732 t.Errorf("Stat %q: size %d want %d", f.Name(), dir.Size(), size) 733 } 734 } 735 736 func TestFTruncate(t *testing.T) { 737 f := newFile("TestFTruncate", t) 738 defer Remove(f.Name()) 739 defer f.Close() 740 741 checkSize(t, f, 0) 742 f.Write([]byte("hello, world\n")) 743 checkSize(t, f, 13) 744 f.Truncate(10) 745 checkSize(t, f, 10) 746 f.Truncate(1024) 747 checkSize(t, f, 1024) 748 f.Truncate(0) 749 checkSize(t, f, 0) 750 _, err := f.Write([]byte("surprise!")) 751 if err == nil { 752 checkSize(t, f, 13+9) // wrote at offset past where hello, world was. 753 } 754 } 755 756 func TestTruncate(t *testing.T) { 757 f := newFile("TestTruncate", t) 758 defer Remove(f.Name()) 759 defer f.Close() 760 761 checkSize(t, f, 0) 762 f.Write([]byte("hello, world\n")) 763 checkSize(t, f, 13) 764 Truncate(f.Name(), 10) 765 checkSize(t, f, 10) 766 Truncate(f.Name(), 1024) 767 checkSize(t, f, 1024) 768 Truncate(f.Name(), 0) 769 checkSize(t, f, 0) 770 _, err := f.Write([]byte("surprise!")) 771 if err == nil { 772 checkSize(t, f, 13+9) // wrote at offset past where hello, world was. 773 } 774 } 775 776 // Use TempDir (via newFile) to make sure we're on a local file system, 777 // so that timings are not distorted by latency and caching. 778 // On NFS, timings can be off due to caching of meta-data on 779 // NFS servers (Issue 848). 780 func TestChtimes(t *testing.T) { 781 f := newFile("TestChtimes", t) 782 defer Remove(f.Name()) 783 784 f.Write([]byte("hello, world\n")) 785 f.Close() 786 787 testChtimes(t, f.Name()) 788 } 789 790 // Use TempDir (via newDir) to make sure we're on a local file system, 791 // so that timings are not distorted by latency and caching. 792 // On NFS, timings can be off due to caching of meta-data on 793 // NFS servers (Issue 848). 794 func TestChtimesDir(t *testing.T) { 795 name := newDir("TestChtimes", t) 796 defer RemoveAll(name) 797 798 testChtimes(t, name) 799 } 800 801 func testChtimes(t *testing.T, name string) { 802 st, err := Stat(name) 803 if err != nil { 804 t.Fatalf("Stat %s: %s", name, err) 805 } 806 preStat := st 807 808 // Move access and modification time back a second 809 at := Atime(preStat) 810 mt := preStat.ModTime() 811 err = Chtimes(name, at.Add(-time.Second), mt.Add(-time.Second)) 812 if err != nil { 813 t.Fatalf("Chtimes %s: %s", name, err) 814 } 815 816 st, err = Stat(name) 817 if err != nil { 818 t.Fatalf("second Stat %s: %s", name, err) 819 } 820 postStat := st 821 822 /* Plan 9, NaCl: 823 Mtime is the time of the last change of content. Similarly, atime is set whenever the 824 contents are accessed; also, it is set whenever mtime is set. 825 */ 826 pat := Atime(postStat) 827 pmt := postStat.ModTime() 828 if !pat.Before(at) && runtime.GOOS != "plan9" && runtime.GOOS != "nacl" && runtime.GOOS != "akaros" { 829 t.Errorf("AccessTime didn't go backwards; was=%d, after=%d", at, pat) 830 } 831 832 if !pmt.Before(mt) { 833 t.Errorf("ModTime didn't go backwards; was=%d, after=%d", mt, pmt) 834 } 835 } 836 837 func TestChdirAndGetwd(t *testing.T) { 838 // TODO(brainman): file.Chdir() is not implemented on windows. 839 if runtime.GOOS == "windows" { 840 return 841 } 842 fd, err := Open(".") 843 if err != nil { 844 t.Fatalf("Open .: %s", err) 845 } 846 // These are chosen carefully not to be symlinks on a Mac 847 // (unlike, say, /var, /etc), except /tmp, which we handle below. 848 dirs := []string{"/", "/usr/bin", "/tmp"} 849 // /usr/bin does not usually exist on Plan 9 or Android. 850 switch runtime.GOOS { 851 case "android": 852 dirs = []string{"/", "/system/bin"} 853 case "plan9", "akaros": 854 dirs = []string{"/", "/usr"} 855 } 856 oldwd := Getenv("PWD") 857 for mode := 0; mode < 2; mode++ { 858 for _, d := range dirs { 859 if mode == 0 { 860 err = Chdir(d) 861 } else { 862 fd1, err := Open(d) 863 if err != nil { 864 t.Errorf("Open %s: %s", d, err) 865 continue 866 } 867 err = fd1.Chdir() 868 fd1.Close() 869 } 870 if d == "/tmp" { 871 Setenv("PWD", "/tmp") 872 } 873 pwd, err1 := Getwd() 874 Setenv("PWD", oldwd) 875 err2 := fd.Chdir() 876 if err2 != nil { 877 // We changed the current directory and cannot go back. 878 // Don't let the tests continue; they'll scribble 879 // all over some other directory. 880 fmt.Fprintf(Stderr, "fchdir back to dot failed: %s\n", err2) 881 Exit(1) 882 } 883 if err != nil { 884 fd.Close() 885 t.Fatalf("Chdir %s: %s", d, err) 886 } 887 if err1 != nil { 888 fd.Close() 889 t.Fatalf("Getwd in %s: %s", d, err1) 890 } 891 if pwd != d { 892 fd.Close() 893 t.Fatalf("Getwd returned %q want %q", pwd, d) 894 } 895 } 896 } 897 fd.Close() 898 } 899 900 func TestSeek(t *testing.T) { 901 f := newFile("TestSeek", t) 902 defer Remove(f.Name()) 903 defer f.Close() 904 905 const data = "hello, world\n" 906 io.WriteString(f, data) 907 908 type test struct { 909 in int64 910 whence int 911 out int64 912 } 913 var tests = []test{ 914 {0, 1, int64(len(data))}, 915 {0, 0, 0}, 916 {5, 0, 5}, 917 {0, 2, int64(len(data))}, 918 {0, 0, 0}, 919 {-1, 2, int64(len(data)) - 1}, 920 {1 << 33, 0, 1 << 33}, 921 {1 << 33, 2, 1<<33 + int64(len(data))}, 922 } 923 for i, tt := range tests { 924 off, err := f.Seek(tt.in, tt.whence) 925 if off != tt.out || err != nil { 926 if e, ok := err.(*PathError); ok && e.Err == syscall.EINVAL && tt.out > 1<<32 { 927 // Reiserfs rejects the big seeks. 928 // http://code.google.com/p/go/issues/detail?id=91 929 break 930 } 931 t.Errorf("#%d: Seek(%v, %v) = %v, %v want %v, nil", i, tt.in, tt.whence, off, err, tt.out) 932 } 933 } 934 } 935 936 type openErrorTest struct { 937 path string 938 mode int 939 error error 940 } 941 942 var openErrorTests = []openErrorTest{ 943 { 944 sfdir + "/no-such-file", 945 O_RDONLY, 946 syscall.ENOENT, 947 }, 948 { 949 sfdir, 950 O_WRONLY, 951 syscall.EISDIR, 952 }, 953 { 954 sfdir + "/" + sfname + "/no-such-file", 955 O_WRONLY, 956 syscall.ENOTDIR, 957 }, 958 } 959 960 func TestOpenError(t *testing.T) { 961 for _, tt := range openErrorTests { 962 f, err := OpenFile(tt.path, tt.mode, 0) 963 if err == nil { 964 t.Errorf("Open(%q, %d) succeeded", tt.path, tt.mode) 965 f.Close() 966 continue 967 } 968 perr, ok := err.(*PathError) 969 if !ok { 970 t.Errorf("Open(%q, %d) returns error of %T type; want *PathError", tt.path, tt.mode, err) 971 } 972 if perr.Err != tt.error { 973 if runtime.GOOS == "akaros" { 974 // On akaros, the actual errno is embedded in an AkaError 975 if perr.Err.(*syscall.AkaError).Errno() == tt.error { 976 continue 977 } 978 } 979 if runtime.GOOS == "plan9" { 980 syscallErrStr := perr.Err.Error() 981 expectedErrStr := strings.Replace(tt.error.Error(), "file ", "", 1) 982 if !strings.HasSuffix(syscallErrStr, expectedErrStr) { 983 // Some Plan 9 file servers incorrectly return 984 // EACCES rather than EISDIR when a directory is 985 // opened for write. 986 if tt.error == syscall.EISDIR && strings.HasSuffix(syscallErrStr, syscall.EACCES.Error()) { 987 continue 988 } 989 t.Errorf("Open(%q, %d) = _, %q; want suffix %q", tt.path, tt.mode, syscallErrStr, expectedErrStr) 990 } 991 continue 992 } 993 if runtime.GOOS == "dragonfly" { 994 // DragonFly incorrectly returns EACCES rather 995 // EISDIR when a directory is opened for write. 996 if tt.error == syscall.EISDIR && perr.Err == syscall.EACCES { 997 continue 998 } 999 } 1000 t.Errorf("Open(%q, %d) = _, %q; want %q", tt.path, tt.mode, perr.Err.Error(), tt.error.Error()) 1001 } 1002 } 1003 } 1004 1005 func TestOpenNoName(t *testing.T) { 1006 f, err := Open("") 1007 if err == nil { 1008 t.Fatal(`Open("") succeeded`) 1009 f.Close() 1010 } 1011 } 1012 1013 func run(t *testing.T, cmd []string) string { 1014 // Run /bin/hostname and collect output. 1015 r, w, err := Pipe() 1016 if err != nil { 1017 t.Fatal(err) 1018 } 1019 defer r.Close() 1020 p, err := StartProcess("/bin/hostname", []string{"hostname"}, &ProcAttr{Files: []*File{nil, w, Stderr}}) 1021 if err != nil { 1022 t.Fatal(err) 1023 } 1024 w.Close() 1025 1026 var b bytes.Buffer 1027 io.Copy(&b, r) 1028 _, err = p.Wait() 1029 if err != nil { 1030 t.Fatalf("run hostname Wait: %v", err) 1031 } 1032 err = p.Kill() 1033 if err == nil { 1034 t.Errorf("expected an error from Kill running 'hostname'") 1035 } 1036 output := b.String() 1037 if n := len(output); n > 0 && output[n-1] == '\n' { 1038 output = output[0 : n-1] 1039 } 1040 if output == "" { 1041 t.Fatalf("%v produced no output", cmd) 1042 } 1043 1044 return output 1045 } 1046 1047 func TestHostname(t *testing.T) { 1048 // There is no other way to fetch hostname on windows, but via winapi. 1049 // On Plan 9 it can be taken from #c/sysname as Hostname() does. 1050 switch runtime.GOOS { 1051 case "android", "nacl", "plan9", "windows", "akaros": 1052 t.Skipf("skipping on %s", runtime.GOOS) 1053 } 1054 1055 // Check internal Hostname() against the output of /bin/hostname. 1056 // Allow that the internal Hostname returns a Fully Qualified Domain Name 1057 // and the /bin/hostname only returns the first component 1058 hostname, err := Hostname() 1059 if err != nil { 1060 t.Fatalf("%v", err) 1061 } 1062 want := run(t, []string{"/bin/hostname"}) 1063 if hostname != want { 1064 i := strings.Index(hostname, ".") 1065 if i < 0 || hostname[0:i] != want { 1066 t.Errorf("Hostname() = %q, want %q", hostname, want) 1067 } 1068 } 1069 } 1070 1071 func TestReadAt(t *testing.T) { 1072 f := newFile("TestReadAt", t) 1073 defer Remove(f.Name()) 1074 defer f.Close() 1075 1076 const data = "hello, world\n" 1077 io.WriteString(f, data) 1078 1079 b := make([]byte, 5) 1080 n, err := f.ReadAt(b, 7) 1081 if err != nil || n != len(b) { 1082 t.Fatalf("ReadAt 7: %d, %v", n, err) 1083 } 1084 if string(b) != "world" { 1085 t.Fatalf("ReadAt 7: have %q want %q", string(b), "world") 1086 } 1087 } 1088 1089 func TestWriteAt(t *testing.T) { 1090 f := newFile("TestWriteAt", t) 1091 defer Remove(f.Name()) 1092 defer f.Close() 1093 1094 const data = "hello, world\n" 1095 io.WriteString(f, data) 1096 1097 n, err := f.WriteAt([]byte("WORLD"), 7) 1098 if err != nil || n != 5 { 1099 t.Fatalf("WriteAt 7: %d, %v", n, err) 1100 } 1101 1102 b, err := ioutil.ReadFile(f.Name()) 1103 if err != nil { 1104 t.Fatalf("ReadFile %s: %v", f.Name(), err) 1105 } 1106 if string(b) != "hello, WORLD\n" { 1107 t.Fatalf("after write: have %q want %q", string(b), "hello, WORLD\n") 1108 } 1109 } 1110 1111 func writeFile(t *testing.T, fname string, flag int, text string) string { 1112 f, err := OpenFile(fname, flag, 0666) 1113 if err != nil { 1114 t.Fatalf("Open: %v", err) 1115 } 1116 n, err := io.WriteString(f, text) 1117 if err != nil { 1118 t.Fatalf("WriteString: %d, %v", n, err) 1119 } 1120 f.Close() 1121 data, err := ioutil.ReadFile(fname) 1122 if err != nil { 1123 t.Fatalf("ReadFile: %v", err) 1124 } 1125 return string(data) 1126 } 1127 1128 func TestAppend(t *testing.T) { 1129 const f = "append.txt" 1130 defer Remove(f) 1131 s := writeFile(t, f, O_CREATE|O_TRUNC|O_RDWR, "new") 1132 if s != "new" { 1133 t.Fatalf("writeFile: have %q want %q", s, "new") 1134 } 1135 s = writeFile(t, f, O_APPEND|O_RDWR, "|append") 1136 if s != "new|append" { 1137 t.Fatalf("writeFile: have %q want %q", s, "new|append") 1138 } 1139 s = writeFile(t, f, O_CREATE|O_APPEND|O_RDWR, "|append") 1140 if s != "new|append|append" { 1141 t.Fatalf("writeFile: have %q want %q", s, "new|append|append") 1142 } 1143 err := Remove(f) 1144 if err != nil { 1145 t.Fatalf("Remove: %v", err) 1146 } 1147 s = writeFile(t, f, O_CREATE|O_APPEND|O_RDWR, "new&append") 1148 if s != "new&append" { 1149 t.Fatalf("writeFile: after append have %q want %q", s, "new&append") 1150 } 1151 s = writeFile(t, f, O_CREATE|O_RDWR, "old") 1152 if s != "old&append" { 1153 t.Fatalf("writeFile: after create have %q want %q", s, "old&append") 1154 } 1155 s = writeFile(t, f, O_CREATE|O_TRUNC|O_RDWR, "new") 1156 if s != "new" { 1157 t.Fatalf("writeFile: after truncate have %q want %q", s, "new") 1158 } 1159 } 1160 1161 func TestStatDirWithTrailingSlash(t *testing.T) { 1162 // Create new temporary directory and arrange to clean it up. 1163 path, err := ioutil.TempDir("", "/_TestStatDirWithSlash_") 1164 if err != nil { 1165 t.Fatalf("TempDir: %s", err) 1166 } 1167 defer RemoveAll(path) 1168 1169 // Stat of path should succeed. 1170 _, err = Stat(path) 1171 if err != nil { 1172 t.Fatalf("stat %s failed: %s", path, err) 1173 } 1174 1175 // Stat of path+"/" should succeed too. 1176 path += "/" 1177 _, err = Stat(path) 1178 if err != nil { 1179 t.Fatalf("stat %s failed: %s", path, err) 1180 } 1181 } 1182 1183 func TestNilProcessStateString(t *testing.T) { 1184 var ps *ProcessState 1185 s := ps.String() 1186 if s != "<nil>" { 1187 t.Errorf("(*ProcessState)(nil).String() = %q, want %q", s, "<nil>") 1188 } 1189 } 1190 1191 func TestSameFile(t *testing.T) { 1192 fa, err := Create("a") 1193 if err != nil { 1194 t.Fatalf("Create(a): %v", err) 1195 } 1196 defer Remove(fa.Name()) 1197 fa.Close() 1198 fb, err := Create("b") 1199 if err != nil { 1200 t.Fatalf("Create(b): %v", err) 1201 } 1202 defer Remove(fb.Name()) 1203 fb.Close() 1204 1205 ia1, err := Stat("a") 1206 if err != nil { 1207 t.Fatalf("Stat(a): %v", err) 1208 } 1209 ia2, err := Stat("a") 1210 if err != nil { 1211 t.Fatalf("Stat(a): %v", err) 1212 } 1213 if !SameFile(ia1, ia2) { 1214 t.Errorf("files should be same") 1215 } 1216 1217 ib, err := Stat("b") 1218 if err != nil { 1219 t.Fatalf("Stat(b): %v", err) 1220 } 1221 if SameFile(ia1, ib) { 1222 t.Errorf("files should be different") 1223 } 1224 } 1225 1226 func TestDevNullFile(t *testing.T) { 1227 f, err := Open(DevNull) 1228 if err != nil { 1229 t.Fatalf("Open(%s): %v", DevNull, err) 1230 } 1231 defer f.Close() 1232 fi, err := f.Stat() 1233 if err != nil { 1234 t.Fatalf("Stat(%s): %v", DevNull, err) 1235 } 1236 name := filepath.Base(DevNull) 1237 if fi.Name() != name { 1238 t.Fatalf("wrong file name have %v want %v", fi.Name(), name) 1239 } 1240 if fi.Size() != 0 { 1241 t.Fatalf("wrong file size have %d want 0", fi.Size()) 1242 } 1243 } 1244 1245 var testLargeWrite = flag.Bool("large_write", false, "run TestLargeWriteToConsole test that floods console with output") 1246 1247 func TestLargeWriteToConsole(t *testing.T) { 1248 if !*testLargeWrite { 1249 t.Skip("skipping console-flooding test; enable with -large_write") 1250 } 1251 b := make([]byte, 32000) 1252 for i := range b { 1253 b[i] = '.' 1254 } 1255 b[len(b)-1] = '\n' 1256 n, err := Stdout.Write(b) 1257 if err != nil { 1258 t.Fatalf("Write to os.Stdout failed: %v", err) 1259 } 1260 if n != len(b) { 1261 t.Errorf("Write to os.Stdout should return %d; got %d", len(b), n) 1262 } 1263 n, err = Stderr.Write(b) 1264 if err != nil { 1265 t.Fatalf("Write to os.Stderr failed: %v", err) 1266 } 1267 if n != len(b) { 1268 t.Errorf("Write to os.Stderr should return %d; got %d", len(b), n) 1269 } 1270 } 1271 1272 func TestStatDirModeExec(t *testing.T) { 1273 const mode = 0111 1274 1275 path, err := ioutil.TempDir("", "go-build") 1276 if err != nil { 1277 t.Fatalf("Failed to create temp directory: %v", err) 1278 } 1279 defer RemoveAll(path) 1280 1281 if err := Chmod(path, 0777); err != nil { 1282 t.Fatalf("Chmod %q 0777: %v", path, err) 1283 } 1284 1285 dir, err := Stat(path) 1286 if err != nil { 1287 t.Fatalf("Stat %q (looking for mode %#o): %s", path, mode, err) 1288 } 1289 if dir.Mode()&mode != mode { 1290 t.Errorf("Stat %q: mode %#o want %#o", path, dir.Mode()&mode, mode) 1291 } 1292 } 1293 1294 func TestReadAtEOF(t *testing.T) { 1295 f := newFile("TestReadAtEOF", t) 1296 defer Remove(f.Name()) 1297 defer f.Close() 1298 1299 _, err := f.ReadAt(make([]byte, 10), 0) 1300 switch err { 1301 case io.EOF: 1302 // all good 1303 case nil: 1304 t.Fatalf("ReadAt succeeded") 1305 default: 1306 t.Fatalf("ReadAt failed: %s", err) 1307 } 1308 } 1309 1310 func testKillProcess(t *testing.T, processKiller func(p *Process)) { 1311 switch runtime.GOOS { 1312 case "android", "nacl": 1313 t.Skipf("skipping on %s", runtime.GOOS) 1314 } 1315 if runtime.GOOS == "akaros" { 1316 t.Skip("skipping on akaros") 1317 } 1318 1319 dir, err := ioutil.TempDir("", "go-build") 1320 if err != nil { 1321 t.Fatalf("Failed to create temp directory: %v", err) 1322 } 1323 defer RemoveAll(dir) 1324 1325 src := filepath.Join(dir, "main.go") 1326 f, err := Create(src) 1327 if err != nil { 1328 t.Fatalf("Failed to create %v: %v", src, err) 1329 } 1330 st := template.Must(template.New("source").Parse(` 1331 package main 1332 import "time" 1333 func main() { 1334 time.Sleep(time.Second) 1335 } 1336 `)) 1337 err = st.Execute(f, nil) 1338 if err != nil { 1339 f.Close() 1340 t.Fatalf("Failed to execute template: %v", err) 1341 } 1342 f.Close() 1343 1344 exe := filepath.Join(dir, "main.exe") 1345 output, err := osexec.Command("go", "build", "-o", exe, src).CombinedOutput() 1346 if err != nil { 1347 t.Fatalf("Failed to build exe %v: %v %v", exe, err, string(output)) 1348 } 1349 1350 cmd := osexec.Command(exe) 1351 err = cmd.Start() 1352 if err != nil { 1353 t.Fatalf("Failed to start test process: %v", err) 1354 } 1355 go func() { 1356 time.Sleep(100 * time.Millisecond) 1357 processKiller(cmd.Process) 1358 }() 1359 err = cmd.Wait() 1360 if err == nil { 1361 t.Errorf("Test process succeeded, but expected to fail") 1362 } 1363 } 1364 1365 func TestKillStartProcess(t *testing.T) { 1366 testKillProcess(t, func(p *Process) { 1367 err := p.Kill() 1368 if err != nil { 1369 t.Fatalf("Failed to kill test process: %v", err) 1370 } 1371 }) 1372 } 1373 1374 func TestGetppid(t *testing.T) { 1375 switch runtime.GOOS { 1376 case "akaros": 1377 t.Skip("skipping on akaros") 1378 case "nacl": 1379 t.Skip("skipping on nacl") 1380 case "plan9": 1381 // TODO: golang.org/issue/8206 1382 t.Skipf("skipping test on plan9; see issue 8206") 1383 } 1384 1385 if Getenv("GO_WANT_HELPER_PROCESS") == "1" { 1386 fmt.Print(Getppid()) 1387 Exit(0) 1388 } 1389 1390 cmd := osexec.Command(Args[0], "-test.run=TestGetppid") 1391 cmd.Env = append(Environ(), "GO_WANT_HELPER_PROCESS=1") 1392 1393 // verify that Getppid() from the forked process reports our process id 1394 output, err := cmd.CombinedOutput() 1395 if err != nil { 1396 t.Fatalf("Failed to spawn child process: %v %q", err, string(output)) 1397 } 1398 1399 childPpid := string(output) 1400 ourPid := fmt.Sprintf("%d", Getpid()) 1401 if childPpid != ourPid { 1402 t.Fatalf("Child process reports parent process id '%v', expected '%v'", childPpid, ourPid) 1403 } 1404 } 1405 1406 func TestKillFindProcess(t *testing.T) { 1407 testKillProcess(t, func(p *Process) { 1408 p2, err := FindProcess(p.Pid) 1409 if err != nil { 1410 t.Fatalf("Failed to find test process: %v", err) 1411 } 1412 err = p2.Kill() 1413 if err != nil { 1414 t.Fatalf("Failed to kill test process: %v", err) 1415 } 1416 }) 1417 } 1418 1419 var nilFileMethodTests = []struct { 1420 name string 1421 f func(*File) error 1422 }{ 1423 {"Chdir", func(f *File) error { return f.Chdir() }}, 1424 {"Close", func(f *File) error { return f.Close() }}, 1425 {"Chmod", func(f *File) error { return f.Chmod(0) }}, 1426 {"Chown", func(f *File) error { return f.Chown(0, 0) }}, 1427 {"Read", func(f *File) error { _, err := f.Read(make([]byte, 0)); return err }}, 1428 {"ReadAt", func(f *File) error { _, err := f.ReadAt(make([]byte, 0), 0); return err }}, 1429 {"Readdir", func(f *File) error { _, err := f.Readdir(1); return err }}, 1430 {"Readdirnames", func(f *File) error { _, err := f.Readdirnames(1); return err }}, 1431 {"Seek", func(f *File) error { _, err := f.Seek(0, 0); return err }}, 1432 {"Stat", func(f *File) error { _, err := f.Stat(); return err }}, 1433 {"Sync", func(f *File) error { return f.Sync() }}, 1434 {"Truncate", func(f *File) error { return f.Truncate(0) }}, 1435 {"Write", func(f *File) error { _, err := f.Write(make([]byte, 0)); return err }}, 1436 {"WriteAt", func(f *File) error { _, err := f.WriteAt(make([]byte, 0), 0); return err }}, 1437 {"WriteString", func(f *File) error { _, err := f.WriteString(""); return err }}, 1438 } 1439 1440 // Test that all File methods give ErrInvalid if the receiver is nil. 1441 func TestNilFileMethods(t *testing.T) { 1442 for _, tt := range nilFileMethodTests { 1443 var file *File 1444 got := tt.f(file) 1445 if got != ErrInvalid { 1446 t.Errorf("%v should fail when f is nil; got %v", tt.name, got) 1447 } 1448 } 1449 } 1450 1451 func mkdirTree(t *testing.T, root string, level, max int) { 1452 if level >= max { 1453 return 1454 } 1455 level++ 1456 for i := 'a'; i < 'c'; i++ { 1457 dir := filepath.Join(root, string(i)) 1458 if err := Mkdir(dir, 0700); err != nil { 1459 t.Fatal(err) 1460 } 1461 mkdirTree(t, dir, level, max) 1462 } 1463 } 1464 1465 // Test that simultaneous RemoveAll do not report an error. 1466 // As long as it gets removed, we should be happy. 1467 func TestRemoveAllRace(t *testing.T) { 1468 if runtime.GOOS == "windows" { 1469 // Windows has very strict rules about things like 1470 // removing directories while someone else has 1471 // them open. The racing doesn't work out nicely 1472 // like it does on Unix. 1473 t.Skip("skipping on windows") 1474 } 1475 1476 n := runtime.GOMAXPROCS(16) 1477 defer runtime.GOMAXPROCS(n) 1478 root, err := ioutil.TempDir("", "issue") 1479 if err != nil { 1480 t.Fatal(err) 1481 } 1482 mkdirTree(t, root, 1, 6) 1483 hold := make(chan struct{}) 1484 var wg sync.WaitGroup 1485 for i := 0; i < 4; i++ { 1486 wg.Add(1) 1487 go func() { 1488 defer wg.Done() 1489 <-hold 1490 err := RemoveAll(root) 1491 if err != nil { 1492 t.Errorf("unexpected error: %T, %q", err, err) 1493 } 1494 }() 1495 } 1496 close(hold) // let workers race to remove root 1497 wg.Wait() 1498 }