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