| OLD | NEW |
| (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 } | |
| OLD | NEW |