v.io/jiri@v0.0.0-20160715023856-abfb8b131290/runutil/executor_test.go (about) 1 // Copyright 2015 The Vanadium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package runutil_test 6 7 import ( 8 "bufio" 9 "bytes" 10 "fmt" 11 "io/ioutil" 12 "os" 13 "os/exec" 14 "path/filepath" 15 "strings" 16 "testing" 17 "time" 18 19 "v.io/jiri/runutil" 20 "v.io/x/lib/envvar" 21 "v.io/x/lib/lookpath" 22 ) 23 24 const timedCommandTimeout = 3 * time.Second 25 26 var forever time.Duration 27 28 func removeTimestamps(t *testing.T, buffer *bytes.Buffer) string { 29 result := "" 30 scanner := bufio.NewScanner(buffer) 31 for scanner.Scan() { 32 line := scanner.Text() 33 if index := strings.Index(line, ">>"); index != -1 { 34 result += line[index:] + "\n" 35 } else { 36 result += line + "\n" 37 } 38 } 39 if err := scanner.Err(); err != nil { 40 t.Fatalf("Scan() failed: %v", err) 41 } 42 return result 43 } 44 45 func osPath(t *testing.T, bin string) string { 46 path, err := lookpath.Look(envvar.SliceToMap(os.Environ()), bin) 47 if err != nil { 48 t.Fatal(err) 49 } 50 return path 51 } 52 53 func removeTimestampsAndPath(t *testing.T, buffer *bytes.Buffer, bin string) string { 54 s := removeTimestamps(t, buffer) 55 return strings.Replace(s, osPath(t, bin), bin, 1) 56 } 57 58 func TestCommandOK(t *testing.T) { 59 var out bytes.Buffer 60 s := runutil.NewSequence(nil, os.Stdin, &out, ioutil.Discard, false, true) 61 if err := s.Last("go", "run", "./testdata/ok_hello.go"); err != nil { 62 t.Fatalf(`Command("go run ./testdata/ok_hello.go") failed: %v`, err) 63 } 64 if got, want := removeTimestampsAndPath(t, &out, "go"), ">> go run ./testdata/ok_hello.go\n>> OK\nhello\n"; got != want { 65 t.Fatalf("unexpected output:\ngot\n%v\nwant\n%v", got, want) 66 } 67 var cout bytes.Buffer 68 if err := s.Capture(&cout, nil).Last("go", "run", "./testdata/ok_hello.go"); err != nil { 69 t.Fatalf(`Command("go run ./testdata/ok_hello.go") failed: %v`, err) 70 } 71 if got, want := cout.String(), "hello\n"; got != want { 72 t.Fatalf("unexpected output:\ngot\n%v\nwant\n%v", got, want) 73 } 74 } 75 76 func TestCommandFail(t *testing.T) { 77 var out bytes.Buffer 78 s := runutil.NewSequence(nil, os.Stdin, &out, ioutil.Discard, false, true) 79 if err := s.Last("go", "run", "./testdata/fail_hello.go"); err == nil { 80 t.Fatalf(`Command("go run ./testdata/fail_hello.go") did not fail when it should`) 81 } 82 83 if got, wantCommon, want1, want2 := removeTimestampsAndPath(t, &out, "go"), ">> go run ./testdata/fail_hello.go\n>> FAILED: exit status 1\n", "hello\nexit status 1\n", "exit status 1\nhello\n"; got != wantCommon+want1 && got != wantCommon+want2 { 84 t.Fatalf("unexpected output:\ngot\n%v\nwant\n%v\nor\n%v\n", got, wantCommon+want1, wantCommon+want2) 85 } 86 var cout, cerr bytes.Buffer 87 if err := s.Capture(&cout, &cerr).Last("go", "run", "./testdata/fail_hello.go"); err == nil { 88 t.Fatalf(`Command("go run ./testdata/fail_hello.go") did not fail when it should`) 89 } 90 if got, want := cout.String(), "hello\n"; got != want { 91 t.Fatalf("unexpected output:\ngot\n%v\nwant\n%v", got, want) 92 } 93 if got, want := cerr.String(), "exit status 1\n"; got != want { 94 t.Fatalf("unexpected output:\ngot\n%v\nwant\n%v", got, want) 95 } 96 } 97 98 func TestCommandWithOptsOK(t *testing.T) { 99 var cmdOut, runOut bytes.Buffer 100 s := runutil.NewSequence(nil, os.Stdin, &runOut, ioutil.Discard, false, true) 101 if err := s.Capture(&cmdOut, nil).Verbose(false).Last("go", "run", "./testdata/ok_hello.go"); err != nil { 102 t.Fatalf(`CommandWithOpts("go run ./testdata/ok_hello.go") failed: %v`, err) 103 } 104 if got, want := removeTimestampsAndPath(t, &runOut, "go"), ">> go run ./testdata/ok_hello.go\n>> OK\n"; got != want { 105 t.Fatalf("unexpected output:\ngot\n%v\nwant\n%v", got, want) 106 } 107 if got, want := strings.TrimSpace(cmdOut.String()), "hello"; got != want { 108 t.Fatalf("unexpected output: got %v, want %v", got, want) 109 } 110 } 111 112 func TestCommandWithOptsFail(t *testing.T) { 113 var cmdOut, runOut bytes.Buffer 114 s := runutil.NewSequence(nil, os.Stdin, &runOut, ioutil.Discard, false, true) 115 if err := s.Capture(&cmdOut, nil).Verbose(false).Last("go", "run", "./testdata/fail_hello.go"); err == nil { 116 t.Fatalf(`CommandWithOpts("go run ./testdata/fail_hello.go") did not fail when it should`) 117 } 118 if got, want := removeTimestampsAndPath(t, &runOut, "go"), ">> go run ./testdata/fail_hello.go\n>> FAILED: exit status 1\n"; got != want { 119 t.Fatalf("unexpected output:\ngot\n%v\nwant\n%v", got, want) 120 } 121 if got, want := strings.TrimSpace(cmdOut.String()), "hello"; got != want { 122 t.Fatalf("unexpected output: got %v, want %v", got, want) 123 } 124 } 125 126 func TestTimedCommandOK(t *testing.T) { 127 var out bytes.Buffer 128 s := runutil.NewSequence(nil, os.Stdin, &out, ioutil.Discard, false, true) 129 bin, err := buildTestProgram("fast_hello") 130 if bin != "" { 131 defer os.RemoveAll(filepath.Dir(bin)) 132 } 133 if err != nil { 134 t.Fatalf("%v", err) 135 } 136 if err := s.Timeout(2 * time.Minute).Last(bin); err != nil { 137 t.Fatalf(`TimedCommand("go run ./testdata/fast_hello.go") failed: %v`, err) 138 } 139 if got, want := removeTimestamps(t, &out), fmt.Sprintf(">> %s\n>> OK\nhello\n", bin); got != want { 140 t.Fatalf("unexpected output:\ngot\n%v\nwant\n%v", got, want) 141 } 142 } 143 144 func TestTimedCommandFail(t *testing.T) { 145 var out, stderr bytes.Buffer 146 s := runutil.NewSequence(nil, os.Stdin, &out, &stderr, false, true) 147 bin, err := buildTestProgram("slow_hello") 148 if bin != "" { 149 defer os.RemoveAll(filepath.Dir(bin)) 150 } 151 if err != nil { 152 t.Fatalf("%v", err) 153 } 154 if err := s.Timeout(timedCommandTimeout).Last(bin); err == nil { 155 t.Fatalf(`TimedCommand("go run ./testdata/slow_hello.go") did not fail when it should`) 156 } else if got, want := runutil.IsTimeout(err), true; got != want { 157 t.Fatalf("unexpected error: got %v, want %v", got, want) 158 } 159 o := removeTimestamps(t, &out) 160 o = strings.Replace(o, bin, "slow_hello", 1) 161 if got, want := o, `>> slow_hello 162 >> TIMED OUT 163 hello 164 >> Waiting for command to exit: ["`+bin+`"] 165 `; !strings.HasPrefix(o, want) { 166 t.Errorf("output doesn't start with %v, got: %v (stderr: %v)", want, got, stderr.String()) 167 } 168 } 169 170 func TestTimedCommandWithOptsOK(t *testing.T) { 171 var cmdOut, runOut bytes.Buffer 172 s := runutil.NewSequence(nil, os.Stdin, &runOut, ioutil.Discard, false, true) 173 bin, err := buildTestProgram("fast_hello") 174 if bin != "" { 175 defer os.RemoveAll(filepath.Dir(bin)) 176 } 177 if err != nil { 178 t.Fatalf("%v", err) 179 } 180 181 if err := s.Timeout(2*time.Minute).Verbose(false).Capture(&cmdOut, nil).Last(bin); err != nil { 182 t.Fatalf(`TimedCommandWithOpts("go run ./testdata/fast_hello.go") failed: %v`, err) 183 } 184 if got, want := removeTimestamps(t, &runOut), fmt.Sprintf(">> %s\n>> OK\n", bin); got != want { 185 t.Fatalf("unexpected output:\ngot\n%v\nwant\n%v", got, want) 186 } 187 if got, want := strings.TrimSpace(cmdOut.String()), "hello"; got != want { 188 t.Fatalf("unexpected output: got %v, want %v", got, want) 189 } 190 } 191 192 func TestTimedCommandWithOptsFail(t *testing.T) { 193 var cmdOut, runOut bytes.Buffer 194 s := runutil.NewSequence(nil, os.Stdin, &runOut, ioutil.Discard, false, true) 195 bin, err := buildTestProgram("slow_hello") 196 if bin != "" { 197 defer os.RemoveAll(filepath.Dir(bin)) 198 } 199 if err != nil { 200 t.Fatalf("%v", err) 201 } 202 if err := s.Timeout(timedCommandTimeout).Capture(&cmdOut, nil).Verbose(false).Last(bin); err == nil { 203 t.Fatalf(`TimedCommandWithOpts("go run ./testdata/slow_hello.go") did not fail when it should`) 204 } else if got, want := runutil.IsTimeout(err), true; got != want { 205 t.Fatalf("unexpected error: got %v, want %v", got, want) 206 } 207 if got, want := removeTimestamps(t, &runOut), fmt.Sprintf(">> %s\n>> TIMED OUT\n", bin); got != want { 208 t.Fatalf("unexpected output:\ngot\n%v\nwant\n%v", got, want) 209 } 210 if got, want := strings.TrimSpace(cmdOut.String()), "hello"; got != want { 211 t.Fatalf("unexpected output: got %v, want %v", got, want) 212 } 213 } 214 215 func TestFunctionOK(t *testing.T) { 216 var out bytes.Buffer 217 s := runutil.NewSequence(nil, os.Stdin, &out, ioutil.Discard, false, true) 218 fn := func() error { 219 cmd := exec.Command("go", "run", "./testdata/ok_hello.go") 220 cmd.Stdout = &out 221 return cmd.Run() 222 } 223 if err := s.Capture(&out, nil).Call(fn, "%v %v %v", "go", "run", "./testdata/ok_hello.go").Done(); err != nil { 224 t.Fatalf(`Function("go run ./testdata/ok_hello.go") failed: %v`, err) 225 } 226 if got, want := removeTimestamps(t, &out), ">> go run ./testdata/ok_hello.go\nhello\n>> OK\n"; got != want { 227 t.Fatalf("unexpected output:\ngot\n%v\nwant\n%v", got, want) 228 } 229 } 230 231 func TestFunctionFail(t *testing.T) { 232 var out bytes.Buffer 233 s := runutil.NewSequence(nil, os.Stdin, &out, ioutil.Discard, false, true) 234 fn := func() error { 235 cmd := exec.Command("go", "run", "./testdata/fail_hello.go") 236 cmd.Stdout = &out 237 cmd.Stdout = &out 238 if err := cmd.Run(); err != nil { 239 return fmt.Errorf("the function failed") 240 } 241 242 return nil 243 } 244 if err := s.Capture(&out, nil).Call(fn, "%v %v %v", "go", "run", "./testdata/fail_hello.go").Done(); err == nil { 245 t.Fatalf(`Function("go run ./testdata/fail_hello.go") did not fail when it should`) 246 } 247 if got, want := removeTimestamps(t, &out), ">> go run ./testdata/fail_hello.go\nhello\n>> FAILED: the function failed\n"; got != want { 248 t.Fatalf("unexpected output:\ngot\n%v\nwant\n%v", got, want) 249 } 250 } 251 252 func TestFunctionWithOptsOK(t *testing.T) { 253 var out bytes.Buffer 254 s := runutil.NewSequence(nil, os.Stdin, &out, ioutil.Discard, false, false) 255 256 fn := func() error { 257 cmd := exec.Command("go", "run", "./testdata/ok_hello.go") 258 cmd.Stdout = &out 259 if err := cmd.Run(); err != nil { 260 return fmt.Errorf("the function failed") 261 } 262 return nil 263 } 264 if err := s.Capture(&out, nil).Verbose(true).Call(fn, "%v %v %v", "go", "run", "./testdata/ok_hello.go").Done(); err != nil { 265 t.Fatalf(`FunctionWithOpts("go run ./testdata/ok_hello.go") failed: %v`, err) 266 } 267 if got, want := removeTimestamps(t, &out), ">> go run ./testdata/ok_hello.go\nhello\n>> OK\n"; got != want { 268 t.Fatalf("unexpected output:\ngot\n%v\nwant\n%v", got, want) 269 } 270 } 271 272 func TestFunctionWithOptsFail(t *testing.T) { 273 var out bytes.Buffer 274 s := runutil.NewSequence(nil, os.Stdin, &out, ioutil.Discard, false, false) 275 fn := func() error { 276 cmd := exec.Command("go", "run", "./testdata/fail_hello.go") 277 cmd.Stdout = &out 278 if err := cmd.Run(); err != nil { 279 return fmt.Errorf("the function failed") 280 } 281 return nil 282 } 283 if err := s.Capture(&out, nil).Verbose(true).Call(fn, "%v %v %v", "go", "run", "./testdata/fail_hello.go").Done(); err == nil { 284 t.Fatalf(`FunctionWithOpts("go run ./testdata/fail_hello.go") did not fail when it should`) 285 } 286 if got, want := removeTimestamps(t, &out), ">> go run ./testdata/fail_hello.go\nhello\n>> FAILED: the function failed\n"; got != want { 287 t.Fatalf("unexpected output:\ngot\n%v\nwant\n%v", got, want) 288 } 289 } 290 291 func TestOutput(t *testing.T) { 292 var out bytes.Buffer 293 s := runutil.NewSequence(nil, os.Stdin, &out, ioutil.Discard, false, true) 294 s.Output([]string{"hello", "world"}) 295 if got, want := removeTimestamps(t, &out), ">> hello\n>> world\n"; got != want { 296 t.Fatalf("unexpected output:\ngot\n%v\nwant\n%v", got, want) 297 } 298 } 299 300 func TestOutputWithOpts(t *testing.T) { 301 var out bytes.Buffer 302 s := runutil.NewSequence(nil, os.Stdin, &out, ioutil.Discard, false, false) 303 s.Verbose(true).Output([]string{"hello", "world"}) 304 if got, want := removeTimestamps(t, &out), ">> hello\n>> world\n"; got != want { 305 t.Fatalf("unexpected output:\ngot\n%v\nwant\n%v", got, want) 306 } 307 } 308 309 func TestNested(t *testing.T) { 310 var out bytes.Buffer 311 s := runutil.NewSequence(nil, os.Stdin, &out, ioutil.Discard, false, true) 312 fn := func() error { 313 s.Output([]string{"hello", "world"}) 314 return nil 315 } 316 s.Call(fn, "%v", "greetings").Done() 317 if got, want := removeTimestamps(t, &out), ">> greetings\n>>>> hello\n>>>> world\n>> OK\n"; got != want { 318 t.Fatalf("unexpected output:\ngot\n%v\nwant\n%v", got, want) 319 } 320 } 321 322 func buildTestProgram(fileName string) (string, error) { 323 tmpDir, err := ioutil.TempDir("", "runtest") 324 if err != nil { 325 return "", fmt.Errorf("TempDir() failed: %v", err) 326 } 327 bin := filepath.Join(tmpDir, fileName) 328 buildArgs := []string{"build", "-o", bin, fmt.Sprintf("./testdata/%s.go", fileName)} 329 s := runutil.NewSequence(nil, os.Stdin, os.Stdout, os.Stderr, false, true) 330 if err := s.Last("go", buildArgs...); err != nil { 331 return "", err 332 } 333 return bin, nil 334 }