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