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 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
|
//go:build freebsd || openbsd || netbsd || dragonfly || darwin || linux || solaris
// +build freebsd openbsd netbsd dragonfly darwin linux solaris
package interrupt_handler_test
import (
"syscall"
"time"
. "github.com/onsi/ginkgo/v2"
"github.com/onsi/ginkgo/v2/internal/interrupt_handler"
"github.com/onsi/ginkgo/v2/internal/parallel_support"
. "github.com/onsi/ginkgo/v2/internal/test_helpers"
. "github.com/onsi/gomega"
)
var _ = Describe("InterruptHandler", func() {
var trigger func()
var interruptHandler *interrupt_handler.InterruptHandler
BeforeEach(func() {
trigger = func() {
syscall.Kill(syscall.Getpid(), syscall.SIGUSR2)
}
})
Describe("Signal interrupts", func() {
BeforeEach(func() {
interruptHandler = interrupt_handler.NewInterruptHandler(nil, syscall.SIGUSR2)
DeferCleanup(interruptHandler.Stop)
}, OncePerOrdered)
It("starts off uninterrupted", func() {
status := interruptHandler.Status()
Ω(status.Interrupted()).Should(BeFalse())
Ω(status.Cause).Should(Equal(interrupt_handler.InterruptCauseInvalid))
Ω(status.Level).Should(Equal(interrupt_handler.InterruptLevelUninterrupted))
Consistently(status.Channel).ShouldNot(BeClosed())
})
When("interrupted repeatedly", Ordered, func() {
var status interrupt_handler.InterruptStatus
BeforeAll(func() {
status = interruptHandler.Status()
Ω(status.Interrupted()).Should(BeFalse())
})
Specify("when first interrupted, it closes the channel and goes to the next Cleanup and Report level", func() {
trigger()
Eventually(status.Channel).Should(BeClosed())
status = interruptHandler.Status()
Ω(status.Interrupted()).Should(BeTrue())
Ω(status.Cause).Should(Equal(interrupt_handler.InterruptCauseSignal))
Ω(status.Message()).Should(Equal("Interrupted by User"))
Ω(status.Level).Should(Equal(interrupt_handler.InterruptLevelCleanupAndReport))
Ω(status.ShouldIncludeProgressReport()).Should(BeTrue())
})
Specify("when interrupted a second time, it closes the next channel and goes to the Report Only level", func() {
trigger()
Eventually(status.Channel).Should(BeClosed())
status = interruptHandler.Status()
Ω(status.Interrupted()).Should(BeTrue())
Ω(status.Cause).Should(Equal(interrupt_handler.InterruptCauseSignal))
Ω(status.Message()).Should(Equal("Interrupted by User"))
Ω(status.Level).Should(Equal(interrupt_handler.InterruptLevelReportOnly))
Ω(status.ShouldIncludeProgressReport()).Should(BeTrue())
})
Specify("when interrupted a third time, it closes the next channel and goes to the Bail-Out level", func() {
trigger()
Eventually(status.Channel).Should(BeClosed())
status = interruptHandler.Status()
Ω(status.Interrupted()).Should(BeTrue())
Ω(status.Cause).Should(Equal(interrupt_handler.InterruptCauseSignal))
Ω(status.Message()).Should(Equal("Interrupted by User"))
Ω(status.Level).Should(Equal(interrupt_handler.InterruptLevelBailOut))
Ω(status.ShouldIncludeProgressReport()).Should(BeTrue())
})
Specify("when interrupted again and again, it no longer interrupts once the last level has been reached", func() {
trigger()
Consistently(status.Channel).ShouldNot(BeClosed())
trigger()
Consistently(status.Channel).ShouldNot(BeClosed())
status = interruptHandler.Status()
Ω(status.Interrupted()).Should(BeTrue())
Ω(status.Cause).Should(Equal(interrupt_handler.InterruptCauseSignal))
Ω(status.Message()).Should(Equal("Interrupted by User"))
Ω(status.Level).Should(Equal(interrupt_handler.InterruptLevelBailOut))
Ω(status.ShouldIncludeProgressReport()).Should(BeTrue())
})
})
})
Describe("Interrupting when another Ginkgo process has aborted", func() {
var client parallel_support.Client
BeforeEach(func() {
_, client, _ = SetUpServerAndClient(2)
interruptHandler = interrupt_handler.NewInterruptHandler(client, syscall.SIGUSR2)
DeferCleanup(interruptHandler.Stop)
})
It("interrupts when the server is told to abort", func() {
status := interruptHandler.Status()
Consistently(status.Channel).ShouldNot(BeClosed())
client.PostAbort()
Eventually(status.Channel).Should(BeClosed())
})
It("notes the correct cause and returns an interrupt message that does not include a progress report", func() {
status := interruptHandler.Status()
Ω(status.Interrupted()).Should(BeFalse())
client.PostAbort()
Eventually(status.Channel).Should(BeClosed())
status = interruptHandler.Status()
Ω(status.Cause).Should(Equal(interrupt_handler.InterruptCauseAbortByOtherProcess))
Ω(status.Level).Should(Equal(interrupt_handler.InterruptLevelCleanupAndReport))
Ω(status.Interrupted()).Should(BeTrue())
Ω(status.Message()).Should(Equal("Interrupted by Other Ginkgo Process"))
Ω(status.ShouldIncludeProgressReport()).Should(BeFalse())
})
It("does not retrigger on subsequent aborts", func() {
status := interruptHandler.Status()
Ω(status.Interrupted()).Should(BeFalse())
client.PostAbort()
Eventually(status.Channel).Should(BeClosed())
status = interruptHandler.Status()
client.PostAbort()
Consistently(status.Channel, interrupt_handler.ABORT_POLLING_INTERVAL+100*time.Millisecond).ShouldNot(BeClosed())
status = interruptHandler.Status()
Ω(status.Cause).Should(Equal(interrupt_handler.InterruptCauseAbortByOtherProcess))
Ω(status.Level).Should(Equal(interrupt_handler.InterruptLevelCleanupAndReport))
})
It("does not trigger if the suite has already been interrupted", func() {
status := interruptHandler.Status()
Ω(status.Interrupted()).Should(BeFalse())
trigger()
Eventually(status.Channel).Should(BeClosed())
status = interruptHandler.Status()
Ω(status.Cause).Should(Equal(interrupt_handler.InterruptCauseSignal))
Ω(status.Level).Should(Equal(interrupt_handler.InterruptLevelCleanupAndReport))
status = interruptHandler.Status()
client.PostAbort()
Consistently(status.Channel, interrupt_handler.ABORT_POLLING_INTERVAL+100*time.Millisecond).ShouldNot(BeClosed())
status = interruptHandler.Status()
Ω(status.Cause).Should(Equal(interrupt_handler.InterruptCauseSignal))
Ω(status.Level).Should(Equal(interrupt_handler.InterruptLevelCleanupAndReport))
})
It("doesn't just rely on the ABORT_POLLING_INTERVAL timer to report that the interrupt has happened", func() {
client.PostAbort()
Ω(interruptHandler.Status().Cause).Should(Equal(interrupt_handler.InterruptCauseAbortByOtherProcess))
}, MustPassRepeatedly(10))
})
})
|