From c659e435a54cfcd33422b6824d62c275e6fe7c20 Mon Sep 17 00:00:00 2001 From: Juan Enrique Escobar Robles Date: Wed, 8 Jul 2020 11:45:22 -0500 Subject: [PATCH] simple stdin implementation --- cmd.go | 12 ++++++++++-- cmd_test.go | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ test/stdin | 5 +++++ 3 files changed, 65 insertions(+), 2 deletions(-) create mode 100755 test/stdin diff --git a/cmd.go b/cmd.go index 29bd9e2..d2c94a3 100644 --- a/cmd.go +++ b/cmd.go @@ -195,7 +195,12 @@ func (c *Cmd) Clone() *Cmd { // Exactly one Status is sent on the channel when the command ends. The channel // is not closed. Any Go error is set to Status.Error. Start is idempotent; it // always returns the same channel. + func (c *Cmd) Start() <-chan Status { + return c.StartWithStdin(nil) +} + +func (c *Cmd) StartWithStdin(in io.Reader) <-chan Status { c.Lock() defer c.Unlock() @@ -204,7 +209,7 @@ func (c *Cmd) Start() <-chan Status { } c.statusChan = make(chan Status, 1) - go c.run() + go c.run(in) return c.statusChan } @@ -290,7 +295,7 @@ func (c *Cmd) Done() <-chan struct{} { // -------------------------------------------------------------------------- -func (c *Cmd) run() { +func (c *Cmd) run(in io.Reader) { defer func() { c.statusChan <- c.Status() // unblocks Start if caller is waiting close(c.doneChan) @@ -300,6 +305,9 @@ func (c *Cmd) run() { // Setup command // ////////////////////////////////////////////////////////////////////// cmd := exec.Command(c.Name, c.Args...) + if in != nil { + cmd.Stdin = in + } // Platform-specific SysProcAttr management setProcessGroupID(cmd) diff --git a/cmd_test.go b/cmd_test.go index dab05ff..0e0a1fe 100644 --- a/cmd_test.go +++ b/cmd_test.go @@ -3,6 +3,7 @@ package cmd_test import ( + "bytes" "errors" "io/ioutil" "os" @@ -1028,3 +1029,52 @@ func TestCmdNoOutput(t *testing.T) { t.Errorf("got stderr, expected no output: %v", s.Stderr) } } + +func TestStdinOk(t *testing.T) { + + tests := []struct { + in []byte + }{ + { + in: []byte("1"), + }, + { + in: []byte("hello"), + }, + { + in: []byte{65, 66, 67, 226, 130, 172}, // ABC€ + }, + } + for _, tt := range tests { + now := time.Now().Unix() + p := cmd.NewCmd("test/stdin") + gotStatus := <-p.StartWithStdin(bytes.NewReader(tt.in)) + expectStatus := cmd.Status{ + Cmd: "test/stdin", + PID: gotStatus.PID, // nondeterministic + Complete: true, + Exit: 0, + Error: nil, + Runtime: gotStatus.Runtime, // nondeterministic + Stdout: []string{"stdin: " + string(tt.in)}, + Stderr: []string{}, + } + if gotStatus.StartTs < now { + t.Error("StartTs < now") + } + if gotStatus.StopTs < gotStatus.StartTs { + t.Error("StopTs < StartTs") + } + gotStatus.StartTs = 0 + gotStatus.StopTs = 0 + if diffs := deep.Equal(gotStatus, expectStatus); diffs != nil { + t.Error(diffs) + } + if gotStatus.PID < 0 { + t.Errorf("got PID %d, expected non-zero", gotStatus.PID) + } + if gotStatus.Runtime < 0 { + t.Errorf("got runtime %f, expected non-zero", gotStatus.Runtime) + } + } +} diff --git a/test/stdin b/test/stdin new file mode 100755 index 0000000..c2fbbb3 --- /dev/null +++ b/test/stdin @@ -0,0 +1,5 @@ +#!/bin/bash + +IN=$(< /dev/stdin) + +echo "stdin: $IN"