| 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 |