1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
|
package integration_test
import (
"encoding/json"
"os/exec"
"time"
. "github.com/onsi/ginkgo/v2"
"github.com/onsi/ginkgo/v2/types"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/gbytes"
"github.com/onsi/gomega/gexec"
)
var _ = Describe("Interrupt and Timeout", func() {
Context("when interrupting a suite", func() {
It("gives the user feedback as the session is interrupted", func() {
fm.MountFixture("hanging")
//we need to signal the actual process, so we must compile the test first
session := startGinkgo(fm.PathTo("hanging"), "build")
Eventually(session).Should(gexec.Exit(0))
//then run the compiled test directly
cmd := exec.Command("./hanging.test", "--test.v", "--ginkgo.no-color", "--ginkgo.grace-period=2s")
cmd.Dir = fm.PathTo("hanging")
var err error
session, err = gexec.Start(cmd, GinkgoWriter, GinkgoWriter)
Ω(err).ShouldNot(HaveOccurred())
Eventually(session).Should(gbytes.Say("Sleeping..."))
session.Interrupt()
Eventually(session).Should(gbytes.Say(`First interrupt received`))
Eventually(session).Should(gbytes.Say("Begin Captured GinkgoWriter Output"))
Eventually(session).Should(gbytes.Say("Just beginning"))
Eventually(session).Should(gbytes.Say("Almost there..."))
Eventually(session).Should(gbytes.Say("Hanging Out"))
Eventually(session).Should(gbytes.Say(`goroutine \d+ \[select\]`))
Eventually(session).Should(gbytes.Say(`>\s*select {`), "The actual source code gets emitted")
Eventually(session, time.Second*5).Should(gbytes.Say("Cleaning up once..."), "The two second grace period should move on past the napping node")
Eventually(session).Should(gbytes.Say("Cleaning up twice..."))
Eventually(session).Should(gbytes.Say("Sleeping again..."))
session.Interrupt()
Eventually(session).Should(gbytes.Say(`Second interrupt received`))
Eventually(session).Should(gbytes.Say(`goroutine \d+ \[sleep\]`))
Eventually(session).Should(gbytes.Say(`>\s*time.Sleep\(time.Hour\)`), "The actual source code gets emitted now")
Eventually(session).Should(gbytes.Say(`\[INTERRUPTED\]`))
Eventually(session).Should(gbytes.Say(`Interrupted by User`))
Eventually(session).Should(gbytes.Say(`Spec Goroutine`))
Eventually(session).Should(gbytes.Say(`goroutine \d+ \[select\]`))
Eventually(session).Should(gbytes.Say(`>\s*select {`), "The actual source code gets emitted now")
Eventually(session).Should(gbytes.Say(`Other Goroutines`))
Eventually(session).Should(gbytes.Say(`main\.main\(\)`))
Eventually(session).Should(gbytes.Say("Reporting at the end"))
Eventually(session).Should(gbytes.Say(`FAIL! - Interrupted by User`))
Eventually(session, time.Second*10).Should(gexec.Exit(1))
// the last AfterEach and the AfterSuite don't actually run
Ω(string(session.Out.Contents())).ShouldNot(ContainSubstring("Cleaning up thrice"))
Ω(string(session.Out.Contents())).ShouldNot(ContainSubstring("Heading Out After Suite"))
})
})
Context("when the suite times out", func() {
It("interrupts the suite and gives the user feedback as it does so", func() {
fm.MountFixture("hanging")
session := startGinkgo(fm.PathTo("hanging"), "--no-color", "--timeout=5s", "--grace-period=1s")
Eventually(session, time.Second*10).Should(gexec.Exit(1))
Ω(session).Should(gbytes.Say("Sleeping..."))
Ω(session).Should(gbytes.Say("Got your signal, but still taking a nap"), "the timeout has signaled the it to stop, but it's napping...")
Ω(session).Should(gbytes.Say("A running node failed to exit in time"), "so we forcibly casue it to exit after the grace-period elapses")
Ω(session).Should(gbytes.Say("Cleaning up once..."))
Ω(session).Should(gbytes.Say("Cleaning up twice..."))
Ω(session).Should(gbytes.Say("Cleaning up thrice..."), "we manage to get here even though the second after-each gets stuck. that's thanks to the GracePeriod configuration.")
Ω(session).Should(gbytes.Say(`\[TIMEDOUT\]`))
Ω(session).Should(gbytes.Say(`Spec Goroutine`))
Ω(session).Should(gbytes.Say(`goroutine \d+ \[select\]`))
Ω(session).Should(gbytes.Say(`>\s*select {`), "The actual source code gets emitted now")
Ω(session).ShouldNot(gbytes.Say(`Other Goroutines`))
Ω(session).Should(gbytes.Say("FAIL! - Suite Timeout Elapsed"))
})
})
Describe("applying the timeout to multiple suites", func() {
It("tracks the timeout across the suites, decrementing the available timeout for each individual suite, and reports on any suites that did not run because the timeout elapsed", Label("slow"), func() {
fm.MountFixture("timeout")
session := startGinkgo(fm.PathTo("timeout"), "--no-color", "-r", "--timeout=10s", "--keep-going", "--json-report=out.json")
Eventually(session).Should(gbytes.Say("TimeoutA Suite"))
Eventually(session, "15s").Should(gexec.Exit(1))
Ω(session).Should(gbytes.Say(`timeout_D ./timeout_D \[Suite did not run because the timeout elapsed\]`))
data := []byte(fm.ContentOf("timeout", "out.json"))
reports := []types.Report{}
Ω(json.Unmarshal(data, &reports)).Should(Succeed())
Ω(reports[3].SpecialSuiteFailureReasons).Should(ContainElement("Suite did not run because the timeout elapsed"))
})
})
})
|