Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(581)

Side by Side Diff: go/exec/exec_test.go

Issue 1302513006: Reland of Add a library for running external commands, providing timeouts and test injection. (Closed) Base URL: https://skia.googlesource.com/buildbot@master
Patch Set: Fix bug with nil io.Writer. Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « go/exec/exec.go ('k') | go/util/command.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 package exec
2
3 import (
4 "bytes"
5 "fmt"
6 "io"
7 "io/ioutil"
8 "os"
9 "path/filepath"
10 "strings"
11 "testing"
12 "time"
13
14 "github.com/skia-dev/glog"
15 expect "github.com/stretchr/testify/assert"
16 assert "github.com/stretchr/testify/require"
17 )
18
19 // Copied from go.skia.org/infra/go/util/util.go to avoid recursive dependency.
20 func RemoveAll(path string) {
21 if err := os.RemoveAll(path); err != nil {
22 glog.Errorf("Failed to RemoveAll(%s): %v", path, err)
23 }
24 }
25
26 func TestParseCommand(t *testing.T) {
27 test := func(input string, expected Command) {
28 expect.Equal(t, expected, ParseCommand(input))
29 }
30 test("", Command{Name: "", Args: []string{}})
31 test("foo", Command{Name: "foo", Args: []string{}})
32 test("foo bar", Command{Name: "foo", Args: []string{"bar"}})
33 test("foo_bar baz", Command{Name: "foo_bar", Args: []string{"baz"}})
34 test("foo-bar baz", Command{Name: "foo-bar", Args: []string{"baz"}})
35 test("foo --bar --baz", Command{Name: "foo", Args: []string{"--bar", "-- baz"}})
36 // Doesn't work.
37 //test("foo 'bar baz'", Command{Name: "foo", Args: []string{"bar baz"}})
38 }
39
40 func TestSquashWriters(t *testing.T) {
41 test := func(input ...*bytes.Buffer) {
42 writers := make([]io.Writer, len(input))
43 for i, buffer := range input {
44 if buffer != nil {
45 writers[i] = buffer
46 }
47 }
48 squashed := squashWriters(writers...)
49 assert.NotNil(t, squashed)
50 testString1, testString2 := "foobar", "baz"
51 n, err := squashed.Write([]byte(testString1))
52 expect.Equal(t, len(testString1), n)
53 expect.NoError(t, err)
54 n, err = squashed.Write([]byte(testString2))
55 expect.Equal(t, len(testString2), n)
56 expect.NoError(t, err)
57 for _, buffer := range input {
58 if buffer != nil {
59 expect.Equal(t, testString1+testString2, string( buffer.Bytes()))
60 }
61 }
62 }
63 expect.Equal(t, nil, squashWriters())
64 expect.Equal(t, nil, squashWriters(nil))
65 expect.Equal(t, nil, squashWriters(nil, nil))
66 test(&bytes.Buffer{})
67 test(&bytes.Buffer{}, &bytes.Buffer{})
68 test(&bytes.Buffer{}, nil)
69 test(nil, &bytes.Buffer{})
70 test(&bytes.Buffer{}, &bytes.Buffer{}, &bytes.Buffer{})
71 test(&bytes.Buffer{}, nil, nil)
72 test(nil, &bytes.Buffer{}, nil)
73 test(nil, nil, &bytes.Buffer{})
74 test(&bytes.Buffer{}, nil, &bytes.Buffer{})
75 }
76
77 func TestBasic(t *testing.T) {
78 dir, err := ioutil.TempDir("", "exec_test")
79 assert.NoError(t, err)
80 defer RemoveAll(dir)
81 file := filepath.Join(dir, "ran")
82 assert.NoError(t, Run(&Command{
83 Name: "touch",
84 Args: []string{file},
85 }))
86 _, err = os.Stat(file)
87 expect.NoError(t, err)
88 }
89
90 func WriteScript(path, script string) error {
91 return ioutil.WriteFile(path, []byte(script), 0777)
92 }
93
94 const SimpleScript = `#!/bin/bash
95 touch "${EXEC_TEST_FILE}"
96 `
97
98 func TestEnv(t *testing.T) {
99 dir, err := ioutil.TempDir("", "exec_test")
100 assert.NoError(t, err)
101 defer RemoveAll(dir)
102 script := filepath.Join(dir, "simple_script.sh")
103 assert.NoError(t, WriteScript(script, SimpleScript))
104 file := filepath.Join(dir, "ran")
105 assert.NoError(t, Run(&Command{
106 Name: script,
107 Env: []string{fmt.Sprintf("EXEC_TEST_FILE=%s", file)},
108 }))
109 _, err = os.Stat(file)
110 expect.NoError(t, err)
111 }
112
113 const PathScript = `#!/bin/bash
114 echo "${PATH}" > "${EXEC_TEST_FILE}"
115 `
116
117 func TestInheritPath(t *testing.T) {
118 dir, err := ioutil.TempDir("", "exec_test")
119 assert.NoError(t, err)
120 defer RemoveAll(dir)
121 script := filepath.Join(dir, "path_script.sh")
122 assert.NoError(t, WriteScript(script, PathScript))
123 file := filepath.Join(dir, "ran")
124 assert.NoError(t, Run(&Command{
125 Name: script,
126 Env: []string{fmt.Sprintf("EXEC_TEST_FILE=%s", file)},
127 InheritPath: true,
128 }))
129 contents, err := ioutil.ReadFile(file)
130 assert.NoError(t, err)
131 expect.Equal(t, os.Getenv("PATH"), strings.TrimSpace(string(contents)))
132 }
133
134 const HelloScript = `#!/bin/bash
135 echo "Hello World!" > output.txt
136 `
137
138 func TestDir(t *testing.T) {
139 dir1, err := ioutil.TempDir("", "exec_test1")
140 assert.NoError(t, err)
141 defer RemoveAll(dir1)
142 script := filepath.Join(dir1, "hello_script.sh")
143 assert.NoError(t, WriteScript(script, HelloScript))
144 dir2, err := ioutil.TempDir("", "exec_test2")
145 assert.NoError(t, err)
146 defer RemoveAll(dir2)
147 assert.NoError(t, Run(&Command{
148 Name: script,
149 Dir: dir2,
150 }))
151 file := filepath.Join(dir2, "output.txt")
152 _, err = os.Stat(file)
153 expect.NoError(t, err)
154 }
155
156 func TestSimpleIO(t *testing.T) {
157 inputString := "foo\nbar\nbaz\n"
158 output := bytes.Buffer{}
159 assert.NoError(t, Run(&Command{
160 Name: "grep",
161 Args: []string{"-e", "^ba"},
162 Stdin: bytes.NewReader([]byte(inputString)),
163 Stdout: &output,
164 }))
165 expect.Equal(t, "bar\nbaz\n", string(output.Bytes()))
166 }
167
168 func TestError(t *testing.T) {
169 dir, err := ioutil.TempDir("", "exec_test")
170 assert.NoError(t, err)
171 defer RemoveAll(dir)
172 output := bytes.Buffer{}
173 err = Run(&Command{
174 Name: "cp",
175 Args: []string{filepath.Join(dir, "doesnt_exist"),
176 filepath.Join(dir, "dest")},
177 Stderr: &output,
178 })
179 expect.Error(t, err)
180 expect.Contains(t, err.Error(), "exit status 1")
181 expect.Contains(t, string(output.Bytes()), "No such file or directory")
182 }
183
184 const CombinedOutputScript = `#!/bin/bash
185 echo "roses"
186 >&2 echo "red"
187 echo "violets"
188 >&2 echo "blue"
189 `
190
191 func TestCombinedOutput(t *testing.T) {
192 dir, err := ioutil.TempDir("", "exec_test")
193 assert.NoError(t, err)
194 defer RemoveAll(dir)
195 script := filepath.Join(dir, "combined_output_script.sh")
196 assert.NoError(t, WriteScript(script, CombinedOutputScript))
197 combined := bytes.Buffer{}
198 assert.NoError(t, Run(&Command{
199 Name: script,
200 CombinedOutput: &combined,
201 }))
202 expect.Equal(t, "roses\nred\nviolets\nblue\n", string(combined.Bytes()))
203 }
204
205 // Previously there was a bug due to code like:
206 // var outputFile *os.File
207 // if outputToFile {
208 // outputFile = ...
209 // }
210 // Run(&Command{... Stdout: outputFile})
211 // See http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/in dex.html#nil_in_nil_in_vals
212 func TestNilIO(t *testing.T) {
213 inputString := "foo\nbar\nbaz\n"
214 assert.NoError(t, Run(&Command{
215 Name: "grep",
216 Args: []string{"-e", "^ba"},
217 Stdin: bytes.NewReader([]byte(inputString)),
218 Stdout: (*os.File)(nil),
219 }))
220 }
221
222 const SleeperScript = `#!/bin/bash
223 sleep 3
224 touch ran
225 `
226
227 func TestTimeoutNotReached(t *testing.T) {
228 dir, err := ioutil.TempDir("", "exec_test")
229 assert.NoError(t, err)
230 defer RemoveAll(dir)
231 script := filepath.Join(dir, "sleeper_script.sh")
232 assert.NoError(t, WriteScript(script, SleeperScript))
233 assert.NoError(t, Run(&Command{
234 Name: script,
235 Dir: dir,
236 Timeout: time.Minute,
237 }))
238 file := filepath.Join(dir, "ran")
239 _, err = os.Stat(file)
240 expect.NoError(t, err)
241 }
242
243 func TestTimeoutExceeded(t *testing.T) {
244 dir, err := ioutil.TempDir("", "exec_test")
245 assert.NoError(t, err)
246 defer RemoveAll(dir)
247 script := filepath.Join(dir, "sleeper_script.sh")
248 assert.NoError(t, WriteScript(script, SleeperScript))
249 err = Run(&Command{
250 Name: script,
251 Dir: dir,
252 Timeout: time.Second,
253 })
254 expect.Error(t, err)
255 expect.Contains(t, err.Error(), "Command killed")
256 file := filepath.Join(dir, "ran")
257 _, err = os.Stat(file)
258 expect.True(t, os.IsNotExist(err))
259 }
260
261 func TestInjection(t *testing.T) {
262 var actualCommand *Command
263 SetRunForTesting(func(command *Command) error {
264 actualCommand = command
265 return nil
266 })
267 defer SetRunForTesting(DefaultRun)
268
269 dir, err := ioutil.TempDir("", "exec_test")
270 assert.NoError(t, err)
271 defer RemoveAll(dir)
272 file := filepath.Join(dir, "ran")
273 assert.NoError(t, Run(&Command{
274 Name: "touch",
275 Args: []string{file},
276 }))
277 _, err = os.Stat(file)
278 expect.True(t, os.IsNotExist(err))
279
280 expect.Equal(t, "touch", actualCommand.Name)
281 expect.Equal(t, 1, len(actualCommand.Args))
282 expect.Equal(t, file, actualCommand.Args[0])
283 }
284
285 func TestRunSimple(t *testing.T) {
286 output, err := RunSimple(`echo "Hello Go!"`)
287 assert.NoError(t, err)
288 expect.Equal(t, "\"Hello Go!\"\n", output)
289 }
OLDNEW
« no previous file with comments | « go/exec/exec.go ('k') | go/util/command.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698