github.com/Benchkram/bob@v0.0.0-20220321080157-7c8f3876e225/pkg/execctl/cmd_test.go (about) 1 package execctl 2 3 import ( 4 "bufio" 5 . "github.com/onsi/ginkgo" 6 . "github.com/onsi/gomega" 7 "os" 8 "testing" 9 ) 10 11 var ( 12 // script that can be interrupted and does some cleanup if it is 13 script = []byte(` 14 cleanup() { 15 echo "interrupted" 16 sleep 1 17 echo "exited" 18 exit 0 19 } 20 21 trap cleanup INT 22 23 sleep .5 24 25 echo "running" 26 sleep 1 27 echo "exited" 28 exit 0 29 `) 30 // script that just errors 31 scriptErr = []byte(` 32 exit -1 33 `) 34 // script that prints the user-provided input to stderr 35 scriptEchoErr = []byte(` 36 read var 37 echo $var >&2 38 exit 0 39 `) 40 ) 41 42 var ( 43 tmpDir string 44 scriptPath string 45 scriptErrPath string 46 scriptEchoErrPath string 47 ) 48 49 var _ = BeforeSuite(func() { 50 var err error 51 tmpDir, err = os.MkdirTemp("", "execctl-*") 52 Expect(err).NotTo(HaveOccurred()) 53 54 scriptPath, err = createTempScript(tmpDir, script) 55 Expect(err).NotTo(HaveOccurred()) 56 Expect(scriptPath).NotTo(BeEmpty()) 57 58 scriptErrPath, err = createTempScript(tmpDir, scriptErr) 59 Expect(err).NotTo(HaveOccurred()) 60 Expect(scriptErrPath).NotTo(BeEmpty()) 61 62 scriptEchoErrPath, err = createTempScript(tmpDir, scriptEchoErr) 63 Expect(err).NotTo(HaveOccurred()) 64 Expect(scriptErrPath).NotTo(BeEmpty()) 65 }) 66 67 var _ = AfterSuite(func() { 68 err := os.RemoveAll(tmpDir) 69 Expect(err).NotTo(HaveOccurred()) 70 }) 71 72 var _ = Describe("Test command start and wait", func() { 73 var cmd *Cmd 74 var r *bufio.Reader 75 76 It("command started", func() { 77 var err error 78 cmd, err = NewCmd("test", "/bin/bash", "-c", scriptPath) 79 Expect(err).NotTo(HaveOccurred()) 80 81 err = cmd.Start() 82 Expect(err).NotTo(HaveOccurred()) 83 84 r = bufio.NewReader(cmd.Stdout()) 85 }) 86 87 It("command exited gracefully", func() { 88 err := cmd.Wait() 89 Expect(err).NotTo(HaveOccurred()) 90 91 l, err := readLine(r) 92 Expect(err).NotTo(HaveOccurred()) 93 Expect(l).To(Equal("running")) 94 95 l, err = readLine(r) 96 Expect(err).NotTo(HaveOccurred()) 97 Expect(l).To(Equal("exited")) 98 }) 99 }) 100 101 var _ = Describe("Test command stop", func() { 102 var cmd *Cmd 103 var r *bufio.Reader 104 105 It("command started", func() { 106 var err error 107 cmd, err = NewCmd("test", "/bin/bash", "-c", scriptPath) 108 Expect(err).NotTo(HaveOccurred()) 109 110 err = cmd.Start() 111 Expect(err).NotTo(HaveOccurred()) 112 113 r = bufio.NewReader(cmd.Stdout()) 114 }) 115 116 It("command interrupted", func() { 117 // allow the command to start running but don't give it enough time to exit gracefully 118 l, err := readLine(r) 119 Expect(err).NotTo(HaveOccurred()) 120 Expect(l).To(Equal("running")) 121 122 err = cmd.Stop() 123 Expect(err).NotTo(HaveOccurred()) 124 125 l, err = readLine(r) 126 Expect(err).NotTo(HaveOccurred()) 127 Expect(l).To(Equal("interrupted")) 128 129 l, err = readLine(r) 130 Expect(err).NotTo(HaveOccurred()) 131 Expect(l).To(Equal("exited")) 132 }) 133 }) 134 135 var _ = Describe("Test command stop when already exited gracefully", func() { 136 var cmd *Cmd 137 var r *bufio.Reader 138 139 It("command started", func() { 140 var err error 141 cmd, err = NewCmd("test", "/bin/bash", "-c", scriptPath) 142 Expect(err).NotTo(HaveOccurred()) 143 144 err = cmd.Start() 145 Expect(err).NotTo(HaveOccurred()) 146 147 r = bufio.NewReader(cmd.Stdout()) 148 }) 149 150 It("command was not interrupted (it was allowed to gracefully exit)", func() { 151 // let the command exit gracefully 152 l, err := readLine(r) 153 Expect(err).NotTo(HaveOccurred()) 154 Expect(l).To(Equal("running")) 155 156 l, err = readLine(r) 157 Expect(err).NotTo(HaveOccurred()) 158 Expect(l).To(Equal("exited")) 159 160 err = cmd.Stop() 161 Expect(err).NotTo(HaveOccurred()) 162 }) 163 }) 164 165 var _ = Describe("Test command manual restart", func() { 166 var cmd *Cmd 167 var r *bufio.Reader 168 169 It("command started", func() { 170 var err error 171 cmd, err = NewCmd("test", "/bin/bash", "-c", scriptPath) 172 Expect(err).NotTo(HaveOccurred()) 173 174 err = cmd.Start() 175 Expect(err).NotTo(HaveOccurred()) 176 177 r = bufio.NewReader(cmd.Stdout()) 178 }) 179 180 It("command interrupted", func() { 181 // don't give the command enough time to exit gracefully 182 l, err := readLine(r) 183 Expect(err).NotTo(HaveOccurred()) 184 Expect(l).To(Equal("running")) 185 186 err = cmd.Stop() 187 Expect(err).NotTo(HaveOccurred()) 188 189 l, err = readLine(r) 190 Expect(err).NotTo(HaveOccurred()) 191 Expect(l).To(Equal("interrupted")) 192 193 l, err = readLine(r) 194 Expect(err).NotTo(HaveOccurred()) 195 Expect(l).To(Equal("exited")) 196 }) 197 198 It("command started again and exited gracefully", func() { 199 err := cmd.Start() 200 Expect(err).NotTo(HaveOccurred()) 201 202 err = cmd.Wait() 203 Expect(err).NotTo(HaveOccurred()) 204 205 l, err := readLine(r) 206 Expect(err).NotTo(HaveOccurred()) 207 Expect(l).To(Equal("running")) 208 209 l, err = readLine(r) 210 Expect(err).NotTo(HaveOccurred()) 211 Expect(l).To(Equal("exited")) 212 }) 213 }) 214 215 var _ = Describe("Test command restart", func() { 216 var cmd *Cmd 217 var r *bufio.Reader 218 219 It("command started", func() { 220 var err error 221 cmd, err = NewCmd("test", "/bin/bash", "-c", scriptPath) 222 Expect(err).NotTo(HaveOccurred()) 223 224 err = cmd.Start() 225 Expect(err).NotTo(HaveOccurred()) 226 227 r = bufio.NewReader(cmd.Stdout()) 228 }) 229 230 It("command is restarted (interrupted and then started and exits gracefully)", func() { 231 // interrupt the command with a restart 232 l, err := readLine(r) 233 Expect(err).NotTo(HaveOccurred()) 234 Expect(l).To(Equal("running")) 235 236 err = cmd.Restart() 237 Expect(err).NotTo(HaveOccurred()) 238 239 l, err = readLine(r) 240 Expect(err).NotTo(HaveOccurred()) 241 Expect(l).To(Equal("interrupted")) 242 243 l, err = readLine(r) 244 Expect(err).NotTo(HaveOccurred()) 245 Expect(l).To(Equal("exited")) 246 247 err = cmd.Wait() 248 Expect(err).NotTo(HaveOccurred()) 249 250 l, err = readLine(r) 251 Expect(err).NotTo(HaveOccurred()) 252 Expect(l).To(Equal("running")) 253 254 l, err = readLine(r) 255 Expect(err).NotTo(HaveOccurred()) 256 Expect(l).To(Equal("exited")) 257 }) 258 }) 259 260 var _ = Describe("Test Wait() called multiple times on command that succeeded", func() { 261 var cmd *Cmd 262 var r *bufio.Reader 263 264 It("command started", func() { 265 var err error 266 cmd, err = NewCmd("test", "/bin/bash", "-c", scriptPath) 267 Expect(err).NotTo(HaveOccurred()) 268 269 err = cmd.Start() 270 Expect(err).NotTo(HaveOccurred()) 271 272 r = bufio.NewReader(cmd.Stdout()) 273 }) 274 275 It("command is awaited multiple times without errors", func() { 276 err := cmd.Wait() 277 Expect(err).NotTo(HaveOccurred()) 278 279 err = cmd.Wait() 280 Expect(err).NotTo(HaveOccurred()) 281 282 l, err := readLine(r) 283 Expect(err).NotTo(HaveOccurred()) 284 Expect(l).To(Equal("running")) 285 286 l, err = readLine(r) 287 Expect(err).NotTo(HaveOccurred()) 288 Expect(l).To(Equal("exited")) 289 }) 290 }) 291 292 var _ = Describe("Test Wait() called multiple times on command that returned error", func() { 293 var cmd *Cmd 294 295 It("command started", func() { 296 var err error 297 cmd, err = NewCmd("test", "/bin/bash", "-c", scriptErrPath) 298 Expect(err).NotTo(HaveOccurred()) 299 300 err = cmd.Start() 301 Expect(err).NotTo(HaveOccurred()) 302 }) 303 304 It("command awaited multiple times and returned an error on all", func() { 305 err := cmd.Wait() 306 Expect(err).To(HaveOccurred()) // original interrupt error 307 308 err = cmd.Wait() 309 Expect(err).To(HaveOccurred()) // from cmd.lastErr 310 }) 311 }) 312 313 var _ = Describe("Test Stop() called multiple times on command that returned error", func() { 314 var cmd *Cmd 315 316 It("command started", func() { 317 var err error 318 cmd, err = NewCmd("test", "/bin/bash", "-c", scriptErrPath) 319 Expect(err).NotTo(HaveOccurred()) 320 321 err = cmd.Start() 322 Expect(err).NotTo(HaveOccurred()) 323 }) 324 325 It("command stopped multiple times and returned an error on all", func() { 326 err := cmd.Wait() 327 Expect(err).To(HaveOccurred()) // original exit error 328 329 err = cmd.Stop() 330 Expect(err).To(HaveOccurred()) // from cmd.lastErr 331 332 err = cmd.Stop() 333 Expect(err).To(HaveOccurred()) // from cmd.lastErr 334 }) 335 }) 336 337 var _ = Describe("Test Start() called multiple times", func() { 338 var cmd *Cmd 339 340 It("command started multiple times", func() { 341 var err error 342 cmd, err = NewCmd("test", "/bin/bash", "-c", scriptErrPath) 343 Expect(err).NotTo(HaveOccurred()) 344 345 err = cmd.Start() 346 Expect(err).NotTo(HaveOccurred()) 347 348 err = cmd.Start() 349 Expect(err).NotTo(HaveOccurred()) 350 }) 351 }) 352 353 var _ = Describe("Test Stdin() and Stderr()", func() { 354 var cmd *Cmd 355 var r *bufio.Reader 356 357 It("command started multiple times", func() { 358 var err error 359 cmd, err = NewCmd("test", "/bin/bash", "-c", scriptEchoErrPath) 360 Expect(err).NotTo(HaveOccurred()) 361 362 err = cmd.Start() 363 Expect(err).NotTo(HaveOccurred()) 364 365 r = bufio.NewReader(cmd.Stderr()) 366 }) 367 368 It("command echoed user input to stderr", func() { 369 in := "ping!" 370 lf := "\n" 371 372 _, err := cmd.Stdin().Write([]byte(in + lf)) 373 Expect(err).NotTo(HaveOccurred()) 374 375 err = cmd.Wait() 376 Expect(err).NotTo(HaveOccurred()) 377 378 l, err := readLine(r) 379 Expect(err).NotTo(HaveOccurred()) 380 Expect(l).To(Equal(in)) 381 }) 382 }) 383 384 func TestExecctl(t *testing.T) { 385 RegisterFailHandler(Fail) 386 RunSpecs(t, "execctl suite") 387 } 388 389 func createTempScript(dir string, b []byte) (string, error) { 390 f, err := os.CreateTemp(dir, "shell-*.sh") 391 if err != nil { 392 return "", err 393 } 394 395 path := f.Name() 396 397 _, err = f.Write(b) 398 if err != nil { 399 return "", err 400 } 401 402 err = f.Chmod(0775) 403 if err != nil { 404 return "", err 405 } 406 407 err = f.Close() 408 if err != nil { 409 return "", err 410 } 411 412 return path, nil 413 } 414 415 func readLine(r *bufio.Reader) (string, error) { 416 l, _, err := r.ReadLine() 417 if err != nil { 418 return "", err 419 } 420 return string(l), nil 421 }