github.com/remobjects/goldbaselibrary@v0.0.0-20230924164425-d458680a936b/Source/Gold/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 if testing.Short() && i >= 3 { 695 break 696 } 697 la := listen() 698 ca := helperCommand(t, "describefiles") 699 ca.ExtraFiles = []*os.File{listenerFile(la)} 700 lb := listen() 701 cb := helperCommand(t, "describefiles") 702 cb.ExtraFiles = []*os.File{listenerFile(lb)} 703 ares := make(chan string) 704 bres := make(chan string) 705 go runCommand(ca, ares) 706 go runCommand(cb, bres) 707 if got, want := <-ares, fmt.Sprintf("fd3: listener %s\n", la.Addr()); got != want { 708 t.Errorf("iteration %d, process A got:\n%s\nwant:\n%s\n", i, got, want) 709 } 710 if got, want := <-bres, fmt.Sprintf("fd3: listener %s\n", lb.Addr()); got != want { 711 t.Errorf("iteration %d, process B got:\n%s\nwant:\n%s\n", i, got, want) 712 } 713 la.Close() 714 lb.Close() 715 for _, f := range ca.ExtraFiles { 716 f.Close() 717 } 718 for _, f := range cb.ExtraFiles { 719 f.Close() 720 } 721 722 } 723 } 724 725 // TestHelperProcess isn't a real test. It's used as a helper process 726 // for TestParameterRun. 727 func TestHelperProcess(*testing.T) { 728 if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" { 729 return 730 } 731 defer os.Exit(0) 732 733 // Determine which command to use to display open files. 734 ofcmd := "lsof" 735 switch runtime.GOOS { 736 case "dragonfly", "freebsd", "netbsd", "openbsd": 737 ofcmd = "fstat" 738 case "plan9": 739 ofcmd = "/bin/cat" 740 case "aix": 741 ofcmd = "procfiles" 742 } 743 744 args := os.Args 745 for len(args) > 0 { 746 if args[0] == "--" { 747 args = args[1:] 748 break 749 } 750 args = args[1:] 751 } 752 if len(args) == 0 { 753 fmt.Fprintf(os.Stderr, "No command\n") 754 os.Exit(2) 755 } 756 757 cmd, args := args[0], args[1:] 758 switch cmd { 759 case "echo": 760 iargs := []interface{}{} 761 for _, s := range args { 762 iargs = append(iargs, s) 763 } 764 fmt.Println(iargs...) 765 case "echoenv": 766 for _, s := range args { 767 fmt.Println(os.Getenv(s)) 768 } 769 os.Exit(0) 770 case "cat": 771 if len(args) == 0 { 772 io.Copy(os.Stdout, os.Stdin) 773 return 774 } 775 exit := 0 776 for _, fn := range args { 777 f, err := os.Open(fn) 778 if err != nil { 779 fmt.Fprintf(os.Stderr, "Error: %v\n", err) 780 exit = 2 781 } else { 782 defer f.Close() 783 io.Copy(os.Stdout, f) 784 } 785 } 786 os.Exit(exit) 787 case "pipetest": 788 bufr := bufio.NewReader(os.Stdin) 789 for { 790 line, _, err := bufr.ReadLine() 791 if err == io.EOF { 792 break 793 } else if err != nil { 794 os.Exit(1) 795 } 796 if bytes.HasPrefix(line, []byte("O:")) { 797 os.Stdout.Write(line) 798 os.Stdout.Write([]byte{'\n'}) 799 } else if bytes.HasPrefix(line, []byte("E:")) { 800 os.Stderr.Write(line) 801 os.Stderr.Write([]byte{'\n'}) 802 } else { 803 os.Exit(1) 804 } 805 } 806 case "stdinClose": 807 b, err := ioutil.ReadAll(os.Stdin) 808 if err != nil { 809 fmt.Fprintf(os.Stderr, "Error: %v\n", err) 810 os.Exit(1) 811 } 812 if s := string(b); s != stdinCloseTestString { 813 fmt.Fprintf(os.Stderr, "Error: Read %q, want %q", s, stdinCloseTestString) 814 os.Exit(1) 815 } 816 os.Exit(0) 817 case "read3": // read fd 3 818 fd3 := os.NewFile(3, "fd3") 819 bs, err := ioutil.ReadAll(fd3) 820 if err != nil { 821 fmt.Printf("ReadAll from fd 3: %v", err) 822 os.Exit(1) 823 } 824 switch runtime.GOOS { 825 case "dragonfly": 826 // TODO(jsing): Determine why DragonFly is leaking 827 // file descriptors... 828 case "darwin": 829 // TODO(bradfitz): broken? Sometimes. 830 // https://golang.org/issue/2603 831 // Skip this additional part of the test for now. 832 case "netbsd": 833 // TODO(jsing): This currently fails on NetBSD due to 834 // the cloned file descriptors that result from opening 835 // /dev/urandom. 836 // https://golang.org/issue/3955 837 case "illumos", "solaris": 838 // TODO(aram): This fails on Solaris because libc opens 839 // its own files, as it sees fit. Darwin does the same, 840 // see: https://golang.org/issue/2603 841 default: 842 // Now verify that there are no other open fds. 843 var files []*os.File 844 for wantfd := basefds() + 1; wantfd <= 100; wantfd++ { 845 if poll.IsPollDescriptor(wantfd) { 846 continue 847 } 848 f, err := os.Open(os.Args[0]) 849 if err != nil { 850 fmt.Printf("error opening file with expected fd %d: %v", wantfd, err) 851 os.Exit(1) 852 } 853 if got := f.Fd(); got != wantfd { 854 fmt.Printf("leaked parent file. fd = %d; want %d\n", got, wantfd) 855 var args []string 856 switch runtime.GOOS { 857 case "plan9": 858 args = []string{fmt.Sprintf("/proc/%d/fd", os.Getpid())} 859 case "aix": 860 args = []string{fmt.Sprint(os.Getpid())} 861 default: 862 args = []string{"-p", fmt.Sprint(os.Getpid())} 863 } 864 out, _ := exec.Command(ofcmd, args...).CombinedOutput() 865 fmt.Print(string(out)) 866 os.Exit(1) 867 } 868 files = append(files, f) 869 } 870 for _, f := range files { 871 f.Close() 872 } 873 } 874 // Referring to fd3 here ensures that it is not 875 // garbage collected, and therefore closed, while 876 // executing the wantfd loop above. It doesn't matter 877 // what we do with fd3 as long as we refer to it; 878 // closing it is the easy choice. 879 fd3.Close() 880 os.Stdout.Write(bs) 881 case "exit": 882 n, _ := strconv.Atoi(args[0]) 883 os.Exit(n) 884 case "describefiles": 885 f := os.NewFile(3, fmt.Sprintf("fd3")) 886 ln, err := net.FileListener(f) 887 if err == nil { 888 fmt.Printf("fd3: listener %s\n", ln.Addr()) 889 ln.Close() 890 } 891 os.Exit(0) 892 case "extraFilesAndPipes": 893 n, _ := strconv.Atoi(args[0]) 894 pipes := make([]*os.File, n) 895 for i := 0; i < n; i++ { 896 pipes[i] = os.NewFile(uintptr(3+i), strconv.Itoa(i)) 897 } 898 response := "" 899 for i, r := range pipes { 900 ch := make(chan string, 1) 901 go func(c chan string) { 902 buf := make([]byte, 10) 903 n, err := r.Read(buf) 904 if err != nil { 905 fmt.Fprintf(os.Stderr, "Child: read error: %v on pipe %d\n", err, i) 906 os.Exit(1) 907 } 908 c <- string(buf[:n]) 909 close(c) 910 }(ch) 911 select { 912 case m := <-ch: 913 response = response + m 914 case <-time.After(5 * time.Second): 915 fmt.Fprintf(os.Stderr, "Child: Timeout reading from pipe: %d\n", i) 916 os.Exit(1) 917 } 918 } 919 fmt.Fprintf(os.Stderr, "child: %s", response) 920 os.Exit(0) 921 case "exec": 922 cmd := exec.Command(args[1]) 923 cmd.Dir = args[0] 924 output, err := cmd.CombinedOutput() 925 if err != nil { 926 fmt.Fprintf(os.Stderr, "Child: %s %s", err, string(output)) 927 os.Exit(1) 928 } 929 fmt.Printf("%s", string(output)) 930 os.Exit(0) 931 case "lookpath": 932 p, err := exec.LookPath(args[0]) 933 if err != nil { 934 fmt.Fprintf(os.Stderr, "LookPath failed: %v\n", err) 935 os.Exit(1) 936 } 937 fmt.Print(p) 938 os.Exit(0) 939 case "stderrfail": 940 fmt.Fprintf(os.Stderr, "some stderr text\n") 941 os.Exit(1) 942 case "sleep": 943 time.Sleep(3 * time.Second) 944 os.Exit(0) 945 default: 946 fmt.Fprintf(os.Stderr, "Unknown command %q\n", cmd) 947 os.Exit(2) 948 } 949 } 950 951 type delayedInfiniteReader struct{} 952 953 func (delayedInfiniteReader) Read(b []byte) (int, error) { 954 time.Sleep(100 * time.Millisecond) 955 for i := range b { 956 b[i] = 'x' 957 } 958 return len(b), nil 959 } 960 961 // Issue 9173: ignore stdin pipe writes if the program completes successfully. 962 func TestIgnorePipeErrorOnSuccess(t *testing.T) { 963 testenv.MustHaveExec(t) 964 965 // We really only care about testing this on Unixy and Windowsy things. 966 if runtime.GOOS == "plan9" { 967 t.Skipf("skipping test on %q", runtime.GOOS) 968 } 969 970 testWith := func(r io.Reader) func(*testing.T) { 971 return func(t *testing.T) { 972 cmd := helperCommand(t, "echo", "foo") 973 var out bytes.Buffer 974 cmd.Stdin = r 975 cmd.Stdout = &out 976 if err := cmd.Run(); err != nil { 977 t.Fatal(err) 978 } 979 if got, want := out.String(), "foo\n"; got != want { 980 t.Errorf("output = %q; want %q", got, want) 981 } 982 } 983 } 984 t.Run("10MB", testWith(strings.NewReader(strings.Repeat("x", 10<<20)))) 985 t.Run("Infinite", testWith(delayedInfiniteReader{})) 986 } 987 988 type badWriter struct{} 989 990 func (w *badWriter) Write(data []byte) (int, error) { 991 return 0, io.ErrUnexpectedEOF 992 } 993 994 func TestClosePipeOnCopyError(t *testing.T) { 995 testenv.MustHaveExec(t) 996 997 if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { 998 t.Skipf("skipping test on %s - no yes command", runtime.GOOS) 999 } 1000 cmd := exec.Command("yes") 1001 cmd.Stdout = new(badWriter) 1002 c := make(chan int, 1) 1003 go func() { 1004 err := cmd.Run() 1005 if err == nil { 1006 t.Errorf("yes completed successfully") 1007 } 1008 c <- 1 1009 }() 1010 select { 1011 case <-c: 1012 // ok 1013 case <-time.After(5 * time.Second): 1014 t.Fatalf("yes got stuck writing to bad writer") 1015 } 1016 } 1017 1018 func TestOutputStderrCapture(t *testing.T) { 1019 testenv.MustHaveExec(t) 1020 1021 cmd := helperCommand(t, "stderrfail") 1022 _, err := cmd.Output() 1023 ee, ok := err.(*exec.ExitError) 1024 if !ok { 1025 t.Fatalf("Output error type = %T; want ExitError", err) 1026 } 1027 got := string(ee.Stderr) 1028 want := "some stderr text\n" 1029 if got != want { 1030 t.Errorf("ExitError.Stderr = %q; want %q", got, want) 1031 } 1032 } 1033 1034 func TestContext(t *testing.T) { 1035 ctx, cancel := context.WithCancel(context.Background()) 1036 c := helperCommandContext(t, ctx, "pipetest") 1037 stdin, err := c.StdinPipe() 1038 if err != nil { 1039 t.Fatal(err) 1040 } 1041 stdout, err := c.StdoutPipe() 1042 if err != nil { 1043 t.Fatal(err) 1044 } 1045 if err := c.Start(); err != nil { 1046 t.Fatal(err) 1047 } 1048 1049 if _, err := stdin.Write([]byte("O:hi\n")); err != nil { 1050 t.Fatal(err) 1051 } 1052 buf := make([]byte, 5) 1053 n, err := io.ReadFull(stdout, buf) 1054 if n != len(buf) || err != nil || string(buf) != "O:hi\n" { 1055 t.Fatalf("ReadFull = %d, %v, %q", n, err, buf[:n]) 1056 } 1057 waitErr := make(chan error, 1) 1058 go func() { 1059 waitErr <- c.Wait() 1060 }() 1061 cancel() 1062 select { 1063 case err := <-waitErr: 1064 if err == nil { 1065 t.Fatal("expected Wait failure") 1066 } 1067 case <-time.After(3 * time.Second): 1068 t.Fatal("timeout waiting for child process death") 1069 } 1070 } 1071 1072 func TestContextCancel(t *testing.T) { 1073 ctx, cancel := context.WithCancel(context.Background()) 1074 defer cancel() 1075 c := helperCommandContext(t, ctx, "cat") 1076 1077 r, w, err := os.Pipe() 1078 if err != nil { 1079 t.Fatal(err) 1080 } 1081 c.Stdin = r 1082 1083 stdout, err := c.StdoutPipe() 1084 if err != nil { 1085 t.Fatal(err) 1086 } 1087 readDone := make(chan struct{}) 1088 go func() { 1089 defer close(readDone) 1090 var a [1024]byte 1091 for { 1092 n, err := stdout.Read(a[:]) 1093 if err != nil { 1094 if err != io.EOF { 1095 t.Errorf("unexpected read error: %v", err) 1096 } 1097 return 1098 } 1099 t.Logf("%s", a[:n]) 1100 } 1101 }() 1102 1103 if err := c.Start(); err != nil { 1104 t.Fatal(err) 1105 } 1106 1107 if err := r.Close(); err != nil { 1108 t.Fatal(err) 1109 } 1110 1111 if _, err := io.WriteString(w, "echo"); err != nil { 1112 t.Fatal(err) 1113 } 1114 1115 cancel() 1116 1117 // Calling cancel should have killed the process, so writes 1118 // should now fail. Give the process a little while to die. 1119 start := time.Now() 1120 for { 1121 if _, err := io.WriteString(w, "echo"); err != nil { 1122 break 1123 } 1124 if time.Since(start) > time.Second { 1125 t.Fatal("canceling context did not stop program") 1126 } 1127 time.Sleep(time.Millisecond) 1128 } 1129 1130 if err := w.Close(); err != nil { 1131 t.Errorf("error closing write end of pipe: %v", err) 1132 } 1133 <-readDone 1134 1135 if err := c.Wait(); err == nil { 1136 t.Error("program unexpectedly exited successfully") 1137 } else { 1138 t.Logf("exit status: %v", err) 1139 } 1140 } 1141 1142 // test that environment variables are de-duped. 1143 func TestDedupEnvEcho(t *testing.T) { 1144 testenv.MustHaveExec(t) 1145 1146 cmd := helperCommand(t, "echoenv", "FOO") 1147 cmd.Env = append(cmd.Env, "FOO=bad", "FOO=good") 1148 out, err := cmd.CombinedOutput() 1149 if err != nil { 1150 t.Fatal(err) 1151 } 1152 if got, want := strings.TrimSpace(string(out)), "good"; got != want { 1153 t.Errorf("output = %q; want %q", got, want) 1154 } 1155 } 1156 1157 func TestString(t *testing.T) { 1158 echoPath, err := exec.LookPath("echo") 1159 if err != nil { 1160 t.Skip(err) 1161 } 1162 tests := [...]struct { 1163 path string 1164 args []string 1165 want string 1166 }{ 1167 {"echo", nil, echoPath}, 1168 {"echo", []string{"a"}, echoPath + " a"}, 1169 {"echo", []string{"a", "b"}, echoPath + " a b"}, 1170 } 1171 for _, test := range tests { 1172 cmd := exec.Command(test.path, test.args...) 1173 if got := cmd.String(); got != test.want { 1174 t.Errorf("String(%q, %q) = %q, want %q", test.path, test.args, got, test.want) 1175 } 1176 } 1177 } 1178 1179 func TestStringPathNotResolved(t *testing.T) { 1180 _, err := exec.LookPath("makemeasandwich") 1181 if err == nil { 1182 t.Skip("wow, thanks") 1183 } 1184 cmd := exec.Command("makemeasandwich", "-lettuce") 1185 want := "makemeasandwich -lettuce" 1186 if got := cmd.String(); got != want { 1187 t.Errorf("String(%q, %q) = %q, want %q", "makemeasandwich", "-lettuce", got, want) 1188 } 1189 } 1190 1191 // start a child process without the user code explicitly starting 1192 // with a copy of the parent's. (The Windows SYSTEMROOT issue: Issue 1193 // 25210) 1194 func TestChildCriticalEnv(t *testing.T) { 1195 testenv.MustHaveExec(t) 1196 if runtime.GOOS != "windows" { 1197 t.Skip("only testing on Windows") 1198 } 1199 cmd := helperCommand(t, "echoenv", "SYSTEMROOT") 1200 cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"} 1201 out, err := cmd.CombinedOutput() 1202 if err != nil { 1203 t.Fatal(err) 1204 } 1205 if strings.TrimSpace(string(out)) == "" { 1206 t.Error("no SYSTEMROOT found") 1207 } 1208 }