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.
5 // Use an external test to avoid os/exec -> net/http -> crypto/x509 -> os/exec
6 // circular dependency on non-cgo darwin.
33 func helperCommandContext(t
*testing
.T
, ctx context
.Context
, s
...string) (cmd
*exec
.Cmd
) {
34 testenv
.MustHaveExec(t
)
36 cs
:= []string{"-test.run=TestHelperProcess", "--"}
39 cmd
= exec
.CommandContext(ctx
, os
.Args
[0], cs
...)
41 cmd
= exec
.Command(os
.Args
[0], cs
...)
43 cmd
.Env
= []string{"GO_WANT_HELPER_PROCESS=1"}
44 path
:= os
.Getenv("LD_LIBRARY_PATH")
46 cmd
.Env
= append(cmd
.Env
, "LD_LIBRARY_PATH="+path
)
51 func helperCommand(t
*testing
.T
, s
...string) *exec
.Cmd
{
52 return helperCommandContext(t
, nil, s
...)
55 func TestEcho(t
*testing
.T
) {
56 bs
, err
:= helperCommand(t
, "echo", "foo bar", "baz").Output()
58 t
.Errorf("echo: %v", err
)
60 if g
, e
:= string(bs
), "foo bar baz\n"; g
!= e
{
61 t
.Errorf("echo: want %q, got %q", e
, g
)
65 func TestCommandRelativeName(t
*testing
.T
) {
66 testenv
.MustHaveExec(t
)
68 // Run our own binary as a relative path
69 // (e.g. "_test/exec.test") our parent directory.
70 base
:= filepath
.Base(os
.Args
[0]) // "exec.test"
71 dir
:= filepath
.Dir(os
.Args
[0]) // "/tmp/go-buildNNNN/os/exec/_test"
73 t
.Skip("skipping; running test at root somehow")
75 parentDir
:= filepath
.Dir(dir
) // "/tmp/go-buildNNNN/os/exec"
76 dirBase
:= filepath
.Base(dir
) // "_test"
78 t
.Skipf("skipping; unexpected shallow dir of %q", dir
)
81 cmd
:= exec
.Command(filepath
.Join(dirBase
, base
), "-test.run=TestHelperProcess", "--", "echo", "foo")
83 cmd
.Env
= []string{"GO_WANT_HELPER_PROCESS=1"}
85 out
, err
:= cmd
.Output()
87 t
.Errorf("echo: %v", err
)
89 if g
, e
:= string(out
), "foo\n"; g
!= e
{
90 t
.Errorf("echo: want %q, got %q", e
, g
)
94 func TestCatStdin(t
*testing
.T
) {
95 // Cat, testing stdin and stdout.
96 input
:= "Input string\nLine 2"
97 p
:= helperCommand(t
, "cat")
98 p
.Stdin
= strings
.NewReader(input
)
101 t
.Errorf("cat: %v", err
)
105 t
.Errorf("cat: want %q, got %q", input
, s
)
109 func TestEchoFileRace(t
*testing
.T
) {
110 cmd
:= helperCommand(t
, "echo")
111 stdin
, err
:= cmd
.StdinPipe()
113 t
.Fatalf("StdinPipe: %v", err
)
115 if err
:= cmd
.Start(); err
!= nil {
116 t
.Fatalf("Start: %v", err
)
118 wrote
:= make(chan bool)
121 fmt
.Fprint(stdin
, "echo\n")
123 if err
:= cmd
.Wait(); err
!= nil {
124 t
.Fatalf("Wait: %v", err
)
129 func TestCatGoodAndBadFile(t
*testing
.T
) {
130 // Testing combined output and error values.
131 bs
, err
:= helperCommand(t
, "cat", "/bogus/file.foo", "exec_test.go").CombinedOutput()
132 if _
, ok
:= err
.(*exec
.ExitError
); !ok
{
133 t
.Errorf("expected *exec.ExitError from cat combined; got %T: %v", err
, err
)
136 sp
:= strings
.SplitN(s
, "\n", 2)
138 t
.Fatalf("expected two lines from cat; got %q", s
)
140 errLine
, body
:= sp
[0], sp
[1]
141 if !strings
.HasPrefix(errLine
, "Error: open /bogus/file.foo") {
142 t
.Errorf("expected stderr to complain about file; got %q", errLine
)
144 if !strings
.Contains(body
, "func TestHelperProcess(t *testing.T)") {
145 t
.Errorf("expected test code; got %q (len %d)", body
, len(body
))
149 func TestNoExistExecutable(t
*testing
.T
) {
150 // Can't run a non-existent executable
151 err
:= exec
.Command("/no-exist-executable").Run()
153 t
.Error("expected error from /no-exist-executable")
157 func TestExitStatus(t
*testing
.T
) {
158 // Test that exit values are returned correctly
159 cmd
:= helperCommand(t
, "exit", "42")
161 want
:= "exit status 42"
162 switch runtime
.GOOS
{
164 want
= fmt
.Sprintf("exit status: '%s %d: 42'", filepath
.Base(cmd
.Path
), cmd
.ProcessState
.Pid())
166 if werr
, ok
:= err
.(*exec
.ExitError
); ok
{
167 if s
:= werr
.Error(); s
!= want
{
168 t
.Errorf("from exit 42 got exit %q, want %q", s
, want
)
171 t
.Fatalf("expected *exec.ExitError from exit 42; got %T: %v", err
, err
)
175 func TestPipes(t
*testing
.T
) {
176 check
:= func(what
string, err error
) {
178 t
.Fatalf("%s: %v", what
, err
)
181 // Cat, testing stdin and stdout.
182 c
:= helperCommand(t
, "pipetest")
183 stdin
, err
:= c
.StdinPipe()
184 check("StdinPipe", err
)
185 stdout
, err
:= c
.StdoutPipe()
186 check("StdoutPipe", err
)
187 stderr
, err
:= c
.StderrPipe()
188 check("StderrPipe", err
)
190 outbr
:= bufio
.NewReader(stdout
)
191 errbr
:= bufio
.NewReader(stderr
)
192 line
:= func(what
string, br
*bufio
.Reader
) string {
193 line
, _
, err
:= br
.ReadLine()
195 t
.Fatalf("%s: %v", what
, err
)
203 _
, err
= stdin
.Write([]byte("O:I am output\n"))
204 check("first stdin Write", err
)
205 if g
, e
:= line("first output line", outbr
), "O:I am output"; g
!= e
{
206 t
.Errorf("got %q, want %q", g
, e
)
209 _
, err
= stdin
.Write([]byte("E:I am error\n"))
210 check("second stdin Write", err
)
211 if g
, e
:= line("first error line", errbr
), "E:I am error"; g
!= e
{
212 t
.Errorf("got %q, want %q", g
, e
)
215 _
, err
= stdin
.Write([]byte("O:I am output2\n"))
216 check("third stdin Write 3", err
)
217 if g
, e
:= line("second output line", outbr
), "O:I am output2"; g
!= e
{
218 t
.Errorf("got %q, want %q", g
, e
)
226 const stdinCloseTestString
= "Some test string."
229 func TestStdinClose(t
*testing
.T
) {
230 check
:= func(what
string, err error
) {
232 t
.Fatalf("%s: %v", what
, err
)
235 cmd
:= helperCommand(t
, "stdinClose")
236 stdin
, err
:= cmd
.StdinPipe()
237 check("StdinPipe", err
)
238 // Check that we can access methods of the underlying os.File.`
239 if _
, ok
:= stdin
.(interface {
242 t
.Error("can't access methods of underlying *os.File")
244 check("Start", cmd
.Start())
246 _
, err
:= io
.Copy(stdin
, strings
.NewReader(stdinCloseTestString
))
248 // Before the fix, this next line would race with cmd.Wait.
249 check("Close", stdin
.Close())
251 check("Wait", cmd
.Wait())
255 // It used to be the case that TestStdinClose, above, would fail when
256 // run under the race detector. This test is a variant of TestStdinClose
257 // that also used to fail when run under the race detector.
258 // This test is run by cmd/dist under the race detector to verify that
259 // the race detector no longer reports any problems.
260 func TestStdinCloseRace(t
*testing
.T
) {
261 cmd
:= helperCommand(t
, "stdinClose")
262 stdin
, err
:= cmd
.StdinPipe()
264 t
.Fatalf("StdinPipe: %v", err
)
266 if err
:= cmd
.Start(); err
!= nil {
267 t
.Fatalf("Start: %v", err
)
270 // We don't check the error return of Kill. It is
271 // possible that the process has already exited, in
272 // which case Kill will return an error "process
273 // already finished". The purpose of this test is to
274 // see whether the race detector reports an error; it
275 // doesn't matter whether this Kill succeeds or not.
279 // Send the wrong string, so that the child fails even
280 // if the other goroutine doesn't manage to kill it first.
281 // This test is to check that the race detector does not
282 // falsely report an error, so it doesn't matter how the
283 // child process fails.
284 io
.Copy(stdin
, strings
.NewReader("unexpected string"))
285 if err
:= stdin
.Close(); err
!= nil {
286 t
.Errorf("stdin.Close: %v", err
)
289 if err
:= cmd
.Wait(); err
== nil {
290 t
.Fatalf("Wait: succeeded unexpectedly")
295 func TestPipeLookPathLeak(t
*testing
.T
) {
296 // If we are reading from /proc/self/fd we (should) get an exact result.
299 // Reading /proc/self/fd is more reliable than calling lsof, so try that
301 numOpenFDs
:= func() (int, []byte, error
) {
302 fds
, err
:= ioutil
.ReadDir("/proc/self/fd")
306 return len(fds
), nil, nil
308 want
, before
, err
:= numOpenFDs()
310 // We encountered a problem reading /proc/self/fd (we might be on
311 // a platform that doesn't have it). Fall back onto lsof.
312 t
.Logf("using lsof because: %v", err
)
313 numOpenFDs
= func() (int, []byte, error
) {
314 // Android's stock lsof does not obey the -p option,
315 // so extra filtering is needed.
316 // https://golang.org/issue/10206
317 if runtime
.GOOS
== "android" {
318 // numOpenFDsAndroid handles errors itself and
319 // might skip or fail the test.
320 n
, lsof
:= numOpenFDsAndroid(t
)
323 lsof
, err
:= exec
.Command("lsof", "-b", "-n", "-p", strconv
.Itoa(os
.Getpid())).Output()
324 return bytes
.Count(lsof
, []byte("\n")), lsof
, err
327 // lsof may see file descriptors associated with the fork itself,
328 // so we allow some extra margin if we have to use it.
329 // https://golang.org/issue/19243
332 // Retry reading the number of open file descriptors.
333 want
, before
, err
= numOpenFDs()
336 t
.Skipf("skipping test; error finding or running lsof")
340 for i
:= 0; i
< 6; i
++ {
341 cmd
:= exec
.Command("something-that-does-not-exist-executable")
345 if err
:= cmd
.Run(); err
== nil {
346 t
.Fatal("unexpected success")
349 got
, after
, err
:= numOpenFDs()
351 // numOpenFDs has already succeeded once, it should work here.
352 t
.Errorf("unexpected failure: %v", err
)
354 if got
-want
> tolerance
{
355 t
.Errorf("number of open file descriptors changed: got %v, want %v", got
, want
)
357 t
.Errorf("before:\n%v\n", before
)
360 t
.Errorf("after:\n%v\n", after
)
365 func numOpenFDsAndroid(t
*testing
.T
) (n
int, lsof
[]byte) {
366 raw
, err
:= exec
.Command("lsof").Output()
368 t
.Skip("skipping test; error finding or running lsof")
371 // First find the PID column index by parsing the first line, and
372 // select lines containing pid in the column.
373 pid
:= []byte(strconv
.Itoa(os
.Getpid()))
376 s
:= bufio
.NewScanner(bytes
.NewReader(raw
))
379 fields
:= bytes
.Fields(line
)
381 for i
, v
:= range fields
{
382 if bytes
.Equal(v
, []byte("PID")) {
387 lsof
= append(lsof
, line
...)
390 if bytes
.Equal(fields
[pidCol
], pid
) {
391 lsof
= append(lsof
, '\n')
392 lsof
= append(lsof
, line
...)
396 t
.Fatal("error processing lsof output: unexpected header format")
398 if err
:= s
.Err(); err
!= nil {
399 t
.Fatalf("error processing lsof output: %v", err
)
401 return bytes
.Count(lsof
, []byte("\n")), lsof
404 var testedAlreadyLeaked
= false
406 // basefds returns the number of expected file descriptors
407 // to be present in a process at start.
408 // stdin, stdout, stderr, epoll/kqueue, maybe testlog
409 func basefds() uintptr {
410 n
:= os
.Stderr
.Fd() + 1
411 // The poll (epoll/kqueue) descriptor can be numerically
412 // either between stderr and the testlog-fd, or after
414 if poll
.PollDescriptor() == n
{
417 for _
, arg
:= range os
.Args
{
418 if strings
.HasPrefix(arg
, "-test.testlogfile=") {
425 func closeUnexpectedFds(t
*testing
.T
, m
string) {
426 for fd
:= basefds(); fd
<= 101; fd
++ {
427 if fd
== poll
.PollDescriptor() {
430 err
:= os
.NewFile(fd
, "").Close()
432 t
.Logf("%s: Something already leaked - closed fd %d", m
, fd
)
437 func TestExtraFilesFDShuffle(t
*testing
.T
) {
438 t
.Skip("flaky test; see https://golang.org/issue/5780")
439 switch runtime
.GOOS
{
441 // TODO(cnicolaou): https://golang.org/issue/2603
442 // leads to leaked file descriptors in this test when it's
443 // run from a builder.
444 closeUnexpectedFds(t
, "TestExtraFilesFDShuffle")
446 // https://golang.org/issue/3955
447 closeUnexpectedFds(t
, "TestExtraFilesFDShuffle")
449 t
.Skip("no operating system support; skipping")
452 // syscall.StartProcess maps all the FDs passed to it in
453 // ProcAttr.Files (the concatenation of stdin,stdout,stderr and
454 // ExtraFiles) into consecutive FDs in the child, that is:
455 // Files{11, 12, 6, 7, 9, 3} should result in the file
456 // represented by FD 11 in the parent being made available as 0
457 // in the child, 12 as 1, etc.
459 // We want to test that FDs in the child do not get overwritten
460 // by one another as this shuffle occurs. The original implementation
461 // was buggy in that in some data dependent cases it would overwrite
462 // stderr in the child with one of the ExtraFile members.
463 // Testing for this case is difficult because it relies on using
464 // the same FD values as that case. In particular, an FD of 3
465 // must be at an index of 4 or higher in ProcAttr.Files and
466 // the FD of the write end of the Stderr pipe (as obtained by
467 // StderrPipe()) must be the same as the size of ProcAttr.Files;
468 // therefore we test that the read end of this pipe (which is what
469 // is returned to the parent by StderrPipe() being one less than
470 // the size of ProcAttr.Files, i.e. 3+len(cmd.ExtraFiles).
472 // Moving this test case around within the overall tests may
473 // affect the FDs obtained and hence the checks to catch these cases.
475 c
:= helperCommand(t
, "extraFilesAndPipes", strconv
.Itoa(npipes
+1))
476 rd
, wr
, _
:= os
.Pipe()
479 t
.Errorf("bad test value for test pipe: fd %d", rd
.Fd())
481 stderr
, _
:= c
.StderrPipe()
482 wr
.WriteString("_LAST")
485 pipes
:= make([]struct {
488 data
:= []string{"a", "b"}
490 for i
:= 0; i
< npipes
; i
++ {
491 r
, w
, err
:= os
.Pipe()
493 t
.Fatalf("unexpected error creating pipe: %s", err
)
497 w
.WriteString(data
[i
])
498 c
.ExtraFiles
= append(c
.ExtraFiles
, pipes
[i
].r
)
504 // Put fd 3 at the end.
505 c
.ExtraFiles
= append(c
.ExtraFiles
, rd
)
507 stderrFd
:= int(stderr
.(*os
.File
).Fd())
508 if stderrFd
!= ((len(c
.ExtraFiles
) + 3) - 1) {
509 t
.Errorf("bad test value for stderr pipe")
512 expected
:= "child: " + strings
.Join(data
, "") + "_LAST"
516 t
.Fatalf("Run: %v", err
)
518 ch
:= make(chan string, 1)
519 go func(ch
chan string) {
520 buf
:= make([]byte, 512)
521 n
, err
:= stderr
.Read(buf
)
523 t
.Errorf("Read: %s", err
)
526 ch
<- string(buf
[:n
])
533 t
.Errorf("Read: '%s' not '%s'", m
, expected
)
535 case <-time
.After(5 * time
.Second
):
536 t
.Errorf("Read timedout")
541 func TestExtraFiles(t
*testing
.T
) {
542 testenv
.MustHaveExec(t
)
544 if runtime
.GOOS
== "windows" {
545 t
.Skipf("skipping test on %q", runtime
.GOOS
)
548 // Ensure that file descriptors have not already been leaked into
550 if !testedAlreadyLeaked
{
551 testedAlreadyLeaked
= true
552 closeUnexpectedFds(t
, "TestExtraFiles")
555 // Force network usage, to verify the epoll (or whatever) fd
556 // doesn't leak to the child,
557 ln
, err
:= net
.Listen("tcp", "127.0.0.1:0")
563 // Make sure duplicated fds don't leak to the child.
564 f
, err
:= ln
.(*net
.TCPListener
).File()
569 ln2
, err
:= net
.FileListener(f
)
575 // Force TLS root certs to be loaded (which might involve
576 // cgo), to make sure none of that potential C code leaks fds.
577 ts
:= httptest
.NewUnstartedServer(http
.HandlerFunc(func(w http
.ResponseWriter
, r
*http
.Request
) {}))
578 // quiet expected TLS handshake error "remote error: bad certificate"
579 ts
.Config
.ErrorLog
= log
.New(ioutil
.Discard
, "", 0)
582 _
, err
= http
.Get(ts
.URL
)
584 t
.Errorf("success trying to fetch %s; want an error", ts
.URL
)
587 tf
, err
:= ioutil
.TempFile("", "")
589 t
.Fatalf("TempFile: %v", err
)
591 defer os
.Remove(tf
.Name())
594 const text
= "Hello, fd 3!"
595 _
, err
= tf
.Write([]byte(text
))
597 t
.Fatalf("Write: %v", err
)
599 _
, err
= tf
.Seek(0, io
.SeekStart
)
601 t
.Fatalf("Seek: %v", err
)
604 c
:= helperCommand(t
, "read3")
605 var stdout
, stderr bytes
.Buffer
608 c
.ExtraFiles
= []*os
.File
{tf
}
611 t
.Fatalf("Run: %v; stdout %q, stderr %q", err
, stdout
.Bytes(), stderr
.Bytes())
613 if stdout
.String() != text
{
614 t
.Errorf("got stdout %q, stderr %q; want %q on stdout", stdout
.String(), stderr
.String(), text
)
618 func TestExtraFilesRace(t
*testing
.T
) {
619 if runtime
.GOOS
== "windows" {
620 t
.Skip("no operating system support; skipping")
622 listen
:= func() net
.Listener
{
623 ln
, err
:= net
.Listen("tcp", "127.0.0.1:0")
629 listenerFile
:= func(ln net
.Listener
) *os
.File
{
630 f
, err
:= ln
.(*net
.TCPListener
).File()
636 runCommand
:= func(c
*exec
.Cmd
, out
chan<- string) {
637 bout
, err
:= c
.CombinedOutput()
639 out
<- "ERROR:" + err
.Error()
645 for i
:= 0; i
< 10; i
++ {
647 ca
:= helperCommand(t
, "describefiles")
648 ca
.ExtraFiles
= []*os
.File
{listenerFile(la
)}
650 cb
:= helperCommand(t
, "describefiles")
651 cb
.ExtraFiles
= []*os
.File
{listenerFile(lb
)}
652 ares
:= make(chan string)
653 bres
:= make(chan string)
654 go runCommand(ca
, ares
)
655 go runCommand(cb
, bres
)
656 if got
, want
:= <-ares
, fmt
.Sprintf("fd3: listener %s\n", la
.Addr()); got
!= want
{
657 t
.Errorf("iteration %d, process A got:\n%s\nwant:\n%s\n", i
, got
, want
)
659 if got
, want
:= <-bres
, fmt
.Sprintf("fd3: listener %s\n", lb
.Addr()); got
!= want
{
660 t
.Errorf("iteration %d, process B got:\n%s\nwant:\n%s\n", i
, got
, want
)
664 for _
, f
:= range ca
.ExtraFiles
{
667 for _
, f
:= range cb
.ExtraFiles
{
674 // TestHelperProcess isn't a real test. It's used as a helper process
675 // for TestParameterRun.
676 func TestHelperProcess(*testing
.T
) {
677 if os
.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
682 // Determine which command to use to display open files.
684 switch runtime
.GOOS
{
685 case "dragonfly", "freebsd", "netbsd", "openbsd":
700 fmt
.Fprintf(os
.Stderr
, "No command\n")
704 cmd
, args
:= args
[0], args
[1:]
707 iargs
:= []interface{}{}
708 for _
, s
:= range args
{
709 iargs
= append(iargs
, s
)
711 fmt
.Println(iargs
...)
713 for _
, s
:= range args
{
714 fmt
.Println(os
.Getenv(s
))
719 io
.Copy(os
.Stdout
, os
.Stdin
)
723 for _
, fn
:= range args
{
724 f
, err
:= os
.Open(fn
)
726 fmt
.Fprintf(os
.Stderr
, "Error: %v\n", err
)
730 io
.Copy(os
.Stdout
, f
)
735 bufr
:= bufio
.NewReader(os
.Stdin
)
737 line
, _
, err
:= bufr
.ReadLine()
740 } else if err
!= nil {
743 if bytes
.HasPrefix(line
, []byte("O:")) {
744 os
.Stdout
.Write(line
)
745 os
.Stdout
.Write([]byte{'\n'})
746 } else if bytes
.HasPrefix(line
, []byte("E:")) {
747 os
.Stderr
.Write(line
)
748 os
.Stderr
.Write([]byte{'\n'})
754 b
, err
:= ioutil
.ReadAll(os
.Stdin
)
756 fmt
.Fprintf(os
.Stderr
, "Error: %v\n", err
)
759 if s
:= string(b
); s
!= stdinCloseTestString
{
760 fmt
.Fprintf(os
.Stderr
, "Error: Read %q, want %q", s
, stdinCloseTestString
)
764 case "read3": // read fd 3
765 fd3
:= os
.NewFile(3, "fd3")
766 bs
, err
:= ioutil
.ReadAll(fd3
)
768 fmt
.Printf("ReadAll from fd 3: %v", err
)
771 switch runtime
.GOOS
{
773 // TODO(jsing): Determine why DragonFly is leaking
774 // file descriptors...
776 // TODO(bradfitz): broken? Sometimes.
777 // https://golang.org/issue/2603
778 // Skip this additional part of the test for now.
780 // TODO(jsing): This currently fails on NetBSD due to
781 // the cloned file descriptors that result from opening
783 // https://golang.org/issue/3955
785 // TODO(aram): This fails on Solaris because libc opens
786 // its own files, as it sees fit. Darwin does the same,
787 // see: https://golang.org/issue/2603
789 // Now verify that there are no other open fds.
791 for wantfd
:= basefds() + 1; wantfd
<= 100; wantfd
++ {
792 if wantfd
== poll
.PollDescriptor() {
795 f
, err
:= os
.Open(os
.Args
[0])
797 fmt
.Printf("error opening file with expected fd %d: %v", wantfd
, err
)
800 if got
:= f
.Fd(); got
!= wantfd
{
801 fmt
.Printf("leaked parent file. fd = %d; want %d\n", got
, wantfd
)
803 switch runtime
.GOOS
{
805 args
= []string{fmt
.Sprintf("/proc/%d/fd", os
.Getpid())}
807 args
= []string{"-p", fmt
.Sprint(os
.Getpid())}
809 out
, _
:= exec
.Command(ofcmd
, args
...).CombinedOutput()
810 fmt
.Print(string(out
))
813 files
= append(files
, f
)
815 for _
, f
:= range files
{
819 // Referring to fd3 here ensures that it is not
820 // garbage collected, and therefore closed, while
821 // executing the wantfd loop above. It doesn't matter
822 // what we do with fd3 as long as we refer to it;
823 // closing it is the easy choice.
827 n
, _
:= strconv
.Atoi(args
[0])
829 case "describefiles":
830 f
:= os
.NewFile(3, fmt
.Sprintf("fd3"))
831 ln
, err
:= net
.FileListener(f
)
833 fmt
.Printf("fd3: listener %s\n", ln
.Addr())
837 case "extraFilesAndPipes":
838 n
, _
:= strconv
.Atoi(args
[0])
839 pipes
:= make([]*os
.File
, n
)
840 for i
:= 0; i
< n
; i
++ {
841 pipes
[i
] = os
.NewFile(uintptr(3+i
), strconv
.Itoa(i
))
844 for i
, r
:= range pipes
{
845 ch
:= make(chan string, 1)
846 go func(c
chan string) {
847 buf
:= make([]byte, 10)
848 n
, err
:= r
.Read(buf
)
850 fmt
.Fprintf(os
.Stderr
, "Child: read error: %v on pipe %d\n", err
, i
)
858 response
= response
+ m
859 case <-time
.After(5 * time
.Second
):
860 fmt
.Fprintf(os
.Stderr
, "Child: Timeout reading from pipe: %d\n", i
)
864 fmt
.Fprintf(os
.Stderr
, "child: %s", response
)
867 cmd
:= exec
.Command(args
[1])
869 output
, err
:= cmd
.CombinedOutput()
871 fmt
.Fprintf(os
.Stderr
, "Child: %s %s", err
, string(output
))
874 fmt
.Printf("%s", string(output
))
877 p
, err
:= exec
.LookPath(args
[0])
879 fmt
.Fprintf(os
.Stderr
, "LookPath failed: %v\n", err
)
885 fmt
.Fprintf(os
.Stderr
, "some stderr text\n")
888 time
.Sleep(3 * time
.Second
)
891 fmt
.Fprintf(os
.Stderr
, "Unknown command %q\n", cmd
)
896 type delayedInfiniteReader
struct{}
898 func (delayedInfiniteReader
) Read(b
[]byte) (int, error
) {
899 time
.Sleep(100 * time
.Millisecond
)
906 // Issue 9173: ignore stdin pipe writes if the program completes successfully.
907 func TestIgnorePipeErrorOnSuccess(t
*testing
.T
) {
908 testenv
.MustHaveExec(t
)
910 // We really only care about testing this on Unixy and Windowsy things.
911 if runtime
.GOOS
== "plan9" {
912 t
.Skipf("skipping test on %q", runtime
.GOOS
)
915 testWith
:= func(r io
.Reader
) func(*testing
.T
) {
916 return func(t
*testing
.T
) {
917 cmd
:= helperCommand(t
, "echo", "foo")
921 if err
:= cmd
.Run(); err
!= nil {
924 if got
, want
:= out
.String(), "foo\n"; got
!= want
{
925 t
.Errorf("output = %q; want %q", got
, want
)
929 t
.Run("10MB", testWith(strings
.NewReader(strings
.Repeat("x", 10<<20))))
930 t
.Run("Infinite", testWith(delayedInfiniteReader
{}))
933 type badWriter
struct{}
935 func (w
*badWriter
) Write(data
[]byte) (int, error
) {
936 return 0, io
.ErrUnexpectedEOF
939 func TestClosePipeOnCopyError(t
*testing
.T
) {
940 testenv
.MustHaveExec(t
)
942 if runtime
.GOOS
== "windows" || runtime
.GOOS
== "plan9" {
943 t
.Skipf("skipping test on %s - no yes command", runtime
.GOOS
)
945 cmd
:= exec
.Command("yes")
946 cmd
.Stdout
= new(badWriter
)
947 c
:= make(chan int, 1)
951 t
.Errorf("yes completed successfully")
958 case <-time
.After(5 * time
.Second
):
959 t
.Fatalf("yes got stuck writing to bad writer")
963 func TestOutputStderrCapture(t
*testing
.T
) {
964 testenv
.MustHaveExec(t
)
966 cmd
:= helperCommand(t
, "stderrfail")
967 _
, err
:= cmd
.Output()
968 ee
, ok
:= err
.(*exec
.ExitError
)
970 t
.Fatalf("Output error type = %T; want ExitError", err
)
972 got
:= string(ee
.Stderr
)
973 want
:= "some stderr text\n"
975 t
.Errorf("ExitError.Stderr = %q; want %q", got
, want
)
979 func TestContext(t
*testing
.T
) {
980 ctx
, cancel
:= context
.WithCancel(context
.Background())
981 c
:= helperCommandContext(t
, ctx
, "pipetest")
982 stdin
, err
:= c
.StdinPipe()
986 stdout
, err
:= c
.StdoutPipe()
990 if err
:= c
.Start(); err
!= nil {
994 if _
, err
:= stdin
.Write([]byte("O:hi\n")); err
!= nil {
997 buf
:= make([]byte, 5)
998 n
, err
:= io
.ReadFull(stdout
, buf
)
999 if n
!= len(buf
) || err
!= nil ||
string(buf
) != "O:hi\n" {
1000 t
.Fatalf("ReadFull = %d, %v, %q", n
, err
, buf
[:n
])
1002 waitErr
:= make(chan error
, 1)
1008 case err
:= <-waitErr
:
1010 t
.Fatal("expected Wait failure")
1012 case <-time
.After(3 * time
.Second
):
1013 t
.Fatal("timeout waiting for child process death")
1017 func TestContextCancel(t
*testing
.T
) {
1018 ctx
, cancel
:= context
.WithCancel(context
.Background())
1020 c
:= helperCommandContext(t
, ctx
, "cat")
1022 r
, w
, err
:= os
.Pipe()
1028 stdout
, err
:= c
.StdoutPipe()
1032 readDone
:= make(chan struct{})
1034 defer close(readDone
)
1037 n
, err
:= stdout
.Read(a
[:])
1040 t
.Errorf("unexpected read error: %v", err
)
1048 if err
:= c
.Start(); err
!= nil {
1052 if err
:= r
.Close(); err
!= nil {
1056 if _
, err
:= io
.WriteString(w
, "echo"); err
!= nil {
1062 // Calling cancel should have killed the process, so writes
1063 // should now fail. Give the process a little while to die.
1066 if _
, err
:= io
.WriteString(w
, "echo"); err
!= nil {
1069 if time
.Since(start
) > time
.Second
{
1070 t
.Fatal("canceling context did not stop program")
1072 time
.Sleep(time
.Millisecond
)
1075 if err
:= w
.Close(); err
!= nil {
1076 t
.Errorf("error closing write end of pipe: %v", err
)
1080 if err
:= c
.Wait(); err
== nil {
1081 t
.Error("program unexpectedly exited successfully")
1083 t
.Logf("exit status: %v", err
)
1087 // test that environment variables are de-duped.
1088 func TestDedupEnvEcho(t
*testing
.T
) {
1089 testenv
.MustHaveExec(t
)
1091 cmd
:= helperCommand(t
, "echoenv", "FOO")
1092 cmd
.Env
= append(cmd
.Env
, "FOO=bad", "FOO=good")
1093 out
, err
:= cmd
.CombinedOutput()
1097 if got
, want
:= strings
.TrimSpace(string(out
)), "good"; got
!= want
{
1098 t
.Errorf("output = %q; want %q", got
, want
)