github.com/gidoBOSSftw5731/go/src@v0.0.0-20210226122457-d24b0edbf019/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 t.Skip("flaky test; see https://golang.org/issue/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 default: 919 fmt.Fprintf(os.Stderr, "Unknown command %q\n", cmd) 920 os.Exit(2) 921 } 922 } 923 924 type delayedInfiniteReader struct{} 925 926 func (delayedInfiniteReader) Read(b []byte) (int, error) { 927 time.Sleep(100 * time.Millisecond) 928 for i := range b { 929 b[i] = 'x' 930 } 931 return len(b), nil 932 } 933 934 // Issue 9173: ignore stdin pipe writes if the program completes successfully. 935 func TestIgnorePipeErrorOnSuccess(t *testing.T) { 936 testenv.MustHaveExec(t) 937 938 testWith := func(r io.Reader) func(*testing.T) { 939 return func(t *testing.T) { 940 cmd := helperCommand(t, "echo", "foo") 941 var out bytes.Buffer 942 cmd.Stdin = r 943 cmd.Stdout = &out 944 if err := cmd.Run(); err != nil { 945 t.Fatal(err) 946 } 947 if got, want := out.String(), "foo\n"; got != want { 948 t.Errorf("output = %q; want %q", got, want) 949 } 950 } 951 } 952 t.Run("10MB", testWith(strings.NewReader(strings.Repeat("x", 10<<20)))) 953 t.Run("Infinite", testWith(delayedInfiniteReader{})) 954 } 955 956 type badWriter struct{} 957 958 func (w *badWriter) Write(data []byte) (int, error) { 959 return 0, io.ErrUnexpectedEOF 960 } 961 962 func TestClosePipeOnCopyError(t *testing.T) { 963 testenv.MustHaveExec(t) 964 965 if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { 966 t.Skipf("skipping test on %s - no yes command", runtime.GOOS) 967 } 968 cmd := exec.Command("yes") 969 cmd.Stdout = new(badWriter) 970 c := make(chan int, 1) 971 go func() { 972 err := cmd.Run() 973 if err == nil { 974 t.Errorf("yes completed successfully") 975 } 976 c <- 1 977 }() 978 select { 979 case <-c: 980 // ok 981 case <-time.After(5 * time.Second): 982 t.Fatalf("yes got stuck writing to bad writer") 983 } 984 } 985 986 func TestOutputStderrCapture(t *testing.T) { 987 testenv.MustHaveExec(t) 988 989 cmd := helperCommand(t, "stderrfail") 990 _, err := cmd.Output() 991 ee, ok := err.(*exec.ExitError) 992 if !ok { 993 t.Fatalf("Output error type = %T; want ExitError", err) 994 } 995 got := string(ee.Stderr) 996 want := "some stderr text\n" 997 if got != want { 998 t.Errorf("ExitError.Stderr = %q; want %q", got, want) 999 } 1000 } 1001 1002 func TestContext(t *testing.T) { 1003 ctx, cancel := context.WithCancel(context.Background()) 1004 c := helperCommandContext(t, ctx, "pipetest") 1005 stdin, err := c.StdinPipe() 1006 if err != nil { 1007 t.Fatal(err) 1008 } 1009 stdout, err := c.StdoutPipe() 1010 if err != nil { 1011 t.Fatal(err) 1012 } 1013 if err := c.Start(); err != nil { 1014 t.Fatal(err) 1015 } 1016 1017 if _, err := stdin.Write([]byte("O:hi\n")); err != nil { 1018 t.Fatal(err) 1019 } 1020 buf := make([]byte, 5) 1021 n, err := io.ReadFull(stdout, buf) 1022 if n != len(buf) || err != nil || string(buf) != "O:hi\n" { 1023 t.Fatalf("ReadFull = %d, %v, %q", n, err, buf[:n]) 1024 } 1025 waitErr := make(chan error, 1) 1026 go func() { 1027 waitErr <- c.Wait() 1028 }() 1029 cancel() 1030 select { 1031 case err := <-waitErr: 1032 if err == nil { 1033 t.Fatal("expected Wait failure") 1034 } 1035 case <-time.After(3 * time.Second): 1036 t.Fatal("timeout waiting for child process death") 1037 } 1038 } 1039 1040 func TestContextCancel(t *testing.T) { 1041 ctx, cancel := context.WithCancel(context.Background()) 1042 defer cancel() 1043 c := helperCommandContext(t, ctx, "cat") 1044 1045 r, w, err := os.Pipe() 1046 if err != nil { 1047 t.Fatal(err) 1048 } 1049 c.Stdin = r 1050 1051 stdout, err := c.StdoutPipe() 1052 if err != nil { 1053 t.Fatal(err) 1054 } 1055 readDone := make(chan struct{}) 1056 go func() { 1057 defer close(readDone) 1058 var a [1024]byte 1059 for { 1060 n, err := stdout.Read(a[:]) 1061 if err != nil { 1062 if err != io.EOF { 1063 t.Errorf("unexpected read error: %v", err) 1064 } 1065 return 1066 } 1067 t.Logf("%s", a[:n]) 1068 } 1069 }() 1070 1071 if err := c.Start(); err != nil { 1072 t.Fatal(err) 1073 } 1074 1075 if err := r.Close(); err != nil { 1076 t.Fatal(err) 1077 } 1078 1079 if _, err := io.WriteString(w, "echo"); err != nil { 1080 t.Fatal(err) 1081 } 1082 1083 cancel() 1084 1085 // Calling cancel should have killed the process, so writes 1086 // should now fail. Give the process a little while to die. 1087 start := time.Now() 1088 for { 1089 if _, err := io.WriteString(w, "echo"); err != nil { 1090 break 1091 } 1092 if time.Since(start) > time.Second { 1093 t.Fatal("canceling context did not stop program") 1094 } 1095 time.Sleep(time.Millisecond) 1096 } 1097 1098 if err := w.Close(); err != nil { 1099 t.Errorf("error closing write end of pipe: %v", err) 1100 } 1101 <-readDone 1102 1103 if err := c.Wait(); err == nil { 1104 t.Error("program unexpectedly exited successfully") 1105 } else { 1106 t.Logf("exit status: %v", err) 1107 } 1108 } 1109 1110 // test that environment variables are de-duped. 1111 func TestDedupEnvEcho(t *testing.T) { 1112 testenv.MustHaveExec(t) 1113 1114 cmd := helperCommand(t, "echoenv", "FOO") 1115 cmd.Env = append(cmd.Env, "FOO=bad", "FOO=good") 1116 out, err := cmd.CombinedOutput() 1117 if err != nil { 1118 t.Fatal(err) 1119 } 1120 if got, want := strings.TrimSpace(string(out)), "good"; got != want { 1121 t.Errorf("output = %q; want %q", got, want) 1122 } 1123 } 1124 1125 func TestString(t *testing.T) { 1126 echoPath, err := exec.LookPath("echo") 1127 if err != nil { 1128 t.Skip(err) 1129 } 1130 tests := [...]struct { 1131 path string 1132 args []string 1133 want string 1134 }{ 1135 {"echo", nil, echoPath}, 1136 {"echo", []string{"a"}, echoPath + " a"}, 1137 {"echo", []string{"a", "b"}, echoPath + " a b"}, 1138 } 1139 for _, test := range tests { 1140 cmd := exec.Command(test.path, test.args...) 1141 if got := cmd.String(); got != test.want { 1142 t.Errorf("String(%q, %q) = %q, want %q", test.path, test.args, got, test.want) 1143 } 1144 } 1145 } 1146 1147 func TestStringPathNotResolved(t *testing.T) { 1148 _, err := exec.LookPath("makemeasandwich") 1149 if err == nil { 1150 t.Skip("wow, thanks") 1151 } 1152 cmd := exec.Command("makemeasandwich", "-lettuce") 1153 want := "makemeasandwich -lettuce" 1154 if got := cmd.String(); got != want { 1155 t.Errorf("String(%q, %q) = %q, want %q", "makemeasandwich", "-lettuce", got, want) 1156 } 1157 } 1158 1159 // start a child process without the user code explicitly starting 1160 // with a copy of the parent's. (The Windows SYSTEMROOT issue: Issue 1161 // 25210) 1162 func TestChildCriticalEnv(t *testing.T) { 1163 testenv.MustHaveExec(t) 1164 if runtime.GOOS != "windows" { 1165 t.Skip("only testing on Windows") 1166 } 1167 cmd := helperCommand(t, "echoenv", "SYSTEMROOT") 1168 cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"} 1169 out, err := cmd.CombinedOutput() 1170 if err != nil { 1171 t.Fatal(err) 1172 } 1173 if strings.TrimSpace(string(out)) == "" { 1174 t.Error("no SYSTEMROOT found") 1175 } 1176 }