github.com/lianghucheng/zrddz@v0.0.0-20200923083010-c71f680932e2/src/golang.org/x/sys/unix/syscall_unix_test.go (about) 1 // Copyright 2013 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 // +build aix darwin dragonfly freebsd linux netbsd openbsd solaris 6 7 package unix_test 8 9 import ( 10 "flag" 11 "fmt" 12 "io/ioutil" 13 "net" 14 "os" 15 "os/exec" 16 "path/filepath" 17 "runtime" 18 "syscall" 19 "testing" 20 "time" 21 22 "golang.org/x/sys/unix" 23 ) 24 25 // Tests that below functions, structures and constants are consistent 26 // on all Unix-like systems. 27 func _() { 28 // program scheduling priority functions and constants 29 var ( 30 _ func(int, int, int) error = unix.Setpriority 31 _ func(int, int) (int, error) = unix.Getpriority 32 ) 33 const ( 34 _ int = unix.PRIO_USER 35 _ int = unix.PRIO_PROCESS 36 _ int = unix.PRIO_PGRP 37 ) 38 39 // termios constants 40 const ( 41 _ int = unix.TCIFLUSH 42 _ int = unix.TCIOFLUSH 43 _ int = unix.TCOFLUSH 44 ) 45 46 // fcntl file locking structure and constants 47 var ( 48 _ = unix.Flock_t{ 49 Type: int16(0), 50 Whence: int16(0), 51 Start: int64(0), 52 Len: int64(0), 53 Pid: int32(0), 54 } 55 ) 56 const ( 57 _ = unix.F_GETLK 58 _ = unix.F_SETLK 59 _ = unix.F_SETLKW 60 ) 61 } 62 63 func TestErrnoSignalName(t *testing.T) { 64 testErrors := []struct { 65 num syscall.Errno 66 name string 67 }{ 68 {syscall.EPERM, "EPERM"}, 69 {syscall.EINVAL, "EINVAL"}, 70 {syscall.ENOENT, "ENOENT"}, 71 } 72 73 for _, te := range testErrors { 74 t.Run(fmt.Sprintf("%d/%s", te.num, te.name), func(t *testing.T) { 75 e := unix.ErrnoName(te.num) 76 if e != te.name { 77 t.Errorf("ErrnoName(%d) returned %s, want %s", te.num, e, te.name) 78 } 79 }) 80 } 81 82 testSignals := []struct { 83 num syscall.Signal 84 name string 85 }{ 86 {syscall.SIGHUP, "SIGHUP"}, 87 {syscall.SIGPIPE, "SIGPIPE"}, 88 {syscall.SIGSEGV, "SIGSEGV"}, 89 } 90 91 for _, ts := range testSignals { 92 t.Run(fmt.Sprintf("%d/%s", ts.num, ts.name), func(t *testing.T) { 93 s := unix.SignalName(ts.num) 94 if s != ts.name { 95 t.Errorf("SignalName(%d) returned %s, want %s", ts.num, s, ts.name) 96 } 97 }) 98 } 99 } 100 101 func TestSignalNum(t *testing.T) { 102 testSignals := []struct { 103 name string 104 want syscall.Signal 105 }{ 106 {"SIGHUP", syscall.SIGHUP}, 107 {"SIGPIPE", syscall.SIGPIPE}, 108 {"SIGSEGV", syscall.SIGSEGV}, 109 {"NONEXISTS", 0}, 110 } 111 for _, ts := range testSignals { 112 t.Run(fmt.Sprintf("%s/%d", ts.name, ts.want), func(t *testing.T) { 113 got := unix.SignalNum(ts.name) 114 if got != ts.want { 115 t.Errorf("SignalNum(%s) returned %d, want %d", ts.name, got, ts.want) 116 } 117 }) 118 119 } 120 } 121 122 func TestFcntlInt(t *testing.T) { 123 t.Parallel() 124 file, err := ioutil.TempFile("", "TestFnctlInt") 125 if err != nil { 126 t.Fatal(err) 127 } 128 defer os.Remove(file.Name()) 129 defer file.Close() 130 f := file.Fd() 131 flags, err := unix.FcntlInt(f, unix.F_GETFD, 0) 132 if err != nil { 133 t.Fatal(err) 134 } 135 if flags&unix.FD_CLOEXEC == 0 { 136 t.Errorf("flags %#x do not include FD_CLOEXEC", flags) 137 } 138 } 139 140 // TestFcntlFlock tests whether the file locking structure matches 141 // the calling convention of each kernel. 142 func TestFcntlFlock(t *testing.T) { 143 name := filepath.Join(os.TempDir(), "TestFcntlFlock") 144 fd, err := unix.Open(name, unix.O_CREAT|unix.O_RDWR|unix.O_CLOEXEC, 0) 145 if err != nil { 146 t.Fatalf("Open failed: %v", err) 147 } 148 defer unix.Unlink(name) 149 defer unix.Close(fd) 150 flock := unix.Flock_t{ 151 Type: unix.F_RDLCK, 152 Start: 0, Len: 0, Whence: 1, 153 } 154 if err := unix.FcntlFlock(uintptr(fd), unix.F_GETLK, &flock); err != nil { 155 t.Fatalf("FcntlFlock failed: %v", err) 156 } 157 } 158 159 // TestPassFD tests passing a file descriptor over a Unix socket. 160 // 161 // This test involved both a parent and child process. The parent 162 // process is invoked as a normal test, with "go test", which then 163 // runs the child process by running the current test binary with args 164 // "-test.run=^TestPassFD$" and an environment variable used to signal 165 // that the test should become the child process instead. 166 func TestPassFD(t *testing.T) { 167 if runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") { 168 t.Skip("cannot exec subprocess on iOS, skipping test") 169 } 170 if runtime.GOOS == "aix" { 171 t.Skip("getsockname issue on AIX 7.2 tl1, skipping test") 172 } 173 174 if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" { 175 passFDChild() 176 return 177 } 178 179 tempDir, err := ioutil.TempDir("", "TestPassFD") 180 if err != nil { 181 t.Fatal(err) 182 } 183 defer os.RemoveAll(tempDir) 184 185 fds, err := unix.Socketpair(unix.AF_LOCAL, unix.SOCK_STREAM, 0) 186 if err != nil { 187 t.Fatalf("Socketpair: %v", err) 188 } 189 defer unix.Close(fds[0]) 190 defer unix.Close(fds[1]) 191 writeFile := os.NewFile(uintptr(fds[0]), "child-writes") 192 readFile := os.NewFile(uintptr(fds[1]), "parent-reads") 193 defer writeFile.Close() 194 defer readFile.Close() 195 196 cmd := exec.Command(os.Args[0], "-test.run=^TestPassFD$", "--", tempDir) 197 cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"} 198 if lp := os.Getenv("LD_LIBRARY_PATH"); lp != "" { 199 cmd.Env = append(cmd.Env, "LD_LIBRARY_PATH="+lp) 200 } 201 cmd.ExtraFiles = []*os.File{writeFile} 202 203 out, err := cmd.CombinedOutput() 204 if len(out) > 0 || err != nil { 205 t.Fatalf("child process: %q, %v", out, err) 206 } 207 208 c, err := net.FileConn(readFile) 209 if err != nil { 210 t.Fatalf("FileConn: %v", err) 211 } 212 defer c.Close() 213 214 uc, ok := c.(*net.UnixConn) 215 if !ok { 216 t.Fatalf("unexpected FileConn type; expected UnixConn, got %T", c) 217 } 218 219 buf := make([]byte, 32) // expect 1 byte 220 oob := make([]byte, 32) // expect 24 bytes 221 closeUnix := time.AfterFunc(5*time.Second, func() { 222 t.Logf("timeout reading from unix socket") 223 uc.Close() 224 }) 225 _, oobn, _, _, err := uc.ReadMsgUnix(buf, oob) 226 if err != nil { 227 t.Fatalf("ReadMsgUnix: %v", err) 228 } 229 closeUnix.Stop() 230 231 scms, err := unix.ParseSocketControlMessage(oob[:oobn]) 232 if err != nil { 233 t.Fatalf("ParseSocketControlMessage: %v", err) 234 } 235 if len(scms) != 1 { 236 t.Fatalf("expected 1 SocketControlMessage; got scms = %#v", scms) 237 } 238 scm := scms[0] 239 gotFds, err := unix.ParseUnixRights(&scm) 240 if err != nil { 241 t.Fatalf("unix.ParseUnixRights: %v", err) 242 } 243 if len(gotFds) != 1 { 244 t.Fatalf("wanted 1 fd; got %#v", gotFds) 245 } 246 247 f := os.NewFile(uintptr(gotFds[0]), "fd-from-child") 248 defer f.Close() 249 250 got, err := ioutil.ReadAll(f) 251 want := "Hello from child process!\n" 252 if string(got) != want { 253 t.Errorf("child process ReadAll: %q, %v; want %q", got, err, want) 254 } 255 } 256 257 // passFDChild is the child process used by TestPassFD. 258 func passFDChild() { 259 defer os.Exit(0) 260 261 // Look for our fd. It should be fd 3, but we work around an fd leak 262 // bug here (http://golang.org/issue/2603) to let it be elsewhere. 263 var uc *net.UnixConn 264 for fd := uintptr(3); fd <= 10; fd++ { 265 f := os.NewFile(fd, "unix-conn") 266 var ok bool 267 netc, _ := net.FileConn(f) 268 uc, ok = netc.(*net.UnixConn) 269 if ok { 270 break 271 } 272 } 273 if uc == nil { 274 fmt.Println("failed to find unix fd") 275 return 276 } 277 278 // Make a file f to send to our parent process on uc. 279 // We make it in tempDir, which our parent will clean up. 280 flag.Parse() 281 tempDir := flag.Arg(0) 282 f, err := ioutil.TempFile(tempDir, "") 283 if err != nil { 284 fmt.Printf("TempFile: %v", err) 285 return 286 } 287 288 f.Write([]byte("Hello from child process!\n")) 289 f.Seek(0, 0) 290 291 rights := unix.UnixRights(int(f.Fd())) 292 dummyByte := []byte("x") 293 n, oobn, err := uc.WriteMsgUnix(dummyByte, rights, nil) 294 if err != nil { 295 fmt.Printf("WriteMsgUnix: %v", err) 296 return 297 } 298 if n != 1 || oobn != len(rights) { 299 fmt.Printf("WriteMsgUnix = %d, %d; want 1, %d", n, oobn, len(rights)) 300 return 301 } 302 } 303 304 // TestUnixRightsRoundtrip tests that UnixRights, ParseSocketControlMessage, 305 // and ParseUnixRights are able to successfully round-trip lists of file descriptors. 306 func TestUnixRightsRoundtrip(t *testing.T) { 307 testCases := [...][][]int{ 308 {{42}}, 309 {{1, 2}}, 310 {{3, 4, 5}}, 311 {{}}, 312 {{1, 2}, {3, 4, 5}, {}, {7}}, 313 } 314 for _, testCase := range testCases { 315 b := []byte{} 316 var n int 317 for _, fds := range testCase { 318 // Last assignment to n wins 319 n = len(b) + unix.CmsgLen(4*len(fds)) 320 b = append(b, unix.UnixRights(fds...)...) 321 } 322 // Truncate b 323 b = b[:n] 324 325 scms, err := unix.ParseSocketControlMessage(b) 326 if err != nil { 327 t.Fatalf("ParseSocketControlMessage: %v", err) 328 } 329 if len(scms) != len(testCase) { 330 t.Fatalf("expected %v SocketControlMessage; got scms = %#v", len(testCase), scms) 331 } 332 for i, scm := range scms { 333 gotFds, err := unix.ParseUnixRights(&scm) 334 if err != nil { 335 t.Fatalf("ParseUnixRights: %v", err) 336 } 337 wantFds := testCase[i] 338 if len(gotFds) != len(wantFds) { 339 t.Fatalf("expected %v fds, got %#v", len(wantFds), gotFds) 340 } 341 for j, fd := range gotFds { 342 if fd != wantFds[j] { 343 t.Fatalf("expected fd %v, got %v", wantFds[j], fd) 344 } 345 } 346 } 347 } 348 } 349 350 func TestRlimit(t *testing.T) { 351 var rlimit, zero unix.Rlimit 352 err := unix.Getrlimit(unix.RLIMIT_NOFILE, &rlimit) 353 if err != nil { 354 t.Fatalf("Getrlimit: save failed: %v", err) 355 } 356 if zero == rlimit { 357 t.Fatalf("Getrlimit: save failed: got zero value %#v", rlimit) 358 } 359 set := rlimit 360 set.Cur = set.Max - 1 361 if runtime.GOOS == "darwin" && set.Cur > 10240 { 362 // The max file limit is 10240, even though 363 // the max returned by Getrlimit is 1<<63-1. 364 // This is OPEN_MAX in sys/syslimits.h. 365 set.Cur = 10240 366 } 367 err = unix.Setrlimit(unix.RLIMIT_NOFILE, &set) 368 if err != nil { 369 t.Fatalf("Setrlimit: set failed: %#v %v", set, err) 370 } 371 var get unix.Rlimit 372 err = unix.Getrlimit(unix.RLIMIT_NOFILE, &get) 373 if err != nil { 374 t.Fatalf("Getrlimit: get failed: %v", err) 375 } 376 set = rlimit 377 set.Cur = set.Max - 1 378 if set != get { 379 // Seems like Darwin requires some privilege to 380 // increase the soft limit of rlimit sandbox, though 381 // Setrlimit never reports an error. 382 switch runtime.GOOS { 383 case "darwin": 384 default: 385 t.Fatalf("Rlimit: change failed: wanted %#v got %#v", set, get) 386 } 387 } 388 err = unix.Setrlimit(unix.RLIMIT_NOFILE, &rlimit) 389 if err != nil { 390 t.Fatalf("Setrlimit: restore failed: %#v %v", rlimit, err) 391 } 392 } 393 394 func TestSeekFailure(t *testing.T) { 395 _, err := unix.Seek(-1, 0, 0) 396 if err == nil { 397 t.Fatalf("Seek(-1, 0, 0) did not fail") 398 } 399 str := err.Error() // used to crash on Linux 400 t.Logf("Seek: %v", str) 401 if str == "" { 402 t.Fatalf("Seek(-1, 0, 0) return error with empty message") 403 } 404 } 405 406 func TestDup(t *testing.T) { 407 file, err := ioutil.TempFile("", "TestDup") 408 if err != nil { 409 t.Fatalf("Tempfile failed: %v", err) 410 } 411 defer os.Remove(file.Name()) 412 defer file.Close() 413 f := int(file.Fd()) 414 415 newFd, err := unix.Dup(f) 416 if err != nil { 417 t.Fatalf("Dup: %v", err) 418 } 419 420 // Create and reserve a file descriptor. 421 // Dup2 automatically closes it before reusing it. 422 nullFile, err := os.Open("/dev/null") 423 if err != nil { 424 t.Fatal(err) 425 } 426 dupFd := int(file.Fd()) 427 err = unix.Dup2(newFd, dupFd) 428 if err != nil { 429 t.Fatalf("Dup2: %v", err) 430 } 431 // Keep the dummy file open long enough to not be closed in 432 // its finalizer. 433 runtime.KeepAlive(nullFile) 434 435 b1 := []byte("Test123") 436 b2 := make([]byte, 7) 437 _, err = unix.Write(dupFd, b1) 438 if err != nil { 439 t.Fatalf("Write to dup2 fd failed: %v", err) 440 } 441 _, err = unix.Seek(f, 0, 0) 442 if err != nil { 443 t.Fatalf("Seek failed: %v", err) 444 } 445 _, err = unix.Read(f, b2) 446 if err != nil { 447 t.Fatalf("Read back failed: %v", err) 448 } 449 if string(b1) != string(b2) { 450 t.Errorf("Dup: stdout write not in file, expected %v, got %v", string(b1), string(b2)) 451 } 452 } 453 454 func TestPoll(t *testing.T) { 455 if runtime.GOOS == "android" || 456 (runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64")) { 457 t.Skip("mkfifo syscall is not available on android and iOS, skipping test") 458 } 459 460 defer chtmpdir(t)() 461 f, cleanup := mktmpfifo(t) 462 defer cleanup() 463 464 const timeout = 100 465 466 ok := make(chan bool, 1) 467 go func() { 468 select { 469 case <-time.After(10 * timeout * time.Millisecond): 470 t.Errorf("Poll: failed to timeout after %d milliseconds", 10*timeout) 471 case <-ok: 472 } 473 }() 474 475 fds := []unix.PollFd{{Fd: int32(f.Fd()), Events: unix.POLLIN}} 476 n, err := unix.Poll(fds, timeout) 477 ok <- true 478 if err != nil { 479 t.Errorf("Poll: unexpected error: %v", err) 480 return 481 } 482 if n != 0 { 483 t.Errorf("Poll: wrong number of events: got %v, expected %v", n, 0) 484 return 485 } 486 } 487 488 func TestGetwd(t *testing.T) { 489 fd, err := os.Open(".") 490 if err != nil { 491 t.Fatalf("Open .: %s", err) 492 } 493 defer fd.Close() 494 // Directory list for test. Do not worry if any are symlinks or do not 495 // exist on some common unix desktop environments. That will be checked. 496 dirs := []string{"/", "/usr/bin", "/etc", "/var", "/opt"} 497 switch runtime.GOOS { 498 case "android": 499 dirs = []string{"/", "/system/bin"} 500 case "darwin": 501 switch runtime.GOARCH { 502 case "arm", "arm64": 503 d1, err := ioutil.TempDir("", "d1") 504 if err != nil { 505 t.Fatalf("TempDir: %v", err) 506 } 507 d2, err := ioutil.TempDir("", "d2") 508 if err != nil { 509 t.Fatalf("TempDir: %v", err) 510 } 511 dirs = []string{d1, d2} 512 } 513 } 514 oldwd := os.Getenv("PWD") 515 for _, d := range dirs { 516 // Check whether d exists, is a dir and that d's path does not contain a symlink 517 fi, err := os.Stat(d) 518 if err != nil || !fi.IsDir() { 519 t.Logf("Test dir %s stat error (%v) or not a directory, skipping", d, err) 520 continue 521 } 522 check, err := filepath.EvalSymlinks(d) 523 if err != nil || check != d { 524 t.Logf("Test dir %s (%s) is symlink or other error (%v), skipping", d, check, err) 525 continue 526 } 527 err = os.Chdir(d) 528 if err != nil { 529 t.Fatalf("Chdir: %v", err) 530 } 531 pwd, err := unix.Getwd() 532 if err != nil { 533 t.Fatalf("Getwd in %s: %s", d, err) 534 } 535 os.Setenv("PWD", oldwd) 536 err = fd.Chdir() 537 if err != nil { 538 // We changed the current directory and cannot go back. 539 // Don't let the tests continue; they'll scribble 540 // all over some other directory. 541 fmt.Fprintf(os.Stderr, "fchdir back to dot failed: %s\n", err) 542 os.Exit(1) 543 } 544 if pwd != d { 545 t.Fatalf("Getwd returned %q want %q", pwd, d) 546 } 547 } 548 } 549 550 func TestFstatat(t *testing.T) { 551 defer chtmpdir(t)() 552 553 touch(t, "file1") 554 555 var st1 unix.Stat_t 556 err := unix.Stat("file1", &st1) 557 if err != nil { 558 t.Fatalf("Stat: %v", err) 559 } 560 561 var st2 unix.Stat_t 562 err = unix.Fstatat(unix.AT_FDCWD, "file1", &st2, 0) 563 if err != nil { 564 t.Fatalf("Fstatat: %v", err) 565 } 566 567 if st1 != st2 { 568 t.Errorf("Fstatat: returned stat does not match Stat") 569 } 570 571 err = os.Symlink("file1", "symlink1") 572 if err != nil { 573 t.Fatal(err) 574 } 575 576 err = unix.Lstat("symlink1", &st1) 577 if err != nil { 578 t.Fatalf("Lstat: %v", err) 579 } 580 581 err = unix.Fstatat(unix.AT_FDCWD, "symlink1", &st2, unix.AT_SYMLINK_NOFOLLOW) 582 if err != nil { 583 t.Fatalf("Fstatat: %v", err) 584 } 585 586 if st1 != st2 { 587 t.Errorf("Fstatat: returned stat does not match Lstat") 588 } 589 } 590 591 func TestFchmodat(t *testing.T) { 592 defer chtmpdir(t)() 593 594 touch(t, "file1") 595 err := os.Symlink("file1", "symlink1") 596 if err != nil { 597 t.Fatal(err) 598 } 599 600 mode := os.FileMode(0444) 601 err = unix.Fchmodat(unix.AT_FDCWD, "symlink1", uint32(mode), 0) 602 if err != nil { 603 t.Fatalf("Fchmodat: unexpected error: %v", err) 604 } 605 606 fi, err := os.Stat("file1") 607 if err != nil { 608 t.Fatal(err) 609 } 610 611 if fi.Mode() != mode { 612 t.Errorf("Fchmodat: failed to change file mode: expected %v, got %v", mode, fi.Mode()) 613 } 614 615 mode = os.FileMode(0644) 616 didChmodSymlink := true 617 err = unix.Fchmodat(unix.AT_FDCWD, "symlink1", uint32(mode), unix.AT_SYMLINK_NOFOLLOW) 618 if err != nil { 619 if (runtime.GOOS == "android" || runtime.GOOS == "linux" || runtime.GOOS == "solaris") && err == unix.EOPNOTSUPP { 620 // Linux and Illumos don't support flags != 0 621 didChmodSymlink = false 622 } else { 623 t.Fatalf("Fchmodat: unexpected error: %v", err) 624 } 625 } 626 627 if !didChmodSymlink { 628 // Didn't change mode of the symlink. On Linux, the permissions 629 // of a symbolic link are always 0777 according to symlink(7) 630 mode = os.FileMode(0777) 631 } 632 633 var st unix.Stat_t 634 err = unix.Lstat("symlink1", &st) 635 if err != nil { 636 t.Fatal(err) 637 } 638 639 got := os.FileMode(st.Mode & 0777) 640 if got != mode { 641 t.Errorf("Fchmodat: failed to change symlink mode: expected %v, got %v", mode, got) 642 } 643 } 644 645 func TestMkdev(t *testing.T) { 646 major := uint32(42) 647 minor := uint32(7) 648 dev := unix.Mkdev(major, minor) 649 650 if unix.Major(dev) != major { 651 t.Errorf("Major(%#x) == %d, want %d", dev, unix.Major(dev), major) 652 } 653 if unix.Minor(dev) != minor { 654 t.Errorf("Minor(%#x) == %d, want %d", dev, unix.Minor(dev), minor) 655 } 656 } 657 658 func TestRenameat(t *testing.T) { 659 defer chtmpdir(t)() 660 661 from, to := "renamefrom", "renameto" 662 663 touch(t, from) 664 665 err := unix.Renameat(unix.AT_FDCWD, from, unix.AT_FDCWD, to) 666 if err != nil { 667 t.Fatalf("Renameat: unexpected error: %v", err) 668 } 669 670 _, err = os.Stat(to) 671 if err != nil { 672 t.Error(err) 673 } 674 675 _, err = os.Stat(from) 676 if err == nil { 677 t.Errorf("Renameat: stat of renamed file %q unexpectedly succeeded", from) 678 } 679 } 680 681 // mktmpfifo creates a temporary FIFO and provides a cleanup function. 682 func mktmpfifo(t *testing.T) (*os.File, func()) { 683 err := unix.Mkfifo("fifo", 0666) 684 if err != nil { 685 t.Fatalf("mktmpfifo: failed to create FIFO: %v", err) 686 } 687 688 f, err := os.OpenFile("fifo", os.O_RDWR, 0666) 689 if err != nil { 690 os.Remove("fifo") 691 t.Fatalf("mktmpfifo: failed to open FIFO: %v", err) 692 } 693 694 return f, func() { 695 f.Close() 696 os.Remove("fifo") 697 } 698 } 699 700 // utilities taken from os/os_test.go 701 702 func touch(t *testing.T, name string) { 703 f, err := os.Create(name) 704 if err != nil { 705 t.Fatal(err) 706 } 707 if err := f.Close(); err != nil { 708 t.Fatal(err) 709 } 710 } 711 712 // chtmpdir changes the working directory to a new temporary directory and 713 // provides a cleanup function. Used when PWD is read-only. 714 func chtmpdir(t *testing.T) func() { 715 oldwd, err := os.Getwd() 716 if err != nil { 717 t.Fatalf("chtmpdir: %v", err) 718 } 719 d, err := ioutil.TempDir("", "test") 720 if err != nil { 721 t.Fatalf("chtmpdir: %v", err) 722 } 723 if err := os.Chdir(d); err != nil { 724 t.Fatalf("chtmpdir: %v", err) 725 } 726 return func() { 727 if err := os.Chdir(oldwd); err != nil { 728 t.Fatalf("chtmpdir: %v", err) 729 } 730 os.RemoveAll(d) 731 } 732 }