summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/onsi/gomega/gexec
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/onsi/gomega/gexec')
-rw-r--r--vendor/github.com/onsi/gomega/gexec/_fixture/firefly/main.go36
-rw-r--r--vendor/github.com/onsi/gomega/gexec/build.go99
-rw-r--r--vendor/github.com/onsi/gomega/gexec/build_test.go59
-rw-r--r--vendor/github.com/onsi/gomega/gexec/exit_matcher.go88
-rw-r--r--vendor/github.com/onsi/gomega/gexec/exit_matcher_test.go113
-rw-r--r--vendor/github.com/onsi/gomega/gexec/gexec_suite_test.go26
-rw-r--r--vendor/github.com/onsi/gomega/gexec/prefixed_writer.go53
-rw-r--r--vendor/github.com/onsi/gomega/gexec/prefixed_writer_test.go43
-rw-r--r--vendor/github.com/onsi/gomega/gexec/session.go305
-rw-r--r--vendor/github.com/onsi/gomega/gexec/session_test.go351
10 files changed, 1173 insertions, 0 deletions
diff --git a/vendor/github.com/onsi/gomega/gexec/_fixture/firefly/main.go b/vendor/github.com/onsi/gomega/gexec/_fixture/firefly/main.go
new file mode 100644
index 0000000..16091c2
--- /dev/null
+++ b/vendor/github.com/onsi/gomega/gexec/_fixture/firefly/main.go
@@ -0,0 +1,36 @@
+package main
+
+import (
+ "fmt"
+ "math/rand"
+ "os"
+ "strconv"
+ "time"
+)
+
+var outQuote = "We've done the impossible, and that makes us mighty."
+var errQuote = "Ah, curse your sudden but inevitable betrayal!"
+
+var randomQuotes = []string{
+ "Can we maybe vote on the whole murdering people issue?",
+ "I swear by my pretty floral bonnet, I will end you.",
+ "My work's illegal, but at least it's honest.",
+}
+
+func main() {
+ fmt.Fprintln(os.Stdout, outQuote)
+ fmt.Fprintln(os.Stderr, errQuote)
+
+ randomIndex := rand.New(rand.NewSource(time.Now().UnixNano())).Intn(len(randomQuotes))
+
+ time.Sleep(100 * time.Millisecond)
+
+ fmt.Fprintln(os.Stdout, randomQuotes[randomIndex])
+
+ if len(os.Args) == 2 {
+ exitCode, _ := strconv.Atoi(os.Args[1])
+ os.Exit(exitCode)
+ } else {
+ os.Exit(randomIndex)
+ }
+}
diff --git a/vendor/github.com/onsi/gomega/gexec/build.go b/vendor/github.com/onsi/gomega/gexec/build.go
new file mode 100644
index 0000000..d11b2fd
--- /dev/null
+++ b/vendor/github.com/onsi/gomega/gexec/build.go
@@ -0,0 +1,99 @@
+package gexec
+
+import (
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path"
+ "path/filepath"
+ "runtime"
+ "sync"
+)
+
+var (
+ mu sync.Mutex
+ tmpDir string
+)
+
+/*
+Build uses go build to compile the package at packagePath. The resulting binary is saved off in a temporary directory.
+A path pointing to this binary is returned.
+
+Build uses the $GOPATH set in your environment. It passes the variadic args on to `go build`.
+*/
+func Build(packagePath string, args ...string) (compiledPath string, err error) {
+ return doBuild(os.Getenv("GOPATH"), packagePath, nil, args...)
+}
+
+/*
+BuildWithEnvironment is identical to Build but allows you to specify env vars to be set at build time.
+*/
+func BuildWithEnvironment(packagePath string, env []string, args ...string) (compiledPath string, err error) {
+ return doBuild(os.Getenv("GOPATH"), packagePath, env, args...)
+}
+
+/*
+BuildIn is identical to Build but allows you to specify a custom $GOPATH (the first argument).
+*/
+func BuildIn(gopath string, packagePath string, args ...string) (compiledPath string, err error) {
+ return doBuild(gopath, packagePath, nil, args...)
+}
+
+func doBuild(gopath, packagePath string, env []string, args ...string) (compiledPath string, err error) {
+ tmpDir, err := temporaryDirectory()
+ if err != nil {
+ return "", err
+ }
+
+ if len(gopath) == 0 {
+ return "", errors.New("$GOPATH not provided when building " + packagePath)
+ }
+
+ executable := filepath.Join(tmpDir, path.Base(packagePath))
+ if runtime.GOOS == "windows" {
+ executable = executable + ".exe"
+ }
+
+ cmdArgs := append([]string{"build"}, args...)
+ cmdArgs = append(cmdArgs, "-o", executable, packagePath)
+
+ build := exec.Command("go", cmdArgs...)
+ build.Env = append([]string{"GOPATH=" + gopath}, os.Environ()...)
+ build.Env = append(build.Env, env...)
+
+ output, err := build.CombinedOutput()
+ if err != nil {
+ return "", fmt.Errorf("Failed to build %s:\n\nError:\n%s\n\nOutput:\n%s", packagePath, err, string(output))
+ }
+
+ return executable, nil
+}
+
+/*
+You should call CleanupBuildArtifacts before your test ends to clean up any temporary artifacts generated by
+gexec. In Ginkgo this is typically done in an AfterSuite callback.
+*/
+func CleanupBuildArtifacts() {
+ mu.Lock()
+ defer mu.Unlock()
+ if tmpDir != "" {
+ os.RemoveAll(tmpDir)
+ tmpDir = ""
+ }
+}
+
+func temporaryDirectory() (string, error) {
+ var err error
+ mu.Lock()
+ defer mu.Unlock()
+ if tmpDir == "" {
+ tmpDir, err = ioutil.TempDir("", "gexec_artifacts")
+ if err != nil {
+ return "", err
+ }
+ }
+
+ return ioutil.TempDir(tmpDir, "g")
+}
diff --git a/vendor/github.com/onsi/gomega/gexec/build_test.go b/vendor/github.com/onsi/gomega/gexec/build_test.go
new file mode 100644
index 0000000..8df0790
--- /dev/null
+++ b/vendor/github.com/onsi/gomega/gexec/build_test.go
@@ -0,0 +1,59 @@
+package gexec_test
+
+import (
+ "os"
+
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+ "github.com/onsi/gomega/gexec"
+)
+
+var packagePath = "./_fixture/firefly"
+
+var _ = Describe(".Build", func() {
+ Context("when there have been previous calls to Build", func() {
+ BeforeEach(func() {
+ _, err := gexec.Build(packagePath)
+ Ω(err).ShouldNot(HaveOccurred())
+ })
+
+ It("compiles the specified package", func() {
+ compiledPath, err := gexec.Build(packagePath)
+ Ω(err).ShouldNot(HaveOccurred())
+ Ω(compiledPath).Should(BeAnExistingFile())
+ })
+
+ Context("and CleanupBuildArtifacts has been called", func() {
+ BeforeEach(func() {
+ gexec.CleanupBuildArtifacts()
+ })
+
+ It("compiles the specified package", func() {
+ var err error
+ fireflyPath, err = gexec.Build(packagePath)
+ Ω(err).ShouldNot(HaveOccurred())
+ Ω(fireflyPath).Should(BeAnExistingFile())
+ })
+ })
+ })
+})
+
+var _ = Describe(".BuildWithEnvironment", func() {
+ var err error
+ env := []string{
+ "GOOS=linux",
+ "GOARCH=amd64",
+ }
+
+ It("compiles the specified package with the specified env vars", func() {
+ compiledPath, err := gexec.BuildWithEnvironment(packagePath, env)
+ Ω(err).ShouldNot(HaveOccurred())
+ Ω(compiledPath).Should(BeAnExistingFile())
+ })
+
+ It("returns the environment to a good state", func() {
+ _, err = gexec.BuildWithEnvironment(packagePath, env)
+ Ω(err).ShouldNot(HaveOccurred())
+ Ω(os.Environ()).ShouldNot(ContainElement("GOOS=linux"))
+ })
+})
diff --git a/vendor/github.com/onsi/gomega/gexec/exit_matcher.go b/vendor/github.com/onsi/gomega/gexec/exit_matcher.go
new file mode 100644
index 0000000..e6f4329
--- /dev/null
+++ b/vendor/github.com/onsi/gomega/gexec/exit_matcher.go
@@ -0,0 +1,88 @@
+package gexec
+
+import (
+ "fmt"
+
+ "github.com/onsi/gomega/format"
+)
+
+/*
+The Exit matcher operates on a session:
+
+ Ω(session).Should(Exit(<optional status code>))
+
+Exit passes if the session has already exited.
+
+If no status code is provided, then Exit will succeed if the session has exited regardless of exit code.
+Otherwise, Exit will only succeed if the process has exited with the provided status code.
+
+Note that the process must have already exited. To wait for a process to exit, use Eventually:
+
+ Eventually(session, 3).Should(Exit(0))
+*/
+func Exit(optionalExitCode ...int) *exitMatcher {
+ exitCode := -1
+ if len(optionalExitCode) > 0 {
+ exitCode = optionalExitCode[0]
+ }
+
+ return &exitMatcher{
+ exitCode: exitCode,
+ }
+}
+
+type exitMatcher struct {
+ exitCode int
+ didExit bool
+ actualExitCode int
+}
+
+type Exiter interface {
+ ExitCode() int
+}
+
+func (m *exitMatcher) Match(actual interface{}) (success bool, err error) {
+ exiter, ok := actual.(Exiter)
+ if !ok {
+ return false, fmt.Errorf("Exit must be passed a gexec.Exiter (Missing method ExitCode() int) Got:\n%s", format.Object(actual, 1))
+ }
+
+ m.actualExitCode = exiter.ExitCode()
+
+ if m.actualExitCode == -1 {
+ return false, nil
+ }
+
+ if m.exitCode == -1 {
+ return true, nil
+ }
+ return m.exitCode == m.actualExitCode, nil
+}
+
+func (m *exitMatcher) FailureMessage(actual interface{}) (message string) {
+ if m.actualExitCode == -1 {
+ return "Expected process to exit. It did not."
+ } else {
+ return format.Message(m.actualExitCode, "to match exit code:", m.exitCode)
+ }
+}
+
+func (m *exitMatcher) NegatedFailureMessage(actual interface{}) (message string) {
+ if m.actualExitCode == -1 {
+ return "you really shouldn't be able to see this!"
+ } else {
+ if m.exitCode == -1 {
+ return "Expected process not to exit. It did."
+ } else {
+ return format.Message(m.actualExitCode, "not to match exit code:", m.exitCode)
+ }
+ }
+}
+
+func (m *exitMatcher) MatchMayChangeInTheFuture(actual interface{}) bool {
+ session, ok := actual.(*Session)
+ if ok {
+ return session.ExitCode() == -1
+ }
+ return true
+}
diff --git a/vendor/github.com/onsi/gomega/gexec/exit_matcher_test.go b/vendor/github.com/onsi/gomega/gexec/exit_matcher_test.go
new file mode 100644
index 0000000..79615dd
--- /dev/null
+++ b/vendor/github.com/onsi/gomega/gexec/exit_matcher_test.go
@@ -0,0 +1,113 @@
+package gexec_test
+
+import (
+ . "github.com/onsi/gomega/gexec"
+ "os/exec"
+ "time"
+
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+)
+
+type NeverExits struct{}
+
+func (e NeverExits) ExitCode() int {
+ return -1
+}
+
+var _ = Describe("ExitMatcher", func() {
+ var command *exec.Cmd
+ var session *Session
+
+ BeforeEach(func() {
+ var err error
+ command = exec.Command(fireflyPath, "0")
+ session, err = Start(command, nil, nil)
+ Ω(err).ShouldNot(HaveOccurred())
+ })
+
+ Describe("when passed something that is an Exiter", func() {
+ It("should act normally", func() {
+ failures := InterceptGomegaFailures(func() {
+ Ω(NeverExits{}).Should(Exit())
+ })
+
+ Ω(failures[0]).Should(ContainSubstring("Expected process to exit. It did not."))
+ })
+ })
+
+ Describe("when passed something that is not an Exiter", func() {
+ It("should error", func() {
+ failures := InterceptGomegaFailures(func() {
+ Ω("aardvark").Should(Exit())
+ })
+
+ Ω(failures[0]).Should(ContainSubstring("Exit must be passed a gexec.Exiter"))
+ })
+ })
+
+ Context("with no exit code", func() {
+ It("should say the right things when it fails", func() {
+ Ω(session).ShouldNot(Exit())
+
+ failures := InterceptGomegaFailures(func() {
+ Ω(session).Should(Exit())
+ })
+
+ Ω(failures[0]).Should(ContainSubstring("Expected process to exit. It did not."))
+
+ Eventually(session).Should(Exit())
+
+ Ω(session).Should(Exit())
+
+ failures = InterceptGomegaFailures(func() {
+ Ω(session).ShouldNot(Exit())
+ })
+
+ Ω(failures[0]).Should(ContainSubstring("Expected process not to exit. It did."))
+ })
+ })
+
+ Context("with an exit code", func() {
+ It("should say the right things when it fails", func() {
+ Ω(session).ShouldNot(Exit(0))
+ Ω(session).ShouldNot(Exit(1))
+
+ failures := InterceptGomegaFailures(func() {
+ Ω(session).Should(Exit(0))
+ })
+
+ Ω(failures[0]).Should(ContainSubstring("Expected process to exit. It did not."))
+
+ Eventually(session).Should(Exit(0))
+
+ Ω(session).Should(Exit(0))
+
+ failures = InterceptGomegaFailures(func() {
+ Ω(session).Should(Exit(1))
+ })
+
+ Ω(failures[0]).Should(ContainSubstring("to match exit code:"))
+
+ Ω(session).ShouldNot(Exit(1))
+
+ failures = InterceptGomegaFailures(func() {
+ Ω(session).ShouldNot(Exit(0))
+ })
+
+ Ω(failures[0]).Should(ContainSubstring("not to match exit code:"))
+ })
+ })
+
+ Describe("bailing out early", func() {
+ It("should bail out early once the process exits", func() {
+ t := time.Now()
+
+ failures := InterceptGomegaFailures(func() {
+ Eventually(session).Should(Exit(1))
+ })
+ Ω(time.Since(t)).Should(BeNumerically("<=", 500*time.Millisecond))
+ Ω(failures).Should(HaveLen(1))
+ })
+ })
+})
diff --git a/vendor/github.com/onsi/gomega/gexec/gexec_suite_test.go b/vendor/github.com/onsi/gomega/gexec/gexec_suite_test.go
new file mode 100644
index 0000000..87672aa
--- /dev/null
+++ b/vendor/github.com/onsi/gomega/gexec/gexec_suite_test.go
@@ -0,0 +1,26 @@
+package gexec_test
+
+import (
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+ "github.com/onsi/gomega/gexec"
+
+ "testing"
+)
+
+var fireflyPath string
+
+func TestGexec(t *testing.T) {
+ BeforeSuite(func() {
+ var err error
+ fireflyPath, err = gexec.Build("./_fixture/firefly")
+ Ω(err).ShouldNot(HaveOccurred())
+ })
+
+ AfterSuite(func() {
+ gexec.CleanupBuildArtifacts()
+ })
+
+ RegisterFailHandler(Fail)
+ RunSpecs(t, "Gexec Suite")
+}
diff --git a/vendor/github.com/onsi/gomega/gexec/prefixed_writer.go b/vendor/github.com/onsi/gomega/gexec/prefixed_writer.go
new file mode 100644
index 0000000..05e695a
--- /dev/null
+++ b/vendor/github.com/onsi/gomega/gexec/prefixed_writer.go
@@ -0,0 +1,53 @@
+package gexec
+
+import (
+ "io"
+ "sync"
+)
+
+/*
+PrefixedWriter wraps an io.Writer, emiting the passed in prefix at the beginning of each new line.
+This can be useful when running multiple gexec.Sessions concurrently - you can prefix the log output of each
+session by passing in a PrefixedWriter:
+
+gexec.Start(cmd, NewPrefixedWriter("[my-cmd] ", GinkgoWriter), NewPrefixedWriter("[my-cmd] ", GinkgoWriter))
+*/
+type PrefixedWriter struct {
+ prefix []byte
+ writer io.Writer
+ lock *sync.Mutex
+ atStartOfLine bool
+}
+
+func NewPrefixedWriter(prefix string, writer io.Writer) *PrefixedWriter {
+ return &PrefixedWriter{
+ prefix: []byte(prefix),
+ writer: writer,
+ lock: &sync.Mutex{},
+ atStartOfLine: true,
+ }
+}
+
+func (w *PrefixedWriter) Write(b []byte) (int, error) {
+ w.lock.Lock()
+ defer w.lock.Unlock()
+
+ toWrite := []byte{}
+
+ for _, c := range b {
+ if w.atStartOfLine {
+ toWrite = append(toWrite, w.prefix...)
+ }
+
+ toWrite = append(toWrite, c)
+
+ w.atStartOfLine = c == '\n'
+ }
+
+ _, err := w.writer.Write(toWrite)
+ if err != nil {
+ return 0, err
+ }
+
+ return len(b), nil
+}
diff --git a/vendor/github.com/onsi/gomega/gexec/prefixed_writer_test.go b/vendor/github.com/onsi/gomega/gexec/prefixed_writer_test.go
new file mode 100644
index 0000000..8657d0c
--- /dev/null
+++ b/vendor/github.com/onsi/gomega/gexec/prefixed_writer_test.go
@@ -0,0 +1,43 @@
+package gexec_test
+
+import (
+ "bytes"
+
+ . "github.com/onsi/gomega/gexec"
+
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+)
+
+var _ = Describe("PrefixedWriter", func() {
+ var buffer *bytes.Buffer
+ var writer *PrefixedWriter
+ BeforeEach(func() {
+ buffer = &bytes.Buffer{}
+ writer = NewPrefixedWriter("[p]", buffer)
+ })
+
+ It("should emit the prefix on newlines", func() {
+ writer.Write([]byte("abc"))
+ writer.Write([]byte("def\n"))
+ writer.Write([]byte("hij\n"))
+ writer.Write([]byte("\n\n"))
+ writer.Write([]byte("klm\n\nnop"))
+ writer.Write([]byte(""))
+ writer.Write([]byte("qrs"))
+ writer.Write([]byte("\ntuv\nwx"))
+ writer.Write([]byte("yz\n\n"))
+
+ Ω(buffer.String()).Should(Equal(`[p]abcdef
+[p]hij
+[p]
+[p]
+[p]klm
+[p]
+[p]nopqrs
+[p]tuv
+[p]wxyz
+[p]
+`))
+ })
+})
diff --git a/vendor/github.com/onsi/gomega/gexec/session.go b/vendor/github.com/onsi/gomega/gexec/session.go
new file mode 100644
index 0000000..387a72c
--- /dev/null
+++ b/vendor/github.com/onsi/gomega/gexec/session.go
@@ -0,0 +1,305 @@
+/*
+Package gexec provides support for testing external processes.
+*/
+package gexec
+
+import (
+ "io"
+ "os"
+ "os/exec"
+ "reflect"
+ "sync"
+ "syscall"
+
+ . "github.com/onsi/gomega"
+ "github.com/onsi/gomega/gbytes"
+)
+
+const INVALID_EXIT_CODE = 254
+
+type Session struct {
+ //The wrapped command
+ Command *exec.Cmd
+
+ //A *gbytes.Buffer connected to the command's stdout
+ Out *gbytes.Buffer
+
+ //A *gbytes.Buffer connected to the command's stderr
+ Err *gbytes.Buffer
+
+ //A channel that will close when the command exits
+ Exited <-chan struct{}
+
+ lock *sync.Mutex
+ exitCode int
+}
+
+/*
+Start starts the passed-in *exec.Cmd command. It wraps the command in a *gexec.Session.
+
+The session pipes the command's stdout and stderr to two *gbytes.Buffers available as properties on the session: session.Out and session.Err.
+These buffers can be used with the gbytes.Say matcher to match against unread output:
+
+ Ω(session.Out).Should(gbytes.Say("foo-out"))
+ Ω(session.Err).Should(gbytes.Say("foo-err"))
+
+In addition, Session satisfies the gbytes.BufferProvider interface and provides the stdout *gbytes.Buffer. This allows you to replace the first line, above, with:
+
+ Ω(session).Should(gbytes.Say("foo-out"))
+
+When outWriter and/or errWriter are non-nil, the session will pipe stdout and/or stderr output both into the session *gybtes.Buffers and to the passed-in outWriter/errWriter.
+This is useful for capturing the process's output or logging it to screen. In particular, when using Ginkgo it can be convenient to direct output to the GinkgoWriter:
+
+ session, err := Start(command, GinkgoWriter, GinkgoWriter)
+
+This will log output when running tests in verbose mode, but - otherwise - will only log output when a test fails.
+
+The session wrapper is responsible for waiting on the *exec.Cmd command. You *should not* call command.Wait() yourself.
+Instead, to assert that the command has exited you can use the gexec.Exit matcher:
+
+ Ω(session).Should(gexec.Exit())
+
+When the session exits it closes the stdout and stderr gbytes buffers. This will short circuit any
+Eventuallys waiting for the buffers to Say something.
+*/
+func Start(command *exec.Cmd, outWriter io.Writer, errWriter io.Writer) (*Session, error) {
+ exited := make(chan struct{})
+
+ session := &Session{
+ Command: command,
+ Out: gbytes.NewBuffer(),
+ Err: gbytes.NewBuffer(),
+ Exited: exited,
+ lock: &sync.Mutex{},
+ exitCode: -1,
+ }
+
+ var commandOut, commandErr io.Writer
+
+ commandOut, commandErr = session.Out, session.Err
+
+ if outWriter != nil && !reflect.ValueOf(outWriter).IsNil() {
+ commandOut = io.MultiWriter(commandOut, outWriter)
+ }
+
+ if errWriter != nil && !reflect.ValueOf(errWriter).IsNil() {
+ commandErr = io.MultiWriter(commandErr, errWriter)
+ }
+
+ command.Stdout = commandOut
+ command.Stderr = commandErr
+
+ err := command.Start()
+ if err == nil {
+ go session.monitorForExit(exited)
+ trackedSessionsMutex.Lock()
+ defer trackedSessionsMutex.Unlock()
+ trackedSessions = append(trackedSessions, session)
+ }
+
+ return session, err
+}
+
+/*
+Buffer implements the gbytes.BufferProvider interface and returns s.Out
+This allows you to make gbytes.Say matcher assertions against stdout without having to reference .Out:
+
+ Eventually(session).Should(gbytes.Say("foo"))
+*/
+func (s *Session) Buffer() *gbytes.Buffer {
+ return s.Out
+}
+
+/*
+ExitCode returns the wrapped command's exit code. If the command hasn't exited yet, ExitCode returns -1.
+
+To assert that the command has exited it is more convenient to use the Exit matcher:
+
+ Eventually(s).Should(gexec.Exit())
+
+When the process exits because it has received a particular signal, the exit code will be 128+signal-value
+(See http://www.tldp.org/LDP/abs/html/exitcodes.html and http://man7.org/linux/man-pages/man7/signal.7.html)
+
+*/
+func (s *Session) ExitCode() int {
+ s.lock.Lock()
+ defer s.lock.Unlock()
+ return s.exitCode
+}
+
+/*
+Wait waits until the wrapped command exits. It can be passed an optional timeout.
+If the command does not exit within the timeout, Wait will trigger a test failure.
+
+Wait returns the session, making it possible to chain:
+
+ session.Wait().Out.Contents()
+
+will wait for the command to exit then return the entirety of Out's contents.
+
+Wait uses eventually under the hood and accepts the same timeout/polling intervals that eventually does.
+*/
+func (s *Session) Wait(timeout ...interface{}) *Session {
+ EventuallyWithOffset(1, s, timeout...).Should(Exit())
+ return s
+}
+
+/*
+Kill sends the running command a SIGKILL signal. It does not wait for the process to exit.
+
+If the command has already exited, Kill returns silently.
+
+The session is returned to enable chaining.
+*/
+func (s *Session) Kill() *Session {
+ if s.ExitCode() != -1 {
+ return s
+ }
+ s.Command.Process.Kill()
+ return s
+}
+
+/*
+Interrupt sends the running command a SIGINT signal. It does not wait for the process to exit.
+
+If the command has already exited, Interrupt returns silently.
+
+The session is returned to enable chaining.
+*/
+func (s *Session) Interrupt() *Session {
+ return s.Signal(syscall.SIGINT)
+}
+
+/*
+Terminate sends the running command a SIGTERM signal. It does not wait for the process to exit.
+
+If the command has already exited, Terminate returns silently.
+
+The session is returned to enable chaining.
+*/
+func (s *Session) Terminate() *Session {
+ return s.Signal(syscall.SIGTERM)
+}
+
+/*
+Signal sends the running command the passed in signal. It does not wait for the process to exit.
+
+If the command has already exited, Signal returns silently.
+
+The session is returned to enable chaining.
+*/
+func (s *Session) Signal(signal os.Signal) *Session {
+ if s.ExitCode() != -1 {
+ return s
+ }
+ s.Command.Process.Signal(signal)
+ return s
+}
+
+func (s *Session) monitorForExit(exited chan<- struct{}) {
+ err := s.Command.Wait()
+ s.lock.Lock()
+ s.Out.Close()
+ s.Err.Close()
+ status := s.Command.ProcessState.Sys().(syscall.WaitStatus)
+ if status.Signaled() {
+ s.exitCode = 128 + int(status.Signal())
+ } else {
+ exitStatus := status.ExitStatus()
+ if exitStatus == -1 && err != nil {
+ s.exitCode = INVALID_EXIT_CODE
+ }
+ s.exitCode = exitStatus
+ }
+ s.lock.Unlock()
+
+ close(exited)
+}
+
+var trackedSessions = []*Session{}
+var trackedSessionsMutex = &sync.Mutex{}
+
+/*
+Kill sends a SIGKILL signal to all the processes started by Run, and waits for them to exit.
+The timeout specified is applied to each process killed.
+
+If any of the processes already exited, KillAndWait returns silently.
+*/
+func KillAndWait(timeout ...interface{}) {
+ trackedSessionsMutex.Lock()
+ defer trackedSessionsMutex.Unlock()
+ for _, session := range trackedSessions {
+ session.Kill().Wait(timeout...)
+ }
+ trackedSessions = []*Session{}
+}
+
+/*
+Kill sends a SIGTERM signal to all the processes started by Run, and waits for them to exit.
+The timeout specified is applied to each process killed.
+
+If any of the processes already exited, TerminateAndWait returns silently.
+*/
+func TerminateAndWait(timeout ...interface{}) {
+ trackedSessionsMutex.Lock()
+ defer trackedSessionsMutex.Unlock()
+ for _, session := range trackedSessions {
+ session.Terminate().Wait(timeout...)
+ }
+}
+
+/*
+Kill sends a SIGKILL signal to all the processes started by Run.
+It does not wait for the processes to exit.
+
+If any of the processes already exited, Kill returns silently.
+*/
+func Kill() {
+ trackedSessionsMutex.Lock()
+ defer trackedSessionsMutex.Unlock()
+ for _, session := range trackedSessions {
+ session.Kill()
+ }
+}
+
+/*
+Terminate sends a SIGTERM signal to all the processes started by Run.
+It does not wait for the processes to exit.
+
+If any of the processes already exited, Terminate returns silently.
+*/
+func Terminate() {
+ trackedSessionsMutex.Lock()
+ defer trackedSessionsMutex.Unlock()
+ for _, session := range trackedSessions {
+ session.Terminate()
+ }
+}
+
+/*
+Signal sends the passed in signal to all the processes started by Run.
+It does not wait for the processes to exit.
+
+If any of the processes already exited, Signal returns silently.
+*/
+func Signal(signal os.Signal) {
+ trackedSessionsMutex.Lock()
+ defer trackedSessionsMutex.Unlock()
+ for _, session := range trackedSessions {
+ session.Signal(signal)
+ }
+}
+
+/*
+Interrupt sends the SIGINT signal to all the processes started by Run.
+It does not wait for the processes to exit.
+
+If any of the processes already exited, Interrupt returns silently.
+*/
+func Interrupt() {
+ trackedSessionsMutex.Lock()
+ defer trackedSessionsMutex.Unlock()
+ for _, session := range trackedSessions {
+ session.Interrupt()
+ }
+}
diff --git a/vendor/github.com/onsi/gomega/gexec/session_test.go b/vendor/github.com/onsi/gomega/gexec/session_test.go
new file mode 100644
index 0000000..b7841a0
--- /dev/null
+++ b/vendor/github.com/onsi/gomega/gexec/session_test.go
@@ -0,0 +1,351 @@
+package gexec_test
+
+import (
+ "os/exec"
+ "syscall"
+ "time"
+
+ . "github.com/onsi/gomega/gbytes"
+ . "github.com/onsi/gomega/gexec"
+
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+)
+
+var _ = Describe("Session", func() {
+ var command *exec.Cmd
+ var session *Session
+
+ var outWriter, errWriter *Buffer
+
+ BeforeEach(func() {
+ outWriter = nil
+ errWriter = nil
+ })
+
+ JustBeforeEach(func() {
+ command = exec.Command(fireflyPath)
+ var err error
+ session, err = Start(command, outWriter, errWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+ })
+
+ Context("running a command", func() {
+ It("should start the process", func() {
+ Ω(command.Process).ShouldNot(BeNil())
+ })
+
+ It("should wrap the process's stdout and stderr with gbytes buffers", func(done Done) {
+ Eventually(session.Out).Should(Say("We've done the impossible, and that makes us mighty"))
+ Eventually(session.Err).Should(Say("Ah, curse your sudden but inevitable betrayal!"))
+ defer session.Out.CancelDetects()
+
+ select {
+ case <-session.Out.Detect("Can we maybe vote on the whole murdering people issue"):
+ Eventually(session).Should(Exit(0))
+ case <-session.Out.Detect("I swear by my pretty floral bonnet, I will end you."):
+ Eventually(session).Should(Exit(1))
+ case <-session.Out.Detect("My work's illegal, but at least it's honest."):
+ Eventually(session).Should(Exit(2))
+ }
+
+ close(done)
+ })
+
+ It("should satisfy the gbytes.BufferProvider interface, passing Stdout", func() {
+ Eventually(session).Should(Say("We've done the impossible, and that makes us mighty"))
+ Eventually(session).Should(Exit())
+ })
+ })
+
+ Describe("providing the exit code", func() {
+ It("should provide the app's exit code", func() {
+ Ω(session.ExitCode()).Should(Equal(-1))
+
+ Eventually(session).Should(Exit())
+ Ω(session.ExitCode()).Should(BeNumerically(">=", 0))
+ Ω(session.ExitCode()).Should(BeNumerically("<", 3))
+ })
+ })
+
+ Describe("wait", func() {
+ It("should wait till the command exits", func() {
+ Ω(session.ExitCode()).Should(Equal(-1))
+ Ω(session.Wait().ExitCode()).Should(BeNumerically(">=", 0))
+ Ω(session.Wait().ExitCode()).Should(BeNumerically("<", 3))
+ })
+ })
+
+ Describe("exited", func() {
+ It("should close when the command exits", func() {
+ Eventually(session.Exited).Should(BeClosed())
+ Ω(session.ExitCode()).ShouldNot(Equal(-1))
+ })
+ })
+
+ Describe("kill", func() {
+ It("should kill the command and don't wait for it to exit", func() {
+ session, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ session.Kill()
+ Ω(session).ShouldNot(Exit(), "Should not exit immediately...")
+ Eventually(session).Should(Exit(128 + 9))
+ })
+ })
+
+ Describe("interrupt", func() {
+ It("should interrupt the command", func() {
+ session, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ session.Interrupt()
+ Ω(session).ShouldNot(Exit(), "Should not exit immediately...")
+ Eventually(session).Should(Exit(128 + 2))
+ })
+ })
+
+ Describe("terminate", func() {
+ It("should terminate the command", func() {
+ session, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ session.Terminate()
+ Ω(session).ShouldNot(Exit(), "Should not exit immediately...")
+ Eventually(session).Should(Exit(128 + 15))
+ })
+ })
+
+ Describe("signal", func() {
+ It("should send the signal to the command", func() {
+ session, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ session.Signal(syscall.SIGABRT)
+ Ω(session).ShouldNot(Exit(), "Should not exit immediately...")
+ Eventually(session).Should(Exit(128 + 6))
+ })
+ })
+
+ Context("tracking sessions", func() {
+ BeforeEach(func() {
+ KillAndWait()
+ })
+
+ Describe("kill", func() {
+ It("should kill all the started sessions", func() {
+ session1, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ session2, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ session3, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ Kill()
+
+ Eventually(session1).Should(Exit(128 + 9))
+ Eventually(session2).Should(Exit(128 + 9))
+ Eventually(session3).Should(Exit(128 + 9))
+ })
+
+ It("should not wait for exit", func() {
+ session1, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ Kill()
+ Ω(session1).ShouldNot(Exit(), "Should not exit immediately...")
+
+ Eventually(session1).Should(Exit(128 + 9))
+ })
+
+ It("should not track unstarted sessions", func() {
+ _, err := Start(exec.Command("does not exist", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).Should(HaveOccurred())
+
+ session2, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ session3, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ Kill()
+
+ Eventually(session2).Should(Exit(128 + 9))
+ Eventually(session3).Should(Exit(128 + 9))
+ })
+
+ })
+
+ Describe("killAndWait", func() {
+ It("should kill all the started sessions and wait for them to finish", func() {
+ session1, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ session2, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ session3, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ KillAndWait()
+ Ω(session1).Should(Exit(128+9), "Should have exited")
+ Ω(session2).Should(Exit(128+9), "Should have exited")
+ Ω(session3).Should(Exit(128+9), "Should have exited")
+ })
+ })
+
+ Describe("terminate", func() {
+ It("should terminate all the started sessions", func() {
+ session1, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ session2, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ session3, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ Terminate()
+
+ Eventually(session1).Should(Exit(128 + 15))
+ Eventually(session2).Should(Exit(128 + 15))
+ Eventually(session3).Should(Exit(128 + 15))
+ })
+
+ It("should not wait for exit", func() {
+ session1, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ Terminate()
+
+ Ω(session1).ShouldNot(Exit(), "Should not exit immediately...")
+ })
+ })
+
+ Describe("terminateAndWait", func() {
+ It("should terminate all the started sessions, and wait for them to exit", func() {
+ session1, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ session2, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ session3, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ TerminateAndWait()
+
+ Ω(session1).Should(Exit(128+15), "Should have exited")
+ Ω(session2).Should(Exit(128+15), "Should have exited")
+ Ω(session3).Should(Exit(128+15), "Should have exited")
+ })
+ })
+
+ Describe("signal", func() {
+ It("should signal all the started sessions", func() {
+ session1, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ session2, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ session3, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ Signal(syscall.SIGABRT)
+
+ Eventually(session1).Should(Exit(128 + 6))
+ Eventually(session2).Should(Exit(128 + 6))
+ Eventually(session3).Should(Exit(128 + 6))
+ })
+
+ It("should not wait", func() {
+ session1, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ Signal(syscall.SIGABRT)
+
+ Ω(session1).ShouldNot(Exit(), "Should not exit immediately...")
+ })
+ })
+
+ Describe("interrupt", func() {
+ It("should interrupt all the started sessions, and not wait", func() {
+ session1, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ session2, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ session3, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ Interrupt()
+
+ Eventually(session1).Should(Exit(128 + 2))
+ Eventually(session2).Should(Exit(128 + 2))
+ Eventually(session3).Should(Exit(128 + 2))
+ })
+
+ It("should not wait", func() {
+ session1, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ Interrupt()
+
+ Ω(session1).ShouldNot(Exit(), "Should not exit immediately...")
+ })
+ })
+ })
+
+ Context("when the command exits", func() {
+ It("should close the buffers", func() {
+ Eventually(session).Should(Exit())
+
+ Ω(session.Out.Closed()).Should(BeTrue())
+ Ω(session.Err.Closed()).Should(BeTrue())
+
+ Ω(session.Out).Should(Say("We've done the impossible, and that makes us mighty"))
+ })
+
+ var So = It
+
+ So("this means that eventually should short circuit", func() {
+ t := time.Now()
+ failures := InterceptGomegaFailures(func() {
+ Eventually(session).Should(Say("blah blah blah blah blah"))
+ })
+ Ω(time.Since(t)).Should(BeNumerically("<=", 500*time.Millisecond))
+ Ω(failures).Should(HaveLen(1))
+ })
+ })
+
+ Context("when wrapping out and err", func() {
+ BeforeEach(func() {
+ outWriter = NewBuffer()
+ errWriter = NewBuffer()
+ })
+
+ It("should route to both the provided writers and the gbytes buffers", func() {
+ Eventually(session.Out).Should(Say("We've done the impossible, and that makes us mighty"))
+ Eventually(session.Err).Should(Say("Ah, curse your sudden but inevitable betrayal!"))
+
+ Ω(outWriter.Contents()).Should(ContainSubstring("We've done the impossible, and that makes us mighty"))
+ Ω(errWriter.Contents()).Should(ContainSubstring("Ah, curse your sudden but inevitable betrayal!"))
+
+ Eventually(session).Should(Exit())
+
+ Ω(outWriter.Contents()).Should(Equal(session.Out.Contents()))
+ Ω(errWriter.Contents()).Should(Equal(session.Err.Contents()))
+ })
+ })
+
+ Describe("when the command fails to start", func() {
+ It("should return an error", func() {
+ _, err := Start(exec.Command("agklsjdfas"), nil, nil)
+ Ω(err).Should(HaveOccurred())
+ })
+ })
+})