func (a *cfApp) VerifySsh(instanceIndex int) { envCmd := cf.Cf("ssh", a.appName, "-i", strconv.Itoa(instanceIndex), "-c", `"/usr/bin/env"`) Expect(envCmd.Wait()).To(gexec.Exit(0)) output := string(envCmd.Buffer().Contents()) Expect(string(output)).To(MatchRegexp(fmt.Sprintf(`VCAP_APPLICATION=.*"application_name":"%s"`, a.appName))) Expect(string(output)).To(MatchRegexp(fmt.Sprintf("INSTANCE_INDEX=%d", instanceIndex))) Eventually(cf.Cf("logs", a.appName, "--recent")).Should(gbytes.Say("Successful remote access")) Eventually(cf.Cf("events", a.appName)).Should(gbytes.Say("audit.app.ssh-authorized")) }
func (runner *clusterTestRunner) buildDroplet(timeout time.Duration, dropletName, buildpack, srcDir string) { fmt.Fprintln(getStyledWriter("test"), colors.PurpleUnderline(fmt.Sprintf("Submitting build of %s with buildpack %s", dropletName, buildpack))) command := runner.command("build-droplet", dropletName, buildpack, "--timeout", timeout.String()) command.Dir = srcDir session, err := gexec.Start(command, getStyledWriter("build-droplet"), getStyledWriter("build-droplet")) Expect(err).NotTo(HaveOccurred()) expectExit(timeout, session) Expect(session.Out).To(gbytes.Say("Submitted build of " + dropletName)) Expect(session.Out).NotTo(gbytes.Say("use of closed network connection")) }
func startMainWithArgs(args ...string) *gexec.Session { command := exec.Command(garagepiBinPath, args...) session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) Expect(err).NotTo(HaveOccurred()) Eventually(session).Should(gbytes.Say("garagepi starting")) return session }
func launchExternalHTTPReps() map[string]rep.SimClient { repNodeBinary, err := gexec.Build("github.com/cloudfoundry-incubator/auction/simulation/repnode") Expect(err).NotTo(HaveOccurred()) cells := map[string]rep.SimClient{} client := &http.Client{ Timeout: timeout, } for i := 0; i < numCells; i++ { repGuid := cellGuid(i) httpAddr := fmt.Sprintf("127.0.0.1:%d", 30000+i) serverCmd := exec.Command( repNodeBinary, "-repGuid", repGuid, "-httpAddr", httpAddr, "-memoryMB", fmt.Sprintf("%d", repResources.MemoryMB), "-diskMB", fmt.Sprintf("%d", repResources.DiskMB), "-containers", fmt.Sprintf("%d", repResources.Containers), "-stack", linuxStack, "-zone", zone(i), ) sess, err := gexec.Start(serverCmd, GinkgoWriter, GinkgoWriter) Expect(err).NotTo(HaveOccurred()) sessionsToTerminate = append(sessionsToTerminate, sess) Eventually(sess).Should(gbytes.Say("listening")) cells[cellGuid(i)] = rep.NewClient(client, "http://"+httpAddr).(rep.SimClient) } return cells }
func canCreateAndUseFuseFileSystem(container garden.Container, user string) { mountpoint := "/tmp/fuse-test" process, err := container.Run(garden.ProcessSpec{ User: user, Path: "mkdir", Args: []string{"-p", mountpoint}, }, garden.ProcessIO{Stdout: GinkgoWriter, Stderr: GinkgoWriter}) Expect(err).ToNot(HaveOccurred()) Expect(process.Wait()).To(Equal(0), "Could not make temporary directory!") process, err = container.Run(garden.ProcessSpec{ User: user, Path: "/usr/bin/hellofs", Args: []string{mountpoint}, }, garden.ProcessIO{Stdout: GinkgoWriter, Stderr: GinkgoWriter}) Expect(err).ToNot(HaveOccurred()) Expect(process.Wait()).To(Equal(0), "Failed to mount hello filesystem.") stdout := gbytes.NewBuffer() process, err = container.Run(garden.ProcessSpec{ User: user, Path: "cat", Args: []string{filepath.Join(mountpoint, "hello")}, }, garden.ProcessIO{Stdout: stdout, Stderr: GinkgoWriter}) Expect(err).ToNot(HaveOccurred()) Expect(process.Wait()).To(Equal(0), "Failed to find hello file.") Expect(stdout).To(gbytes.Say("Hello World!")) process, err = container.Run(garden.ProcessSpec{ User: user, Path: "fusermount", Args: []string{"-u", mountpoint}, }, garden.ProcessIO{Stdout: GinkgoWriter, Stderr: GinkgoWriter}) Expect(err).ToNot(HaveOccurred()) Expect(process.Wait()).To(Equal(0), "Failed to unmount user filesystem.") stdout2 := gbytes.NewBuffer() process, err = container.Run(garden.ProcessSpec{ User: user, Path: "ls", Args: []string{mountpoint}, }, garden.ProcessIO{Stdout: stdout2, Stderr: GinkgoWriter}) Expect(err).ToNot(HaveOccurred()) Expect(process.Wait()).To(Equal(0)) Expect(stdout2).ToNot(gbytes.Say("hello"), "Fuse filesystem appears still to be visible after being unmounted.") }
func startEncryptedTCPServer(syslogDrainAddress string) *gexec.Session { command := exec.Command(pathToTCPEchoServer, "-address", syslogDrainAddress, "-ssl", "-cert", "fixtures/key.crt", "-key", "fixtures/key.key") drainSession, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) Expect(err).NotTo(HaveOccurred()) Eventually(drainSession.Err, 10).Should(gbytes.Say("Startup: tcp echo server listening")) return drainSession }
func (e *Expector) matchOutput(pattern string) bool { select { case val := <-e.buffer.Detect(pattern): return val case <-e.closed: ok, err := gbytes.Say(pattern).Match(e.buffer) return ok && err == nil } }
func (runner *CLIRunner) start(command string, timestamp int, message string) *gexec.Session { cmd := exec.Command(runner.hm9000Binary, command, fmt.Sprintf("--config=%s", runner.configPath)) cmd.Env = append(os.Environ(), fmt.Sprintf("HM9000_FAKE_TIME=%d", timestamp)) session, err := gexec.Start(cmd, ginkgo.GinkgoWriter, ginkgo.GinkgoWriter) Ω(err).ShouldNot(HaveOccurred()) Eventually(session, 10*time.Second).Should(gbytes.Say(message)) return session }
func (runner *clusterTestRunner) scaleApp(timeout time.Duration, appName string, args ...string) { fmt.Fprintln(getStyledWriter("test"), colors.PurpleUnderline(fmt.Sprintf("Attempting to scale %s", appName))) command := runner.command("scale", appName, "3") session, err := gexec.Start(command, getStyledWriter("scale"), getStyledWriter("scale")) Expect(err).NotTo(HaveOccurred()) expectExit(timeout, session) Expect(session.Out).To(gbytes.Say("App Scaled Successfully")) }
func (runner *clusterTestRunner) launchDroplet(timeout time.Duration, appName, dropletName string, args ...string) { fmt.Fprintln(getStyledWriter("test"), colors.PurpleUnderline(fmt.Sprintf("Launching droplet %s as %s", dropletName, appName))) launchArgs := append([]string{"launch-droplet", appName, dropletName}, args...) command := runner.command(launchArgs...) session, err := gexec.Start(command, getStyledWriter("launch-droplet"), getStyledWriter("launch-droplet")) Expect(err).NotTo(HaveOccurred()) expectExit(timeout, session) Expect(session.Out).To(gbytes.Say(appName + " is now running.")) }
func boshCmd(manifest, action, completeMsg string) { args := []string{"-n"} if manifest != "" { args = append(args, "-d", manifest) } args = append(args, strings.Split(action, " ")...) cmd := bosh(args...) sess, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter) Expect(err).NotTo(HaveOccurred()) Eventually(sess, BOSH_DEPLOY_TIMEOUT).Should(gexec.Exit(0)) Expect(sess).To(gbytes.Say(completeMsg)) }
func (runner *clusterTestRunner) listDroplets(timeout time.Duration, dropletName string) { fmt.Fprintln(getStyledWriter("test"), colors.PurpleUnderline("Attempting to find droplet in the list")) command := runner.command("list-droplets") session, err := gexec.Start(command, getStyledWriter("list-droplets"), getStyledWriter("list-droplets")) Expect(err).NotTo(HaveOccurred()) expectExit(timeout, session) Expect(session.Out).To(gbytes.Say(dropletName)) fmt.Fprintln(getStyledWriter("test"), "Found", dropletName, "in the list!") }
func (runner *clusterTestRunner) removeDroplet(timeout time.Duration, dropletName string) { fmt.Fprintln(getStyledWriter("test"), colors.PurpleUnderline(fmt.Sprintf("Attempting to remove droplet %s", dropletName))) command := runner.command("remove-droplet", dropletName) session, err := gexec.Start(command, getStyledWriter("remove-droplet"), getStyledWriter("remove-droplet")) Expect(err).NotTo(HaveOccurred()) expectExit(timeout, session) Expect(session.Out).To(gbytes.Say("Droplet removed")) fmt.Fprintln(getStyledWriter("test"), "Removed", dropletName) }
func (runner *clusterTestRunner) updateApp(timeout time.Duration, appName string, args ...string) { fmt.Fprintln(getStyledWriter("test"), colors.PurpleUnderline(fmt.Sprintf("Attempting to update %s", appName))) updateArgs := append([]string{"update", appName}, args...) command := runner.command(updateArgs...) session, err := gexec.Start(command, getStyledWriter("update"), getStyledWriter("update")) Expect(err).NotTo(HaveOccurred()) expectExit(timeout, session) Expect(session.Out).To(gbytes.Say("Updating " + appName + " routes")) fmt.Fprintln(getStyledWriter("test"), "Yay! updated", appName) }
func (runner *clusterTestRunner) createDockerApp(timeout time.Duration, appName, dockerPath string, args ...string) { fmt.Fprintln(getStyledWriter("test"), colors.PurpleUnderline(fmt.Sprintf("Attempting to create %s", appName))) createArgs := append([]string{"create", appName, dockerPath}, args...) command := runner.command(createArgs...) session, err := gexec.Start(command, getStyledWriter("create"), getStyledWriter("create")) Expect(err).NotTo(HaveOccurred()) expectExit(timeout, session) Expect(session.Out).To(gbytes.Say(appName + " is now running.")) fmt.Fprintln(getStyledWriter("test"), "Yay! Created", appName) }
func (runner *integrationTestRunner) uploadBits(timeout time.Duration, dropletName, bits string) { fmt.Fprintln(getStyledWriter("test"), colors.PurpleUnderline(fmt.Sprintf("Attempting to upload %s to %s", bits, dropletName))) command := runner.command("upload-bits", dropletName, bits) session, err := gexec.Start(command, getStyledWriter("upload-bits"), getStyledWriter("upload-bits")) Expect(err).ToNot(HaveOccurred()) expectExit(timeout, session) Expect(session.Out).To(gbytes.Say("Successfully uploaded " + dropletName)) fmt.Fprintln(getStyledWriter("test"), "Uploaded", bits, "to", dropletName) }
func setupMetron() *gexec.Session { pathToMetronExecutable, err := gexec.Build("metron") Expect(err).ShouldNot(HaveOccurred()) command := exec.Command(pathToMetronExecutable, "--config=fixtures/metron.json", "--debug") metronSession, err := gexec.Start(command, gexec.NewPrefixedWriter("[o][metron]", GinkgoWriter), gexec.NewPrefixedWriter("[e][metron]", GinkgoWriter)) Expect(err).ShouldNot(HaveOccurred()) Eventually(metronSession.Buffer).Should(gbytes.Say("Chose protocol")) Consistently(metronSession.Exited).ShouldNot(BeClosed()) return metronSession }
func (c *cmdRunner) Run() *gexec.Session { cmd := c.session.Command cmdString := strings.Join(cmd.Args, " ") var exitCode int var failureMessage string for i := 0; i < c.attempts; i++ { // The first time through this loop we use the command that was provided, // which is already running. // After that we must explicitly start the command if i > 0 { newCmd := exec.Command(cmd.Args[0], cmd.Args[1:]...) c.session = innerRun(newCmd) } timer := time.NewTimer(c.timeout) select { case <-timer.C: failureMessage = fmt.Sprintf( "Timed out executing command (%v):\nCommand: %s\n\n[stdout]:\n%s\n\n[stderr]:\n%s", c.timeout.String(), cmdString, string(c.session.Out.Contents()), string(c.session.Err.Contents())) case <-c.session.Exited: // immediate kill the timer goroutine timer.Stop() // command may not have failed, but pre-construct failure message for final exit code expectation failureMessage = fmt.Sprintf( "Failed executing command (exit %d):\nCommand: %s\n\n[stdout]:\n%s\n\n[stderr]:\n%s", c.session.ExitCode(), cmdString, string(c.session.Out.Contents()), string(c.session.Err.Contents())) } exitCode = c.session.ExitCode() outputFound := strings.Contains(string(c.session.Buffer().Contents()), c.output) if exitCode == c.exitCode && outputFound { break } } Expect(exitCode).To(Equal(c.exitCode), failureMessage) Expect(c.session).To(gbytes.Say(c.output)) return c.session }
func (runner *clusterTestRunner) cloneRepo(timeout time.Duration, repoURL string) string { tmpDir, err := ioutil.TempDir("", "repo") Expect(err).NotTo(HaveOccurred()) fmt.Fprintln(getStyledWriter("test"), colors.PurpleUnderline(fmt.Sprintf("Attempting to clone %s to %s", repoURL, tmpDir))) command := exec.Command("/usr/bin/env", "git", "clone", repoURL, tmpDir) session, err := gexec.Start(command, getStyledWriter("git-clone"), getStyledWriter("git-clone")) Expect(err).NotTo(HaveOccurred()) expectExitInBuffer(timeout, session, session.Err) Eventually(session.Err).Should(gbytes.Say(fmt.Sprintf("Cloning into '%s'...", tmpDir))) fmt.Fprintf(getStyledWriter("test"), "Cloned %s into %s\n", repoURL, tmpDir) return tmpDir }
func startAuctioneers(numAuctioneers int) { auctioneerNodeBinary, err := gexec.Build("github.com/onsi/auction/auctioneernode") Ω(err).ShouldNot(HaveOccurred()) for i := 0; i < numAuctioneers; i++ { auctioneerCmd := exec.Command( auctioneerNodeBinary, "-natsAddr", fmt.Sprintf("127.0.0.1:%d", natsPort), "-timeout", fmt.Sprintf("%s", timeout), ) sess, err := gexec.Start(auctioneerCmd, GinkgoWriter, GinkgoWriter) Ω(err).ShouldNot(HaveOccurred()) Eventually(sess).Should(gbytes.Say("auctioneering")) sessionsToTerminate = append(sessionsToTerminate, sess) } }
func defineTheMainTests(runner *integrationTestRunner) { Describe("exit codes", func() { It("exits non-zero when an unknown command is invoked", func() { command := runner.command("unknownCommand") session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) Expect(err).ToNot(HaveOccurred()) Eventually(session, 3*time.Second).Should(gbytes.Say("not a registered command")) Eventually(session).Should(gexec.Exit(1)) }) It("exits non-zero when known command is invoked with invalid option", func() { command := runner.command("status", "--badFlag") session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) Expect(err).ToNot(HaveOccurred()) Eventually(session, 3*time.Second).Should(gexec.Exit(1)) }) }) }
func startClique(cfg config.Config, args ...string) (*runner.ClqProcess, error) { if useIperf && !cfg.UseIperf { cfg.UseIperf = true cfg.IperfPort = testhelpers.SelectPort(GinkgoParallelNode()) } configFile, err := ioutil.TempFile("", "clique-agent-config") if err != nil { return nil, err } configFilePath := configFile.Name() encoder := json.NewEncoder(configFile) if err := encoder.Encode(cfg); err != nil { configFile.Close() os.Remove(configFilePath) return nil, err } configFile.Close() finalArgs := []string{"-config", configFilePath, "-debug"} finalArgs = append(finalArgs, args...) cmd := exec.Command(cliqueAgentBin, finalArgs...) buffer := gbytes.NewBuffer() cmd.Stdout = io.MultiWriter(buffer, GinkgoWriter) cmd.Stderr = io.MultiWriter(buffer, GinkgoWriter) if err := cmd.Start(); err != nil { os.Remove(configFilePath) return nil, err } Eventually(buffer).Should(gbytes.Say("Clique Agent")) return &runner.ClqProcess{ Cmd: cmd, Buffer: buffer, Config: cfg, ConfigDirPath: configFilePath, }, nil }
func (r *Runner) Start() { if r.Session != nil && r.Session.ExitCode() == -1 { panic("starting more than one rep!!!") } args := []string{ "-cellID", r.config.CellID, "-listenAddr", fmt.Sprintf("0.0.0.0:%d", r.config.ServerPort), "-bbsAddress", r.config.BBSAddress, "-logLevel", r.config.LogLevel, "-pollingInterval", r.config.PollingInterval.String(), "-evacuationTimeout", r.config.EvacuationTimeout.String(), "-lockRetryInterval", "1s", "-consulCluster", r.config.ConsulCluster, "-containerMaxCpuShares", "1024", "-gardenNetwork", "tcp", "-gardenAddr", r.config.GardenAddr, "-gardenHealthcheckProcessUser", "me", "-gardenHealthcheckProcessPath", "ls", } for _, rootfs := range r.config.PreloadedRootFSes { args = append(args, "-preloadedRootFS", rootfs) } for _, provider := range r.config.RootFSProviders { args = append(args, "-rootFSProvider", provider) } repSession, err := gexec.Start( exec.Command( r.binPath, args..., ), gexec.NewPrefixedWriter("\x1b[32m[o]\x1b[32m[rep]\x1b[0m ", ginkgo.GinkgoWriter), gexec.NewPrefixedWriter("\x1b[91m[e]\x1b[32m[rep]\x1b[0m ", ginkgo.GinkgoWriter), ) Expect(err).NotTo(HaveOccurred()) r.Session = repSession Eventually(r.Session.Buffer(), 2).Should(gbytes.Say(r.StartCheck)) }
func (a *cfApp) Push() { // create org and space Eventually(func() int { return cf.Cf("login", "-a", "api."+config.OverrideDomain, "-u", CFUser, "-p", CFPassword, "--skip-ssl-validation").Wait().ExitCode() }).Should(Equal(0)) Eventually(cf.Cf("create-org", a.orgName)).Should(gexec.Exit(0)) Eventually(cf.Cf("target", "-o", a.orgName)).Should(gexec.Exit(0)) Eventually(cf.Cf("create-space", a.spaceName)).Should(gexec.Exit(0)) Eventually(cf.Cf("target", "-s", a.spaceName)).Should(gexec.Exit(0)) // push app Eventually(cf.Cf("push", a.appName, "-p", "dora", "-i", "1", "-b", "ruby_buildpack"), 5*time.Minute).Should(gexec.Exit(0)) Eventually(cf.Cf("logs", a.appName, "--recent")).Should(gbytes.Say("[HEALTH/0]")) curlAppMain := func() string { response, err := a.Curl("") if err != nil { return "" } return response } Eventually(curlAppMain).Should(ContainSubstring("Hi, I'm Dora!")) }
Expect(err).ToNot(HaveOccurred()) diffLayersAmt = len(diffFiles) mntFiles, err := ioutil.ReadDir(mntPath) Expect(err).ToNot(HaveOccurred()) mntLayersAmt = len(mntFiles) } BeforeEach(func() { imageLayersAmt = 0 diffLayersAmt = 0 mntLayersAmt = 0 }) JustBeforeEach(func() { Eventually(client, "30s").Should(gbytes.Say("retain.retained")) }) Context("and local images are used", func() { BeforeEach(func() { persistentImages = []string{runner.RootFSPath} }) Context("and destroying a container that uses a rootfs from the whitelist", func() { JustBeforeEach(func() { container, err := client.Create(garden.ContainerSpec{ RootFSPath: persistentImages[0], }) Expect(err).ToNot(HaveOccurred()) populateMetrics()
Data: []ui.TableRow{ {{Contents: "early-handle"}, {Contents: "git-repo"}, {Contents: "pipeline-name"}, {Contents: "get"}, {Contents: "123"}, {Contents: "worker-name-1"}}, {{Contents: "handle-1"}, {Contents: "git-repo"}, {Contents: "pipeline-name"}, {Contents: "check"}, {Contents: "none", Color: color.New(color.Faint)}, {Contents: "worker-name-1"}}, {{Contents: "other-handle"}, {Contents: "unit-tests"}, {Contents: "pipeline-name"}, {Contents: "task"}, {Contents: "122"}, {Contents: "worker-name-2"}}, }, })) Expect(flyCmd).To(HaveExited(0)) }) }) Context("and the api returns an internal server error", func() { BeforeEach(func() { atcServer.AppendHandlers( ghttp.CombineHandlers( ghttp.VerifyRequest("GET", "/api/v1/containers"), ghttp.RespondWith(500, ""), ), ) }) It("writes an error message to stderr", func() { sess, err := gexec.Start(flyCmd, nil, nil) Expect(err).ToNot(HaveOccurred()) Eventually(sess.Err).Should(gbytes.Say("Unexpected Response")) Eventually(sess).Should(gexec.Exit(1)) }) }) }) })
} func (f *TestRunner) Run() error { f.RunCalled = true return nil } var _ = Describe("Stager", func() { Describe("Running a buildpack", func() { It("should tell a buildpack runner to run", func() { buffer := gbytes.NewBuffer() testrunner := new(TestRunner) err := stager.RunBuildpack(buffer, testrunner) Expect(testrunner.RunCalled).To(Equal(true)) Expect(err).ShouldNot(HaveOccurred()) Eventually(buffer).Should(gbytes.Say(`Running Buildpacks...`)) }) }) Describe("Getting a buildpack runner", func() { It("should return the address of a valid buildpack runner, with a correct buildpack list", func() { buildpackDir, _ := ioutil.TempDir(os.TempDir(), "cfocker-buildpackrunner-test") os.Mkdir(buildpackDir+"/test-buildpack", 0755) ioutil.WriteFile(buildpackDir+"/test-buildpack"+"/testfile", []byte("test"), 0644) runner := stager.NewBuildpackRunner(buildpackDir) var runnerVar *buildpackrunner.Runner Expect(runner).Should(BeAssignableToTypeOf(runnerVar)) md5BuildpackName := fmt.Sprintf("%x", md5.Sum([]byte("test-buildpack"))) md5BuildpackDir, err := os.Open("/tmp/buildpacks") contents, err := md5BuildpackDir.Readdirnames(0) Expect(contents, err).Should(ContainElement(md5BuildpackName))
import ( "os/exec" "syscall" "code.cloudfoundry.org/guardian/rundmc/stopper" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/onsi/gomega/gbytes" "github.com/onsi/gomega/gexec" ) var _ = Describe("Killer", func() { It("kills the given processes", func() { cmd1 := exec.Command("sh", "-c", "trap 'exit 41' TERM; while true; do echo trapping; sleep 1; done") sess1, err := gexec.Start(cmd1, GinkgoWriter, GinkgoWriter) Expect(err).NotTo(HaveOccurred()) cmd2 := exec.Command("sh", "-c", "trap 'exit 41' TERM; while true; do echo trapping; sleep 1; done") sess2, err := gexec.Start(cmd2, GinkgoWriter, GinkgoWriter) Expect(err).NotTo(HaveOccurred()) Eventually(sess1).Should(gbytes.Say("trapping")) Eventually(sess2).Should(gbytes.Say("trapping")) stopper.DefaultKiller{}.Kill(syscall.SIGTERM, cmd1.Process.Pid, cmd2.Process.Pid) Eventually(sess1, "5s").Should(gexec.Exit(41)) Eventually(sess2, "5s").Should(gexec.Exit(41)) }) })
return fakeClient, expectedErr } }) AfterEach(func() { log.SetOutput(GinkgoWriter) }) It("exits with a non-zero exit code", func() { Enable() Expect(atomic.LoadInt32(&exitCode)).ToNot(BeZero()) }) It("logs the error", func() { Enable() Expect(buf).To(gbytes.Say(expectedErr.Error())) }) }) Context("when getting the app from env fails", func() { var ( origAppProvider = appProvider expectedErr = errors.New("some-error") buf io.ReadWriter ) BeforeEach(func() { buf = gbytes.NewBuffer() log.SetOutput(io.MultiWriter(buf, GinkgoWriter)) appProvider = func() (env.App, error) {
Context("when there are grandchildren processes", func() { It("waits for a process to return and returns its exit status", func() { cmd := exec.Command("sh", "-c", "sleep 1; exit 3") Expect(reaper.Start(cmd)).To(Succeed()) Expect(reaper.Wait(cmd)).To(Equal(byte(3))) }) It("the child process can receive SIGCHLD when a grandchild terminates", func() { stdout := gbytes.NewBuffer() trap := exec.Command("sh", "-c", "trap 'echo caught SIGCHLD' CHLD; (ls / >/dev/null 2/&1); exit 0") trap.Stdout = stdout Expect(reaper.Start(trap)).To(Succeed()) Expect(reaper.Wait(trap)).To(Equal(byte(0))) Eventually(stdout).Should(gbytes.Say("caught SIGCHLD\n")) }) }) It("returns correct exit statuses of short-lived processes", func(done Done) { for i := 0; i < 100; i++ { cmd := exec.Command("sh", "-c", "exit 42") Expect(reaper.Start(cmd)).To(Succeed()) cmd2 := exec.Command("sh", "-c", "exit 43") Expect(reaper.Start(cmd2)).To(Succeed()) cmd3 := exec.Command("sh", "-c", "exit 44") Expect(reaper.Start(cmd3)).To(Succeed()) exitStatus := reaper.Wait(cmd3)