// available after a call to Wait or Run.
ProcessState *os.ProcessState
- lookPathErr error // LookPath error, if any.
- finished bool // when Wait was called
+ ctx context.Context // nil means none
+ lookPathErr error // LookPath error, if any.
+ finished bool // when Wait was called
childFiles []*os.File
closeAfterStart []io.Closer
closeAfterWait []io.Closer
return cmd
}
+// CommandContext is like Command but includes a context.
+//
+// The provided context is used to kill the process (by calling
+// os.Process.Kill) if the context becomes done before the command
+// completes on its own.
+func CommandContext(ctx context.Context, name string, arg ...string) *Cmd {
+ if ctx == nil {
+ panic("nil Context")
+ }
+ cmd := Command(name, arg...)
+ cmd.ctx = ctx
+ return cmd
+}
+
// interfaceEqual protects against panics from doing equality tests on
// two interfaces with non-comparable underlying types.
func interfaceEqual(a, b interface{}) bool {
return c.Wait()
}
-// RunContext is like Run, but kills the process (by calling os.Process.Kill)
-// if ctx is done before the process ends on its own.
-func (c *Cmd) RunContext(ctx context.Context) error {
- if err := c.Start(); err != nil {
- return err
- }
- return c.WaitContext(ctx)
-}
-
// lookExtensions finds windows executable by its dir and path.
// It uses LookPath to try appropriate extensions.
// lookExtensions does not search PATH, instead it converts `prog` into `.\prog`.
//
// Wait releases any resources associated with the Cmd.
func (c *Cmd) Wait() error {
- return c.WaitContext(nil)
-}
-
-// WaitContext is like Wait, but kills the process (by calling os.Process.Kill)
-// if ctx is done before the process ends on its own.
-func (c *Cmd) WaitContext(ctx context.Context) error {
if c.Process == nil {
return errors.New("exec: not started")
}
c.finished = true
var waitDone chan struct{}
- if ctx != nil {
+ if c.ctx != nil {
waitDone = make(chan struct{})
go func() {
select {
- case <-ctx.Done():
+ case <-c.ctx.Done():
c.Process.Kill()
case <-waitDone:
}
"time"
)
-func helperCommand(t *testing.T, s ...string) *exec.Cmd {
+func helperCommandContext(t *testing.T, ctx context.Context, s ...string) (cmd *exec.Cmd) {
testenv.MustHaveExec(t)
cs := []string{"-test.run=TestHelperProcess", "--"}
cs = append(cs, s...)
- cmd := exec.Command(os.Args[0], cs...)
+ if ctx != nil {
+ cmd = exec.CommandContext(ctx, os.Args[0], cs...)
+ } else {
+ cmd = exec.Command(os.Args[0], cs...)
+ }
cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
return cmd
}
+func helperCommand(t *testing.T, s ...string) *exec.Cmd {
+ return helperCommandContext(t, nil, s...)
+}
+
func TestEcho(t *testing.T) {
bs, err := helperCommand(t, "echo", "foo bar", "baz").Output()
if err != nil {
}
func TestContext(t *testing.T) {
- c := helperCommand(t, "pipetest")
+ ctx, cancel := context.WithCancel(context.Background())
+ c := helperCommandContext(t, ctx, "pipetest")
stdin, err := c.StdinPipe()
if err != nil {
t.Fatal(err)
if err != nil {
t.Fatal(err)
}
- ctx, cancel := context.WithCancel(context.Background())
if err := c.Start(); err != nil {
t.Fatal(err)
}
}
waitErr := make(chan error, 1)
go func() {
- waitErr <- c.WaitContext(ctx)
+ waitErr <- c.Wait()
}()
cancel()
select {