github.com/zxy12/go_duplicate_112_new@v0.0.0-20200807091221-747231827200/src/os/exec/exec_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 // Use an external test to avoid os/exec -> net/http -> crypto/x509 -> os/exec 6 // circular dependency on non-cgo darwin. 7 8 package exec_test 9 10 import ( 11 "bufio" 12 "bytes" 13 "context" 14 "fmt" 15 "internal/poll" 16 "internal/testenv" 17 "io" 18 "io/ioutil" 19 "log" 20 "net" 21 "net/http" 22 "net/http/httptest" 23 "os" 24 "os/exec" 25 "path/filepath" 26 "runtime" 27 "strconv" 28 "strings" 29 "testing" 30 "time" 31 ) 32 33 func helperCommandContext(t *testing.T, ctx context.Context, s ...string) (cmd *exec.Cmd) { 34 testenv.MustHaveExec(t) 35 36 cs := []string{"-test.run=TestHelperProcess", "--"} 37 cs = append(cs, s...) 38 if ctx != nil { 39 cmd = exec.CommandContext(ctx, os.Args[0], cs...) 40 } else { 41 cmd = exec.Command(os.Args[0], cs...) 42 } 43 cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"} 44 return cmd 45 } 46 47 func helperCommand(t *testing.T, s ...string) *exec.Cmd { 48 return helperCommandContext(t, nil, s...) 49 } 50 51 func TestEcho(t *testing.T) { 52 bs, err := helperCommand(t, "echo", "foo bar", "baz").Output() 53 if err != nil { 54 t.Errorf("echo: %v", err) 55 } 56 if g, e := string(bs), "foo bar baz\n"; g != e { 57 t.Errorf("echo: want %q, got %q", e, g) 58 } 59 } 60 61 func TestCommandRelativeName(t *testing.T) { 62 testenv.MustHaveExec(t) 63 64 // Run our own binary as a relative path 65 // (e.g. "_test/exec.test") our parent directory. 66 base := filepath.Base(os.Args[0]) // "exec.test" 67 dir := filepath.Dir(os.Args[0]) // "/tmp/go-buildNNNN/os/exec/_test" 68 if dir == "." { 69 t.Skip("skipping; running test at root somehow") 70 } 71 parentDir := filepath.Dir(dir) // "/tmp/go-buildNNNN/os/exec" 72 dirBase := filepath.Base(dir) // "_test" 73 if dirBase == "." { 74 t.Skipf("skipping; unexpected shallow dir of %q", dir) 75 } 76 77 cmd := exec.Command(filepath.Join(dirBase, base), "-test.run=TestHelperProcess", "--", "echo", "foo") 78 cmd.Dir = parentDir 79 cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"} 80 81 out, err := cmd.Output() 82 if err != nil { 83 t.Errorf("echo: %v", err) 84 } 85 if g, e := string(out), "foo\n"; g != e { 86 t.Errorf("echo: want %q, got %q", e, g) 87 } 88 } 89 90 func TestCatStdin(t *testing.T) { 91 // Cat, testing stdin and stdout. 92 input := "Input string\nLine 2" 93 p := helperCommand(t, "cat") 94 p.Stdin = strings.NewReader(input) 95 bs, err := p.Output() 96 if err != nil { 97 t.Errorf("cat: %v", err) 98 } 99 s := string(bs) 100 if s != input { 101 t.Errorf("cat: want %q, got %q", input, s) 102 } 103 } 104 105 func TestEchoFileRace(t *testing.T) { 106 cmd := helperCommand(t, "echo") 107 stdin, err := cmd.StdinPipe() 108 if err != nil { 109 t.Fatalf("StdinPipe: %v", err) 110 } 111 if err := cmd.Start(); err != nil { 112 t.Fatalf("Start: %v", err) 113 } 114 wrote := make(chan bool) 115 go func() { 116 defer close(wrote) 117 fmt.Fprint(stdin, "echo\n") 118 }() 119 if err := cmd.Wait(); err != nil { 120 t.Fatalf("Wait: %v", err) 121 } 122 <-wrote 123 } 124 125 func TestCatGoodAndBadFile(t *testing.T) { 126 // Testing combined output and error values. 127 bs, err := helperCommand(t, "cat", "/bogus/file.foo", "exec_test.go").CombinedOutput() 128 if _, ok := err.(*exec.ExitError); !ok { 129 t.Errorf("expected *exec.ExitError from cat combined; got %T: %v", err, err) 130 } 131 s := string(bs) 132 sp := strings.SplitN(s, "\n", 2) 133 if len(sp) != 2 { 134 t.Fatalf("expected two lines from cat; got %q", s) 135 } 136 errLine, body := sp[0], sp[1] 137 if !strings.HasPrefix(errLine, "Error: open /bogus/file.foo") { 138 t.Errorf("expected stderr to complain about file; got %q", errLine) 139 } 140 if !strings.Contains(body, "func TestHelperProcess(t *testing.T)") { 141 t.Errorf("expected test code; got %q (len %d)", body, len(body)) 142 } 143 } 144 145 func TestNoExistExecutable(t *testing.T) { 146 // Can't run a non-existent executable 147 err := exec.Command("/no-exist-executable").Run() 148 if err == nil { 149 t.Error("expected error from /no-exist-executable") 150 } 151 } 152 153 func TestExitStatus(t *testing.T) { 154 // Test that exit values are returned correctly 155 cmd := helperCommand(t, "exit", "42") 156 err := cmd.Run() 157 want := "exit status 42" 158 switch runtime.GOOS { 159 case "plan9": 160 want = fmt.Sprintf("exit status: '%s %d: 42'", filepath.Base(cmd.Path), cmd.ProcessState.Pid()) 161 } 162 if werr, ok := err.(*exec.ExitError); ok { 163 if s := werr.Error(); s != want { 164 t.Errorf("from exit 42 got exit %q, want %q", s, want) 165 } 166 } else { 167 t.Fatalf("expected *exec.ExitError from exit 42; got %T: %v", err, err) 168 } 169 } 170 171 func TestExitCode(t *testing.T) { 172 // Test that exit code are returned correctly 173 cmd := helperCommand(t, "exit", "42") 174 cmd.Run() 175 want := 42 176 if runtime.GOOS == "plan9" { 177 want = 1 178 } 179 got := cmd.ProcessState.ExitCode() 180 if want != got { 181 t.Errorf("ExitCode got %d, want %d", got, want) 182 } 183 184 cmd = helperCommand(t, "/no-exist-executable") 185 cmd.Run() 186 want = 2 187 if runtime.GOOS == "plan9" { 188 want = 1 189 } 190 got = cmd.ProcessState.ExitCode() 191 if want != got { 192 t.Errorf("ExitCode got %d, want %d", got, want) 193 } 194 195 cmd = helperCommand(t, "exit", "255") 196 cmd.Run() 197 want = 255 198 if runtime.GOOS == "plan9" { 199 want = 1 200 } 201 got = cmd.ProcessState.ExitCode() 202 if want != got { 203 t.Errorf("ExitCode got %d, want %d", got, want) 204 } 205 206 cmd = helperCommand(t, "cat") 207 cmd.Run() 208 want = 0 209 got = cmd.ProcessState.ExitCode() 210 if want != got { 211 t.Errorf("ExitCode got %d, want %d", got, want) 212 } 213 214 // Test when command does not call Run(). 215 cmd = helperCommand(t, "cat") 216 want = -1 217 got = cmd.ProcessState.ExitCode() 218 if want != got { 219 t.Errorf("ExitCode got %d, want %d", got, want) 220 } 221 } 222 223 func TestPipes(t *testing.T) { 224 check := func(what string, err error) { 225 if err != nil { 226 t.Fatalf("%s: %v", what, err) 227 } 228 } 229 // Cat, testing stdin and stdout. 230 c := helperCommand(t, "pipetest") 231 stdin, err := c.StdinPipe() 232 check("StdinPipe", err) 233 stdout, err := c.StdoutPipe() 234 check("StdoutPipe", err) 235 stderr, err := c.StderrPipe() 236 check("StderrPipe", err) 237 238 outbr := bufio.NewReader(stdout) 239 errbr := bufio.NewReader(stderr) 240 line := func(what string, br *bufio.Reader) string { 241 line, _, err := br.ReadLine() 242 if err != nil { 243 t.Fatalf("%s: %v", what, err) 244 } 245 return string(line) 246 } 247 248 err = c.Start() 249 check("Start", err) 250 251 _, err = stdin.Write([]byte("O:I am output\n")) 252 check("first stdin Write", err) 253 if g, e := line("first output line", outbr), "O:I am output"; g != e { 254 t.Errorf("got %q, want %q", g, e) 255 } 256 257 _, err = stdin.Write([]byte("E:I am error\n")) 258 check("second stdin Write", err) 259 if g, e := line("first error line", errbr), "E:I am error"; g != e { 260 t.Errorf("got %q, want %q", g, e) 261 } 262 263 _, err = stdin.Write([]byte("O:I am output2\n")) 264 check("third stdin Write 3", err) 265 if g, e := line("second output line", outbr), "O:I am output2"; g != e { 266 t.Errorf("got %q, want %q", g, e) 267 } 268 269 stdin.Close() 270 err = c.Wait() 271 check("Wait", err) 272 } 273 274 const stdinCloseTestString = "Some test string." 275 276 // Issue 6270. 277 func TestStdinClose(t *testing.T) { 278 check := func(what string, err error) { 279 if err != nil { 280 t.Fatalf("%s: %v", what, err) 281 } 282 } 283 cmd := helperCommand(t, "stdinClose") 284 stdin, err := cmd.StdinPipe() 285 check("StdinPipe", err) 286 // Check that we can access methods of the underlying os.File.` 287 if _, ok := stdin.(interface { 288 Fd() uintptr 289 }); !ok { 290 t.Error("can't access methods of underlying *os.File") 291 } 292 check("Start", cmd.Start()) 293 go func() { 294 _, err := io.Copy(stdin, strings.NewReader(stdinCloseTestString)) 295 check("Copy", err) 296 // Before the fix, this next line would race with cmd.Wait. 297 check("Close", stdin.Close()) 298 }() 299 check("Wait", cmd.Wait()) 300 } 301 302 // Issue 17647. 303 // It used to be the case that TestStdinClose, above, would fail when 304 // run under the race detector. This test is a variant of TestStdinClose 305 // that also used to fail when run under the race detector. 306 // This test is run by cmd/dist under the race detector to verify that 307 // the race detector no longer reports any problems. 308 func TestStdinCloseRace(t *testing.T) { 309 cmd := helperCommand(t, "stdinClose") 310 stdin, err := cmd.StdinPipe() 311 if err != nil { 312 t.Fatalf("StdinPipe: %v", err) 313 } 314 if err := cmd.Start(); err != nil { 315 t.Fatalf("Start: %v", err) 316 } 317 go func() { 318 // We don't check the error return of Kill. It is 319 // possible that the process has already exited, in 320 // which case Kill will return an error "process 321 // already finished". The purpose of this test is to 322 // see whether the race detector reports an error; it 323 // doesn't matter whether this Kill succeeds or not. 324 cmd.Process.Kill() 325 }() 326 go func() { 327 // Send the wrong string, so that the child fails even 328 // if the other goroutine doesn't manage to kill it first. 329 // This test is to check that the race detector does not 330 // falsely report an error, so it doesn't matter how the 331 // child process fails. 332 io.Copy(stdin, strings.NewReader("unexpected string")) 333 if err := stdin.Close(); err != nil { 334 t.Errorf("stdin.Close: %v", err) 335 } 336 }() 337 if err := cmd.Wait(); err == nil { 338 t.Fatalf("Wait: succeeded unexpectedly") 339 } 340 } 341 342 // Issue 5071 343 func TestPipeLookPathLeak(t *testing.T) { 344 // If we are reading from /proc/self/fd we (should) get an exact result. 345 tolerance := 0 346 347 // Reading /proc/self/fd is more reliable than calling lsof, so try that 348 // first. 349 numOpenFDs := func() (int, []byte, error) { 350 fds, err := ioutil.ReadDir("/proc/self/fd") 351 if err != nil { 352 return 0, nil, err 353 } 354 return len(fds), nil, nil 355 } 356 want, before, err := numOpenFDs() 357 if err != nil { 358 // We encountered a problem reading /proc/self/fd (we might be on 359 // a platform that doesn't have it). Fall back onto lsof. 360 t.Logf("using lsof because: %v", err) 361 numOpenFDs = func() (int, []byte, error) { 362 // Android's stock lsof does not obey the -p option, 363 // so extra filtering is needed. 364 // https://golang.org/issue/10206 365 if runtime.GOOS == "android" { 366 // numOpenFDsAndroid handles errors itself and 367 // might skip or fail the test. 368 n, lsof := numOpenFDsAndroid(t) 369 return n, lsof, nil 370 } 371 lsof, err := exec.Command("lsof", "-b", "-n", "-p", strconv.Itoa(os.Getpid())).Output() 372 return bytes.Count(lsof, []byte("\n")), lsof, err 373 } 374 375 // lsof may see file descriptors associated with the fork itself, 376 // so we allow some extra margin if we have to use it. 377 // https://golang.org/issue/19243 378 tolerance = 5 379 380 // Retry reading the number of open file descriptors. 381 want, before, err = numOpenFDs() 382 if err != nil { 383 t.Log(err) 384 t.Skipf("skipping test; error finding or running lsof") 385 } 386 } 387 388 for i := 0; i < 6; i++ { 389 cmd := exec.Command("something-that-does-not-exist-executable") 390 cmd.StdoutPipe() 391 cmd.StderrPipe() 392 cmd.StdinPipe() 393 if err := cmd.Run(); err == nil { 394 t.Fatal("unexpected success") 395 } 396 } 397 got, after, err := numOpenFDs() 398 if err != nil { 399 // numOpenFDs has already succeeded once, it should work here. 400 t.Errorf("unexpected failure: %v", err) 401 } 402 if got-want > tolerance { 403 t.Errorf("number of open file descriptors changed: got %v, want %v", got, want) 404 if before != nil { 405 t.Errorf("before:\n%v\n", before) 406 } 407 if after != nil { 408 t.Errorf("after:\n%v\n", after) 409 } 410 } 411 } 412 413 func numOpenFDsAndroid(t *testing.T) (n int, lsof []byte) { 414 raw, err := exec.Command("lsof").Output() 415 if err != nil { 416 t.Skip("skipping test; error finding or running lsof") 417 } 418 419 // First find the PID column index by parsing the first line, and 420 // select lines containing pid in the column. 421 pid := []byte(strconv.Itoa(os.Getpid())) 422 pidCol := -1 423 424 s := bufio.NewScanner(bytes.NewReader(raw)) 425 for s.Scan() { 426 line := s.Bytes() 427 fields := bytes.Fields(line) 428 if pidCol < 0 { 429 for i, v := range fields { 430 if bytes.Equal(v, []byte("PID")) { 431 pidCol = i 432 break 433 } 434 } 435 lsof = append(lsof, line...) 436 continue 437 } 438 if bytes.Equal(fields[pidCol], pid) { 439 lsof = append(lsof, '\n') 440 lsof = append(lsof, line...) 441 } 442 } 443 if pidCol < 0 { 444 t.Fatal("error processing lsof output: unexpected header format") 445 } 446 if err := s.Err(); err != nil { 447 t.Fatalf("error processing lsof output: %v", err) 448 } 449 return bytes.Count(lsof, []byte("\n")), lsof 450 } 451 452 var testedAlreadyLeaked = false 453 454 // basefds returns the number of expected file descriptors 455 // to be present in a process at start. 456 // stdin, stdout, stderr, epoll/kqueue, maybe testlog 457 func basefds() uintptr { 458 n := os.Stderr.Fd() + 1 459 // The poll (epoll/kqueue) descriptor can be numerically 460 // either between stderr and the testlog-fd, or after 461 // testlog-fd. 462 if poll.IsPollDescriptor(n) { 463 n++ 464 } 465 for _, arg := range os.Args { 466 if strings.HasPrefix(arg, "-test.testlogfile=") { 467 n++ 468 } 469 } 470 return n 471 } 472 473 func closeUnexpectedFds(t *testing.T, m string) { 474 for fd := basefds(); fd <= 101; fd++ { 475 if poll.IsPollDescriptor(fd) { 476 continue 477 } 478 err := os.NewFile(fd, "").Close() 479 if err == nil { 480 t.Logf("%s: Something already leaked - closed fd %d", m, fd) 481 } 482 } 483 } 484 485 func TestExtraFilesFDShuffle(t *testing.T) { 486 t.Skip("flaky test; see https://golang.org/issue/5780") 487 switch runtime.GOOS { 488 case "darwin": 489 // TODO(cnicolaou): https://golang.org/issue/2603 490 // leads to leaked file descriptors in this test when it's 491 // run from a builder. 492 closeUnexpectedFds(t, "TestExtraFilesFDShuffle") 493 case "netbsd": 494 // https://golang.org/issue/3955 495 closeUnexpectedFds(t, "TestExtraFilesFDShuffle") 496 case "windows": 497 t.Skip("no operating system support; skipping") 498 } 499 500 // syscall.StartProcess maps all the FDs passed to it in 501 // ProcAttr.Files (the concatenation of stdin,stdout,stderr and 502 // ExtraFiles) into consecutive FDs in the child, that is: 503 // Files{11, 12, 6, 7, 9, 3} should result in the file 504 // represented by FD 11 in the parent being made available as 0 505 // in the child, 12 as 1, etc. 506 // 507 // We want to test that FDs in the child do not get overwritten 508 // by one another as this shuffle occurs. The original implementation 509 // was buggy in that in some data dependent cases it would overwrite 510 // stderr in the child with one of the ExtraFile members. 511 // Testing for this case is difficult because it relies on using 512 // the same FD values as that case. In particular, an FD of 3 513 // must be at an index of 4 or higher in ProcAttr.Files and 514 // the FD of the write end of the Stderr pipe (as obtained by 515 // StderrPipe()) must be the same as the size of ProcAttr.Files; 516 // therefore we test that the read end of this pipe (which is what 517 // is returned to the parent by StderrPipe() being one less than 518 // the size of ProcAttr.Files, i.e. 3+len(cmd.ExtraFiles). 519 // 520 // Moving this test case around within the overall tests may 521 // affect the FDs obtained and hence the checks to catch these cases. 522 npipes := 2 523 c := helperCommand(t, "extraFilesAndPipes", strconv.Itoa(npipes+1)) 524 rd, wr, _ := os.Pipe() 525 defer rd.Close() 526 if rd.Fd() != 3 { 527 t.Errorf("bad test value for test pipe: fd %d", rd.Fd()) 528 } 529 stderr, _ := c.StderrPipe() 530 wr.WriteString("_LAST") 531 wr.Close() 532 533 pipes := make([]struct { 534 r, w *os.File 535 }, npipes) 536 data := []string{"a", "b"} 537 538 for i := 0; i < npipes; i++ { 539 r, w, err := os.Pipe() 540 if err != nil { 541 t.Fatalf("unexpected error creating pipe: %s", err) 542 } 543 pipes[i].r = r 544 pipes[i].w = w 545 w.WriteString(data[i]) 546 c.ExtraFiles = append(c.ExtraFiles, pipes[i].r) 547 defer func() { 548 r.Close() 549 w.Close() 550 }() 551 } 552 // Put fd 3 at the end. 553 c.ExtraFiles = append(c.ExtraFiles, rd) 554 555 stderrFd := int(stderr.(*os.File).Fd()) 556 if stderrFd != ((len(c.ExtraFiles) + 3) - 1) { 557 t.Errorf("bad test value for stderr pipe") 558 } 559 560 expected := "child: " + strings.Join(data, "") + "_LAST" 561 562 err := c.Start() 563 if err != nil { 564 t.Fatalf("Run: %v", err) 565 } 566 ch := make(chan string, 1) 567 go func(ch chan string) { 568 buf := make([]byte, 512) 569 n, err := stderr.Read(buf) 570 if err != nil { 571 t.Errorf("Read: %s", err) 572 ch <- err.Error() 573 } else { 574 ch <- string(buf[:n]) 575 } 576 close(ch) 577 }(ch) 578 select { 579 case m := <-ch: 580 if m != expected { 581 t.Errorf("Read: '%s' not '%s'", m, expected) 582 } 583 case <-time.After(5 * time.Second): 584 t.Errorf("Read timedout") 585 } 586 c.Wait() 587 } 588 589 func TestExtraFiles(t *testing.T) { 590 testenv.MustHaveExec(t) 591 592 if runtime.GOOS == "windows" { 593 t.Skipf("skipping test on %q", runtime.GOOS) 594 } 595 596 // Ensure that file descriptors have not already been leaked into 597 // our environment. 598 if !testedAlreadyLeaked { 599 testedAlreadyLeaked = true 600 closeUnexpectedFds(t, "TestExtraFiles") 601 } 602 603 // Force network usage, to verify the epoll (or whatever) fd 604 // doesn't leak to the child, 605 ln, err := net.Listen("tcp", "127.0.0.1:0") 606 if err != nil { 607 t.Fatal(err) 608 } 609 defer ln.Close() 610 611 // Make sure duplicated fds don't leak to the child. 612 f, err := ln.(*net.TCPListener).File() 613 if err != nil { 614 t.Fatal(err) 615 } 616 defer f.Close() 617 ln2, err := net.FileListener(f) 618 if err != nil { 619 t.Fatal(err) 620 } 621 defer ln2.Close() 622 623 // Force TLS root certs to be loaded (which might involve 624 // cgo), to make sure none of that potential C code leaks fds. 625 ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) 626 // quiet expected TLS handshake error "remote error: bad certificate" 627 ts.Config.ErrorLog = log.New(ioutil.Discard, "", 0) 628 ts.StartTLS() 629 defer ts.Close() 630 _, err = http.Get(ts.URL) 631 if err == nil { 632 t.Errorf("success trying to fetch %s; want an error", ts.URL) 633 } 634 635 tf, err := ioutil.TempFile("", "") 636 if err != nil { 637 t.Fatalf("TempFile: %v", err) 638 } 639 defer os.Remove(tf.Name()) 640 defer tf.Close() 641 642 const text = "Hello, fd 3!" 643 _, err = tf.Write([]byte(text)) 644 if err != nil { 645 t.Fatalf("Write: %v", err) 646 } 647 _, err = tf.Seek(0, io.SeekStart) 648 if err != nil { 649 t.Fatalf("Seek: %v", err) 650 } 651 652 c := helperCommand(t, "read3") 653 var stdout, stderr bytes.Buffer 654 c.Stdout = &stdout 655 c.Stderr = &stderr 656 c.ExtraFiles = []*os.File{tf} 657 err = c.Run() 658 if err != nil { 659 t.Fatalf("Run: %v; stdout %q, stderr %q", err, stdout.Bytes(), stderr.Bytes()) 660 } 661 if stdout.String() != text { 662 t.Errorf("got stdout %q, stderr %q; want %q on stdout", stdout.String(), stderr.String(), text) 663 } 664 } 665 666 func TestExtraFilesRace(t *testing.T) { 667 if runtime.GOOS == "windows" { 668 t.Skip("no operating system support; skipping") 669 } 670 listen := func() net.Listener { 671 ln, err := net.Listen("tcp", "127.0.0.1:0") 672 if err != nil { 673 t.Fatal(err) 674 } 675 return ln 676 } 677 listenerFile := func(ln net.Listener) *os.File { 678 f, err := ln.(*net.TCPListener).File() 679 if err != nil { 680 t.Fatal(err) 681 } 682 return f 683 } 684 runCommand := func(c *exec.Cmd, out chan<- string) { 685 bout, err := c.CombinedOutput() 686 if err != nil { 687 out <- "ERROR:" + err.Error() 688 } else { 689 out <- string(bout) 690 } 691 } 692 693 for i := 0; i < 10; i++ { 694 la := listen() 695 ca := helperCommand(t, "describefiles") 696 ca.ExtraFiles = []*os.File{listenerFile(la)} 697 lb := listen() 698 cb := helperCommand(t, "describefiles") 699 cb.ExtraFiles = []*os.File{listenerFile(lb)} 700 ares := make(chan string) 701 bres := make(chan string) 702 go runCommand(ca, ares) 703 go runCommand(cb, bres) 704 if got, want := <-ares, fmt.Sprintf("fd3: listener %s\n", la.Addr()); got != want { 705 t.Errorf("iteration %d, process A got:\n%s\nwant:\n%s\n", i, got, want) 706 } 707 if got, want := <-bres, fmt.Sprintf("fd3: listener %s\n", lb.Addr()); got != want { 708 t.Errorf("iteration %d, process B got:\n%s\nwant:\n%s\n", i, got, want) 709 } 710 la.Close() 711 lb.Close() 712 for _, f := range ca.ExtraFiles { 713 f.Close() 714 } 715 for _, f := range cb.ExtraFiles { 716 f.Close() 717 } 718 719 } 720 } 721 722 // TestHelperProcess isn't a real test. It's used as a helper process 723 // for TestParameterRun. 724 func TestHelperProcess(*testing.T) { 725 if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" { 726 return 727 } 728 defer os.Exit(0) 729 730 // Determine which command to use to display open files. 731 ofcmd := "lsof" 732 switch runtime.GOOS { 733 case "dragonfly", "freebsd", "netbsd", "openbsd": 734 ofcmd = "fstat" 735 case "plan9": 736 ofcmd = "/bin/cat" 737 case "aix": 738 ofcmd = "procfiles" 739 } 740 741 args := os.Args 742 for len(args) > 0 { 743 if args[0] == "--" { 744 args = args[1:] 745 break 746 } 747 args = args[1:] 748 } 749 if len(args) == 0 { 750 fmt.Fprintf(os.Stderr, "No command\n") 751 os.Exit(2) 752 } 753 754 cmd, args := args[0], args[1:] 755 switch cmd { 756 case "echo": 757 iargs := []interface{}{} 758 for _, s := range args { 759 iargs = append(iargs, s) 760 } 761 fmt.Println(iargs...) 762 case "echoenv": 763 for _, s := range args { 764 fmt.Println(os.Getenv(s)) 765 } 766 os.Exit(0) 767 case "cat": 768 if len(args) == 0 { 769 io.Copy(os.Stdout, os.Stdin) 770 return 771 } 772 exit := 0 773 for _, fn := range args { 774 f, err := os.Open(fn) 775 if err != nil { 776 fmt.Fprintf(os.Stderr, "Error: %v\n", err) 777 exit = 2 778 } else { 779 defer f.Close() 780 io.Copy(os.Stdout, f) 781 } 782 } 783 os.Exit(exit) 784 case "pipetest": 785 bufr := bufio.NewReader(os.Stdin) 786 for { 787 line, _, err := bufr.ReadLine() 788 if err == io.EOF { 789 break 790 } else if err != nil { 791 os.Exit(1) 792 } 793 if bytes.HasPrefix(line, []byte("O:")) { 794 os.Stdout.Write(line) 795 os.Stdout.Write([]byte{'\n'}) 796 } else if bytes.HasPrefix(line, []byte("E:")) { 797 os.Stderr.Write(line) 798 os.Stderr.Write([]byte{'\n'}) 799 } else { 800 os.Exit(1) 801 } 802 } 803 case "stdinClose": 804 b, err := ioutil.ReadAll(os.Stdin) 805 if err != nil { 806 fmt.Fprintf(os.Stderr, "Error: %v\n", err) 807 os.Exit(1) 808 } 809 if s := string(b); s != stdinCloseTestString { 810 fmt.Fprintf(os.Stderr, "Error: Read %q, want %q", s, stdinCloseTestString) 811 os.Exit(1) 812 } 813 os.Exit(0) 814 case "read3": // read fd 3 815 fd3 := os.NewFile(3, "fd3") 816 bs, err := ioutil.ReadAll(fd3) 817 if err != nil { 818 fmt.Printf("ReadAll from fd 3: %v", err) 819 os.Exit(1) 820 } 821 switch runtime.GOOS { 822 case "dragonfly": 823 // TODO(jsing): Determine why DragonFly is leaking 824 // file descriptors... 825 case "darwin": 826 // TODO(bradfitz): broken? Sometimes. 827 // https://golang.org/issue/2603 828 // Skip this additional part of the test for now. 829 case "netbsd": 830 // TODO(jsing): This currently fails on NetBSD due to 831 // the cloned file descriptors that result from opening 832 // /dev/urandom. 833 // https://golang.org/issue/3955 834 case "solaris": 835 // TODO(aram): This fails on Solaris because libc opens 836 // its own files, as it sees fit. Darwin does the same, 837 // see: https://golang.org/issue/2603 838 default: 839 // Now verify that there are no other open fds. 840 var files []*os.File 841 for wantfd := basefds() + 1; wantfd <= 100; wantfd++ { 842 if poll.IsPollDescriptor(wantfd) { 843 continue 844 } 845 f, err := os.Open(os.Args[0]) 846 if err != nil { 847 fmt.Printf("error opening file with expected fd %d: %v", wantfd, err) 848 os.Exit(1) 849 } 850 if got := f.Fd(); got != wantfd { 851 fmt.Printf("leaked parent file. fd = %d; want %d\n", got, wantfd) 852 var args []string 853 switch runtime.GOOS { 854 case "plan9": 855 args = []string{fmt.Sprintf("/proc/%d/fd", os.Getpid())} 856 case "aix": 857 args = []string{fmt.Sprint(os.Getpid())} 858 default: 859 args = []string{"-p", fmt.Sprint(os.Getpid())} 860 } 861 out, _ := exec.Command(ofcmd, args...).CombinedOutput() 862 fmt.Print(string(out)) 863 os.Exit(1) 864 } 865 files = append(files, f) 866 } 867 for _, f := range files { 868 f.Close() 869 } 870 } 871 // Referring to fd3 here ensures that it is not 872 // garbage collected, and therefore closed, while 873 // executing the wantfd loop above. It doesn't matter 874 // what we do with fd3 as long as we refer to it; 875 // closing it is the easy choice. 876 fd3.Close() 877 os.Stdout.Write(bs) 878 case "exit": 879 n, _ := strconv.Atoi(args[0]) 880 os.Exit(n) 881 case "describefiles": 882 f := os.NewFile(3, fmt.Sprintf("fd3")) 883 ln, err := net.FileListener(f) 884 if err == nil { 885 fmt.Printf("fd3: listener %s\n", ln.Addr()) 886 ln.Close() 887 } 888 os.Exit(0) 889 case "extraFilesAndPipes": 890 n, _ := strconv.Atoi(args[0]) 891 pipes := make([]*os.File, n) 892 for i := 0; i < n; i++ { 893 pipes[i] = os.NewFile(uintptr(3+i), strconv.Itoa(i)) 894 } 895 response := "" 896 for i, r := range pipes { 897 ch := make(chan string, 1) 898 go func(c chan string) { 899 buf := make([]byte, 10) 900 n, err := r.Read(buf) 901 if err != nil { 902 fmt.Fprintf(os.Stderr, "Child: read error: %v on pipe %d\n", err, i) 903 os.Exit(1) 904 } 905 c <- string(buf[:n]) 906 close(c) 907 }(ch) 908 select { 909 case m := <-ch: 910 response = response + m 911 case <-time.After(5 * time.Second): 912 fmt.Fprintf(os.Stderr, "Child: Timeout reading from pipe: %d\n", i) 913 os.Exit(1) 914 } 915 } 916 fmt.Fprintf(os.Stderr, "child: %s", response) 917 os.Exit(0) 918 case "exec": 919 cmd := exec.Command(args[1]) 920 cmd.Dir = args[0] 921 output, err := cmd.CombinedOutput() 922 if err != nil { 923 fmt.Fprintf(os.Stderr, "Child: %s %s", err, string(output)) 924 os.Exit(1) 925 } 926 fmt.Printf("%s", string(output)) 927 os.Exit(0) 928 case "lookpath": 929 p, err := exec.LookPath(args[0]) 930 if err != nil { 931 fmt.Fprintf(os.Stderr, "LookPath failed: %v\n", err) 932 os.Exit(1) 933 } 934 fmt.Print(p) 935 os.Exit(0) 936 case "stderrfail": 937 fmt.Fprintf(os.Stderr, "some stderr text\n") 938 os.Exit(1) 939 case "sleep": 940 time.Sleep(3 * time.Second) 941 os.Exit(0) 942 default: 943 fmt.Fprintf(os.Stderr, "Unknown command %q\n", cmd) 944 os.Exit(2) 945 } 946 } 947 948 type delayedInfiniteReader struct{} 949 950 func (delayedInfiniteReader) Read(b []byte) (int, error) { 951 time.Sleep(100 * time.Millisecond) 952 for i := range b { 953 b[i] = 'x' 954 } 955 return len(b), nil 956 } 957 958 // Issue 9173: ignore stdin pipe writes if the program completes successfully. 959 func TestIgnorePipeErrorOnSuccess(t *testing.T) { 960 testenv.MustHaveExec(t) 961 962 // We really only care about testing this on Unixy and Windowsy things. 963 if runtime.GOOS == "plan9" { 964 t.Skipf("skipping test on %q", runtime.GOOS) 965 } 966 967 testWith := func(r io.Reader) func(*testing.T) { 968 return func(t *testing.T) { 969 cmd := helperCommand(t, "echo", "foo") 970 var out bytes.Buffer 971 cmd.Stdin = r 972 cmd.Stdout = &out 973 if err := cmd.Run(); err != nil { 974 t.Fatal(err) 975 } 976 if got, want := out.String(), "foo\n"; got != want { 977 t.Errorf("output = %q; want %q", got, want) 978 } 979 } 980 } 981 t.Run("10MB", testWith(strings.NewReader(strings.Repeat("x", 10<<20)))) 982 t.Run("Infinite", testWith(delayedInfiniteReader{})) 983 } 984 985 type badWriter struct{} 986 987 func (w *badWriter) Write(data []byte) (int, error) { 988 return 0, io.ErrUnexpectedEOF 989 } 990 991 func TestClosePipeOnCopyError(t *testing.T) { 992 testenv.MustHaveExec(t) 993 994 if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { 995 t.Skipf("skipping test on %s - no yes command", runtime.GOOS) 996 } 997 cmd := exec.Command("yes") 998 cmd.Stdout = new(badWriter) 999 c := make(chan int, 1) 1000 go func() { 1001 err := cmd.Run() 1002 if err == nil { 1003 t.Errorf("yes completed successfully") 1004 } 1005 c <- 1 1006 }() 1007 select { 1008 case <-c: 1009 // ok 1010 case <-time.After(5 * time.Second): 1011 t.Fatalf("yes got stuck writing to bad writer") 1012 } 1013 } 1014 1015 func TestOutputStderrCapture(t *testing.T) { 1016 testenv.MustHaveExec(t) 1017 1018 cmd := helperCommand(t, "stderrfail") 1019 _, err := cmd.Output() 1020 ee, ok := err.(*exec.ExitError) 1021 if !ok { 1022 t.Fatalf("Output error type = %T; want ExitError", err) 1023 } 1024 got := string(ee.Stderr) 1025 want := "some stderr text\n" 1026 if got != want { 1027 t.Errorf("ExitError.Stderr = %q; want %q", got, want) 1028 } 1029 } 1030 1031 func TestContext(t *testing.T) { 1032 ctx, cancel := context.WithCancel(context.Background()) 1033 c := helperCommandContext(t, ctx, "pipetest") 1034 stdin, err := c.StdinPipe() 1035 if err != nil { 1036 t.Fatal(err) 1037 } 1038 stdout, err := c.StdoutPipe() 1039 if err != nil { 1040 t.Fatal(err) 1041 } 1042 if err := c.Start(); err != nil { 1043 t.Fatal(err) 1044 } 1045 1046 if _, err := stdin.Write([]byte("O:hi\n")); err != nil { 1047 t.Fatal(err) 1048 } 1049 buf := make([]byte, 5) 1050 n, err := io.ReadFull(stdout, buf) 1051 if n != len(buf) || err != nil || string(buf) != "O:hi\n" { 1052 t.Fatalf("ReadFull = %d, %v, %q", n, err, buf[:n]) 1053 } 1054 waitErr := make(chan error, 1) 1055 go func() { 1056 waitErr <- c.Wait() 1057 }() 1058 cancel() 1059 select { 1060 case err := <-waitErr: 1061 if err == nil { 1062 t.Fatal("expected Wait failure") 1063 } 1064 case <-time.After(3 * time.Second): 1065 t.Fatal("timeout waiting for child process death") 1066 } 1067 } 1068 1069 func TestContextCancel(t *testing.T) { 1070 ctx, cancel := context.WithCancel(context.Background()) 1071 defer cancel() 1072 c := helperCommandContext(t, ctx, "cat") 1073 1074 r, w, err := os.Pipe() 1075 if err != nil { 1076 t.Fatal(err) 1077 } 1078 c.Stdin = r 1079 1080 stdout, err := c.StdoutPipe() 1081 if err != nil { 1082 t.Fatal(err) 1083 } 1084 readDone := make(chan struct{}) 1085 go func() { 1086 defer close(readDone) 1087 var a [1024]byte 1088 for { 1089 n, err := stdout.Read(a[:]) 1090 if err != nil { 1091 if err != io.EOF { 1092 t.Errorf("unexpected read error: %v", err) 1093 } 1094 return 1095 } 1096 t.Logf("%s", a[:n]) 1097 } 1098 }() 1099 1100 if err := c.Start(); err != nil { 1101 t.Fatal(err) 1102 } 1103 1104 if err := r.Close(); err != nil { 1105 t.Fatal(err) 1106 } 1107 1108 if _, err := io.WriteString(w, "echo"); err != nil { 1109 t.Fatal(err) 1110 } 1111 1112 cancel() 1113 1114 // Calling cancel should have killed the process, so writes 1115 // should now fail. Give the process a little while to die. 1116 start := time.Now() 1117 for { 1118 if _, err := io.WriteString(w, "echo"); err != nil { 1119 break 1120 } 1121 if time.Since(start) > time.Second { 1122 t.Fatal("canceling context did not stop program") 1123 } 1124 time.Sleep(time.Millisecond) 1125 } 1126 1127 if err := w.Close(); err != nil { 1128 t.Errorf("error closing write end of pipe: %v", err) 1129 } 1130 <-readDone 1131 1132 if err := c.Wait(); err == nil { 1133 t.Error("program unexpectedly exited successfully") 1134 } else { 1135 t.Logf("exit status: %v", err) 1136 } 1137 } 1138 1139 // test that environment variables are de-duped. 1140 func TestDedupEnvEcho(t *testing.T) { 1141 testenv.MustHaveExec(t) 1142 1143 cmd := helperCommand(t, "echoenv", "FOO") 1144 cmd.Env = append(cmd.Env, "FOO=bad", "FOO=good") 1145 out, err := cmd.CombinedOutput() 1146 if err != nil { 1147 t.Fatal(err) 1148 } 1149 if got, want := strings.TrimSpace(string(out)), "good"; got != want { 1150 t.Errorf("output = %q; want %q", got, want) 1151 } 1152 }