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

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

Issue 1300273002: 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 squashWriters for struct arguments. 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 expect.Equal(t, nil, squashWriters())
42 expect.Equal(t, nil, squashWriters(nil))
43 expect.Equal(t, nil, squashWriters(nil, nil))
44 expect.Equal(t, nil, squashWriters((*bytes.Buffer)(nil)))
45 expect.Equal(t, nil, squashWriters((*bytes.Buffer)(nil), (*os.File)(nil) ))
46 test := func(input ...*bytes.Buffer) {
47 writers := make([]io.Writer, len(input))
48 for i, buffer := range input {
49 if buffer != nil {
50 writers[i] = buffer
51 }
52 }
53 squashed := squashWriters(writers...)
54 assert.NotNil(t, squashed)
55 testString1, testString2 := "foobar", "baz"
56 n, err := squashed.Write([]byte(testString1))
57 expect.Equal(t, len(testString1), n)
58 expect.NoError(t, err)
59 n, err = squashed.Write([]byte(testString2))
60 expect.Equal(t, len(testString2), n)
61 expect.NoError(t, err)
62 for _, buffer := range input {
63 if buffer != nil {
64 expect.Equal(t, testString1+testString2, string( buffer.Bytes()))
65 }
66 }
67 }
68 test(&bytes.Buffer{})
69 test(&bytes.Buffer{}, &bytes.Buffer{})
70 test(&bytes.Buffer{}, nil)
71 test(nil, &bytes.Buffer{})
72 test(&bytes.Buffer{}, &bytes.Buffer{}, &bytes.Buffer{})
73 test(&bytes.Buffer{}, nil, nil)
74 test(nil, &bytes.Buffer{}, nil)
75 test(nil, nil, &bytes.Buffer{})
76 test(&bytes.Buffer{}, nil, &bytes.Buffer{})
77 // Test with non-pointer io.Writers.
78 // expect.Equal returns false for two WriteLogs pointing to the same fun ction, so we test
79 // by side-effect instead.
80 out := ""
81 f := func(format string, args ...interface{}) { out = out + fmt.Sprintf( format, args...) }
82 w := squashWriters(WriteLog{LogFunc: f}, (*os.File)(nil))
83 _, err := w.Write([]byte("same"))
84 assert.NoError(t, err)
85 w = squashWriters(nil, WriteLog{LogFunc: f})
86 _, err = w.Write([]byte("obj"))
87 assert.NoError(t, err)
88 expect.Equal(t, "sameobj", out)
89 }
90
91 func TestBasic(t *testing.T) {
92 dir, err := ioutil.TempDir("", "exec_test")
93 assert.NoError(t, err)
94 defer RemoveAll(dir)
95 file := filepath.Join(dir, "ran")
96 assert.NoError(t, Run(&Command{
97 Name: "touch",
98 Args: []string{file},
99 }))
100 _, err = os.Stat(file)
101 expect.NoError(t, err)
102 }
103
104 func WriteScript(path, script string) error {
105 return ioutil.WriteFile(path, []byte(script), 0777)
106 }
107
108 const SimpleScript = `#!/bin/bash
109 touch "${EXEC_TEST_FILE}"
110 `
111
112 func TestEnv(t *testing.T) {
113 dir, err := ioutil.TempDir("", "exec_test")
114 assert.NoError(t, err)
115 defer RemoveAll(dir)
116 script := filepath.Join(dir, "simple_script.sh")
117 assert.NoError(t, WriteScript(script, SimpleScript))
118 file := filepath.Join(dir, "ran")
119 assert.NoError(t, Run(&Command{
120 Name: script,
121 Env: []string{fmt.Sprintf("EXEC_TEST_FILE=%s", file)},
122 }))
123 _, err = os.Stat(file)
124 expect.NoError(t, err)
125 }
126
127 const PathScript = `#!/bin/bash
128 echo "${PATH}" > "${EXEC_TEST_FILE}"
129 `
130
131 func TestInheritPath(t *testing.T) {
132 dir, err := ioutil.TempDir("", "exec_test")
133 assert.NoError(t, err)
134 defer RemoveAll(dir)
135 script := filepath.Join(dir, "path_script.sh")
136 assert.NoError(t, WriteScript(script, PathScript))
137 file := filepath.Join(dir, "ran")
138 assert.NoError(t, Run(&Command{
139 Name: script,
140 Env: []string{fmt.Sprintf("EXEC_TEST_FILE=%s", file)},
141 InheritPath: true,
142 }))
143 contents, err := ioutil.ReadFile(file)
144 assert.NoError(t, err)
145 expect.Equal(t, os.Getenv("PATH"), strings.TrimSpace(string(contents)))
146 }
147
148 const HelloScript = `#!/bin/bash
149 echo "Hello World!" > output.txt
150 `
151
152 func TestDir(t *testing.T) {
153 dir1, err := ioutil.TempDir("", "exec_test1")
154 assert.NoError(t, err)
155 defer RemoveAll(dir1)
156 script := filepath.Join(dir1, "hello_script.sh")
157 assert.NoError(t, WriteScript(script, HelloScript))
158 dir2, err := ioutil.TempDir("", "exec_test2")
159 assert.NoError(t, err)
160 defer RemoveAll(dir2)
161 assert.NoError(t, Run(&Command{
162 Name: script,
163 Dir: dir2,
164 }))
165 file := filepath.Join(dir2, "output.txt")
166 _, err = os.Stat(file)
167 expect.NoError(t, err)
168 }
169
170 func TestSimpleIO(t *testing.T) {
171 inputString := "foo\nbar\nbaz\n"
172 output := bytes.Buffer{}
173 assert.NoError(t, Run(&Command{
174 Name: "grep",
175 Args: []string{"-e", "^ba"},
176 Stdin: bytes.NewReader([]byte(inputString)),
177 Stdout: &output,
178 }))
179 expect.Equal(t, "bar\nbaz\n", string(output.Bytes()))
180 }
181
182 func TestError(t *testing.T) {
183 dir, err := ioutil.TempDir("", "exec_test")
184 assert.NoError(t, err)
185 defer RemoveAll(dir)
186 output := bytes.Buffer{}
187 err = Run(&Command{
188 Name: "cp",
189 Args: []string{filepath.Join(dir, "doesnt_exist"),
190 filepath.Join(dir, "dest")},
191 Stderr: &output,
192 })
193 expect.Error(t, err)
194 expect.Contains(t, err.Error(), "exit status 1")
195 expect.Contains(t, string(output.Bytes()), "No such file or directory")
196 }
197
198 const CombinedOutputScript = `#!/bin/bash
199 echo "roses"
200 >&2 echo "red"
201 echo "violets"
202 >&2 echo "blue"
203 `
204
205 func TestCombinedOutput(t *testing.T) {
206 dir, err := ioutil.TempDir("", "exec_test")
207 assert.NoError(t, err)
208 defer RemoveAll(dir)
209 script := filepath.Join(dir, "combined_output_script.sh")
210 assert.NoError(t, WriteScript(script, CombinedOutputScript))
211 combined := bytes.Buffer{}
212 assert.NoError(t, Run(&Command{
213 Name: script,
214 CombinedOutput: &combined,
215 }))
216 expect.Equal(t, "roses\nred\nviolets\nblue\n", string(combined.Bytes()))
217 }
218
219 // Previously there was a bug due to code like:
220 // var outputFile *os.File
221 // if outputToFile {
222 // outputFile = ...
223 // }
224 // Run(&Command{... Stdout: outputFile})
225 // See http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/in dex.html#nil_in_nil_in_vals
226 func TestNilIO(t *testing.T) {
227 inputString := "foo\nbar\nbaz\n"
228 assert.NoError(t, Run(&Command{
229 Name: "grep",
230 Args: []string{"-e", "^ba"},
231 Stdin: bytes.NewReader([]byte(inputString)),
232 Stdout: (*os.File)(nil),
233 }))
234 }
235
236 const SleeperScript = `#!/bin/bash
237 sleep 3
238 touch ran
239 `
240
241 func TestTimeoutNotReached(t *testing.T) {
242 dir, err := ioutil.TempDir("", "exec_test")
243 assert.NoError(t, err)
244 defer RemoveAll(dir)
245 script := filepath.Join(dir, "sleeper_script.sh")
246 assert.NoError(t, WriteScript(script, SleeperScript))
247 assert.NoError(t, Run(&Command{
248 Name: script,
249 Dir: dir,
250 Timeout: time.Minute,
251 }))
252 file := filepath.Join(dir, "ran")
253 _, err = os.Stat(file)
254 expect.NoError(t, err)
255 }
256
257 func TestTimeoutExceeded(t *testing.T) {
258 dir, err := ioutil.TempDir("", "exec_test")
259 assert.NoError(t, err)
260 defer RemoveAll(dir)
261 script := filepath.Join(dir, "sleeper_script.sh")
262 assert.NoError(t, WriteScript(script, SleeperScript))
263 err = Run(&Command{
264 Name: script,
265 Dir: dir,
266 Timeout: time.Second,
267 })
268 expect.Error(t, err)
269 expect.Contains(t, err.Error(), "Command killed")
270 file := filepath.Join(dir, "ran")
271 _, err = os.Stat(file)
272 expect.True(t, os.IsNotExist(err))
273 }
274
275 func TestInjection(t *testing.T) {
276 var actualCommand *Command
277 SetRunForTesting(func(command *Command) error {
278 actualCommand = command
279 return nil
280 })
281 defer SetRunForTesting(DefaultRun)
282
283 dir, err := ioutil.TempDir("", "exec_test")
284 assert.NoError(t, err)
285 defer RemoveAll(dir)
286 file := filepath.Join(dir, "ran")
287 assert.NoError(t, Run(&Command{
288 Name: "touch",
289 Args: []string{file},
290 }))
291 _, err = os.Stat(file)
292 expect.True(t, os.IsNotExist(err))
293
294 expect.Equal(t, "touch", actualCommand.Name)
295 expect.Equal(t, 1, len(actualCommand.Args))
296 expect.Equal(t, file, actualCommand.Args[0])
297 }
298
299 func TestRunSimple(t *testing.T) {
300 output, err := RunSimple(`echo "Hello Go!"`)
301 assert.NoError(t, err)
302 expect.Equal(t, "\"Hello Go!\"\n", output)
303 }
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