// Job creates a new job which can later be executed. // This function mimics `Command` from the standard os/exec package. func (eng *Engine) Job(name string, args ...string) *Job { job := &Job{ Eng: eng, Name: name, Args: args, Stdin: NewInput(), Stdout: NewOutput(), Stderr: NewOutput(), env: &Env{}, closeIO: true, cancelled: make(chan struct{}), } if eng.Logging { job.Stderr.Add(ioutils.NopWriteCloser(eng.Stderr)) } // Catchall is shadowed by specific Register. if handler, exists := eng.handlers[name]; exists { job.handler = handler } else if eng.catchall != nil && name != "" { // empty job names are illegal, catchall or not. job.handler = eng.catchall } return job }
// Ensure that a job within a job both using the same underlying standard // output writer does not close the output of the outer job when the inner // job's stdout is wrapped with a NopCloser. When not wrapped, it should // close the outer job's output. func TestNestedJobSharedOutput(t *testing.T) { var ( outerHandler Handler innerHandler Handler wrapOutput bool ) outerHandler = func(job *Job) error { job.Stdout.Write([]byte("outer1")) innerJob := job.Eng.Job("innerJob") if wrapOutput { innerJob.Stdout.Add(ioutils.NopWriteCloser(job.Stdout)) } else { innerJob.Stdout.Add(job.Stdout) } if err := innerJob.Run(); err != nil { t.Fatal(err) } // If wrapOutput was *false* this write will do nothing. // FIXME (jlhawn): It should cause an error to write to // closed output. job.Stdout.Write([]byte(" outer2")) return nil } innerHandler = func(job *Job) error { job.Stdout.Write([]byte(" inner")) return nil } eng := New() eng.Register("outerJob", outerHandler) eng.Register("innerJob", innerHandler) // wrapOutput starts *false* so the expected // output of running the outer job will be: // // "outer1 inner" // outBuf := new(bytes.Buffer) outerJob := eng.Job("outerJob") outerJob.Stdout.Add(outBuf) if err := outerJob.Run(); err != nil { t.Fatal(err) } expectedOutput := "outer1 inner" if outBuf.String() != expectedOutput { t.Fatalf("expected job output to be %q, got %q", expectedOutput, outBuf.String()) } // Set wrapOutput to true so that the expected // output of running the outer job will be: // // "outer1 inner outer2" // wrapOutput = true outBuf.Reset() outerJob = eng.Job("outerJob") outerJob.Stdout.Add(outBuf) if err := outerJob.Run(); err != nil { t.Fatal(err) } expectedOutput = "outer1 inner outer2" if outBuf.String() != expectedOutput { t.Fatalf("expected job output to be %q, got %q", expectedOutput, outBuf.String()) } }