package internal_integration_test import ( "time" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" . "github.com/onsi/ginkgo/v2/internal/test_helpers" "github.com/onsi/ginkgo/v2/types" ) var _ = Describe("handling test failures", func() { Describe("when BeforeSuite fails", func() { BeforeEach(func() { success, _ := RunFixture("failed beforesuite", func() { BeforeSuite(rt.T("before-suite", func() { writer.Write([]byte("before-suite")) F("fail", cl) })) It("A", rt.T("A")) It("B", rt.T("B")) AfterSuite(rt.T("after-suite")) }) Ω(success).Should(BeFalse()) }) It("reports a suite failure", func() { Ω(reporter.End).Should(BeASuiteSummary(false, NSpecs(2), NSkipped(0))) }) It("reports a failure for the BeforeSuite", func() { Ω(reporter.Did.FindByLeafNodeType(types.NodeTypeBeforeSuite)).Should(HaveFailed("fail", cl, CapturedGinkgoWriterOutput("before-suite"))) Ω(reporter.Did.FindByLeafNodeType(types.NodeTypeAfterSuite)).Should(HavePassed()) }) It("does not run any of the Its", func() { Ω(rt).ShouldNot(HaveRun("A")) Ω(rt).ShouldNot(HaveRun("B")) }) It("does run the AfterSuite", func() { Ω(rt).Should(HaveTracked("before-suite", "after-suite")) }) It("emits the failures", func() { Ω(reporter.Failures[0]).Should(HaveFailed("fail", cl, FailureNodeType(types.NodeTypeBeforeSuite))) }) }) Describe("when BeforeSuite panics", func() { BeforeEach(func() { success, _ := RunFixture("panicked beforesuite", func() { BeforeSuite(rt.T("before-suite", func() { writer.Write([]byte("before-suite")) panic("boom") })) It("A", rt.T("A")) It("B", rt.T("B")) AfterSuite(rt.T("after-suite")) }) Ω(success).Should(BeFalse()) }) It("reports a suite failure", func() { Ω(reporter.End).Should(BeASuiteSummary(false, NSpecs(2), NSkipped(0))) }) It("reports a failure for the BeforeSuite", func() { Ω(reporter.Did.FindByLeafNodeType(types.NodeTypeBeforeSuite)).Should(HavePanicked("boom", CapturedGinkgoWriterOutput("before-suite"))) }) It("does not run any of the Its", func() { Ω(rt).ShouldNot(HaveRun("A")) Ω(rt).ShouldNot(HaveRun("B")) }) It("does run the AfterSuite", func() { Ω(rt).Should(HaveTracked("before-suite", "after-suite")) }) It("emits the failures", func() { Ω(reporter.Failures[0]).Should(HavePanicked("boom", FailureNodeType(types.NodeTypeBeforeSuite))) }) }) Describe("when AfterSuite fails/panics", func() { BeforeEach(func() { success, _ := RunFixture("failed aftersuite", func() { BeforeSuite(rt.T("before-suite")) Describe("top-level", func() { It("A", rt.T("A")) It("B", rt.T("B")) }) AfterSuite(rt.T("after-suite", func() { writer.Write([]byte("after-suite")) F("fail", cl) })) }) Ω(success).Should(BeFalse()) }) It("reports a suite failure", func() { Ω(reporter.End).Should(BeASuiteSummary(false, NSpecs(2), NPassed(2))) }) It("runs and reports on all the tests and reports a failure for the AfterSuite", func() { Ω(rt).Should(HaveTracked("before-suite", "A", "B", "after-suite")) Ω(reporter.Did.FindByLeafNodeType(types.NodeTypeBeforeSuite)).Should(HavePassed()) Ω(reporter.Did.Find("A")).Should(HavePassed()) Ω(reporter.Did.Find("B")).Should(HavePassed()) Ω(reporter.Did.FindByLeafNodeType(types.NodeTypeAfterSuite)).Should(HaveFailed("fail", cl, CapturedGinkgoWriterOutput("after-suite"))) }) It("emits the failures", func() { Ω(reporter.Failures[0]).Should(HaveFailed("fail", FailureNodeType(types.NodeTypeAfterSuite), TLWithOffset("after-suite"))) }) }) Describe("individual test falures", func() { Describe("when an It fails", func() { BeforeEach(func() { success, _ := RunFixture("failed it", func() { BeforeSuite(rt.T("before-suite")) Describe("top-level", func() { It("A", rt.T("A", func() { writer.Write([]byte("running A")) })) It("B", rt.T("B", func() { writer.Write([]byte("running B")) F("fail", cl) })) It("C", rt.T("C")) }) AfterEach(rt.T("after-each")) AfterSuite(rt.T("after-suite")) }) Ω(success).Should(BeFalse()) }) It("reports a suite failure", func() { Ω(reporter.End).Should(BeASuiteSummary(false, NSpecs(3), NPassed(2), NFailed(1))) }) It("runs other Its, the AfterEach, and the AfterSuite", func() { Ω(rt).Should(HaveTracked("before-suite", "A", "after-each", "B", "after-each", "C", "after-each", "after-suite")) }) It("reports the It's failure", func() { Ω(reporter.Did.Find("A")).Should(HavePassed(CapturedGinkgoWriterOutput("running A"))) Ω(reporter.Did.Find("B")).Should(HaveFailed("fail", cl, CapturedGinkgoWriterOutput("running B"), TLWithOffset("running B"))) Ω(reporter.Did.Find("C")).Should(HavePassed()) }) It("sets up the failure node location correctly", func() { report := reporter.Did.Find("B") Ω(report.Failure.FailureNodeContext).Should(Equal(types.FailureNodeIsLeafNode)) Ω(report.Failure.FailureNodeType).Should(Equal(types.NodeTypeIt)) Ω(report.Failure.FailureNodeLocation).Should(Equal(report.LeafNodeLocation)) }) It("emits the failures", func() { Ω(reporter.Failures[0]).Should(HaveFailed("fail", cl, TLWithOffset("running B"))) }) }) Describe("when an It panics", func() { BeforeEach(func() { success, _ := RunFixture("panicked it", func() { BeforeSuite(rt.T("before-suite")) Describe("top-level", func() { It("A", rt.T("A", func() { writer.Write([]byte("running A")) })) It("B", rt.T("B", func() { writer.Write([]byte("running B")) panic("boom") })) It("C", rt.T("C")) }) AfterSuite(rt.T("after-suite")) }) Ω(success).Should(BeFalse()) }) It("reports a suite failure", func() { Ω(reporter.End).Should(BeASuiteSummary(false, NSpecs(3), NPassed(2), NFailed(1))) }) It("runs other Its and the AfterSuite", func() { Ω(rt).Should(HaveTracked("before-suite", "A", "B", "C", "after-suite")) }) It("reports the It's failure", func() { Ω(reporter.Did.Find("A")).Should(HavePassed(CapturedGinkgoWriterOutput("running A"))) Ω(reporter.Did.Find("B")).Should(HavePanicked("boom", CapturedGinkgoWriterOutput("running B"), TLWithOffset("running B"))) Ω(reporter.Did.Find("C")).Should(HavePassed()) }) It("sets up the failure node location correctly", func() { report := reporter.Did.Find("B") Ω(report.Failure.FailureNodeContext).Should(Equal(types.FailureNodeIsLeafNode)) Ω(report.Failure.FailureNodeType).Should(Equal(types.NodeTypeIt)) Ω(report.Failure.FailureNodeLocation).Should(Equal(report.LeafNodeLocation)) }) It("emits the failures", func() { Ω(reporter.Failures[0]).Should(HavePanicked("boom", TLWithOffset("running B"))) }) }) Describe("when a BeforeEach fails/panics", func() { BeforeEach(func() { success, _ := RunFixture("failed before each", func() { BeforeEach(rt.T("bef-1")) JustBeforeEach(rt.T("jus-bef-1")) Describe("top-level", func() { BeforeEach(rt.T("bef-2", func() { writer.Write([]byte("bef-2 is running")) F("fail", cl) })) JustBeforeEach(rt.T("jus-bef-2")) Describe("nested", func() { BeforeEach(rt.T("bef-3")) JustBeforeEach(rt.T("jus-bef-3")) It("the test", rt.T("it")) JustAfterEach(rt.T("jus-aft-3")) AfterEach(rt.T("aft-3")) }) JustAfterEach(rt.T("jus-aft-2")) AfterEach(rt.T("aft-2", func() { writer.Write([]byte("aft-2 is running")) })) }) JustAfterEach(rt.T("jus-aft-1")) AfterEach(rt.T("aft-1")) }) Ω(success).Should(BeFalse()) }) It("reports a suite failure and a spec failure", func() { Ω(reporter.End).Should(BeASuiteSummary(false, NSpecs(1), NPassed(0), NFailed(1))) specReport := reporter.Did.Find("the test") Ω(specReport).Should(HaveFailed("fail", cl, CapturedGinkgoWriterOutput("bef-2 is runningaft-2 is running"))) }) It("sets up the failure node location correctly", func() { report := reporter.Did.Find("the test") Ω(report.Failure.FailureNodeContext).Should(Equal(types.FailureNodeInContainer)) Ω(report.Failure.FailureNodeType).Should(Equal(types.NodeTypeBeforeEach)) Ω(report.Failure.FailureNodeContainerIndex).Should(Equal(0)) }) It("emits the failures", func() { Ω(reporter.Failures[0]).Should(HaveFailed("fail", cl)) }) It("runs the JustAfterEaches and AfterEaches at the same or lesser nesting level", func() { Ω(rt).Should(HaveTracked("bef-1", "bef-2", "jus-aft-2", "jus-aft-1", "aft-2", "aft-1")) }) }) Describe("when a top-level BeforeEach fails/panics", func() { BeforeEach(func() { success, _ := RunFixture("failed before each", func() { BeforeEach(rt.T("bef-1", func() { F("fail", cl) })) It("the test", rt.T("it")) }) Ω(success).Should(BeFalse()) }) It("sets up the failure node location correctly", func() { report := reporter.Did.Find("the test") Ω(report.Failure.FailureNodeContext).Should(Equal(types.FailureNodeAtTopLevel)) Ω(report.Failure.FailureNodeType).Should(Equal(types.NodeTypeBeforeEach)) Ω(reporter.Failures[0]).Should(HaveFailed("fail", cl)) }) }) Describe("when an AfterEach fails/panics", func() { BeforeEach(func() { success, _ := RunFixture("failed after each", func() { BeforeEach(rt.T("bef-1")) JustBeforeEach(rt.T("jus-bef-1")) Describe("top-level", func() { BeforeEach(rt.T("bef-2")) Describe("nested", func() { BeforeEach(rt.T("bef-3")) It("the test", rt.T("it")) JustAfterEach(rt.T("jus-aft-3")) AfterEach(rt.T("aft-3", func() { F("fail", types.NewCodeLocation(0)) })) }) JustAfterEach(rt.T("jus-aft-2")) AfterEach(rt.T("aft-2", func() { writer.Write([]byte("aft-2 is running")) })) }) JustAfterEach(rt.T("jus-aft-1")) AfterEach(rt.T("aft-1")) }) Ω(success).Should(BeFalse()) }) It("reports a suite failure and a spec failure", func() { Ω(reporter.End).Should(BeASuiteSummary(false, NSpecs(1), NPassed(0), NFailed(1))) specReport := reporter.Did.Find("the test") Ω(specReport).Should(HaveFailed("fail", CapturedGinkgoWriterOutput("aft-2 is running"))) Ω(reporter.Failures[0]).Should(HaveFailed("fail")) }) It("sets up the failure node location correctly", func() { report := reporter.Did.Find("the test") Ω(report.Failure.FailureNodeContext).Should(Equal(types.FailureNodeInContainer)) Ω(report.Failure.FailureNodeType).Should(Equal(types.NodeTypeAfterEach)) location := report.Failure.Location location.LineNumber = location.LineNumber - 1 Ω(report.Failure.FailureNodeLocation).Should(Equal(location)) Ω(report.Failure.FailureNodeContainerIndex).Should(Equal(1)) }) It("runs the subsequent after eaches", func() { Ω(rt).Should(HaveTracked("bef-1", "bef-2", "bef-3", "jus-bef-1", "it", "jus-aft-3", "jus-aft-2", "jus-aft-1", "aft-3", "aft-2", "aft-1")) }) }) Describe("when multiple nodes within a given test run and fail", func() { var clA, clB types.CodeLocation BeforeEach(func() { clA = types.CodeLocation{FileName: "A"} clB = types.CodeLocation{FileName: "B"} success, _ := RunFixture("failed after each", func() { BeforeEach(rt.T("bef-1", func() { writer.Write([]byte("run A")) DeferCleanup(rt.TSC("dc-1", func(ctx SpecContext) { writer.Write([]byte("run DC")) <-ctx.Done() F("fail-DC") }), NodeTimeout(time.Millisecond*50)) F("fail-A", clA) })) It("the test", rt.T("it")) AfterEach(rt.T("aft-1", func() { writer.Write([]byte("run B")) F("fail-B", clB) })) }) Ω(success).Should(BeFalse()) }) It("reports a suite failure and a spec failure and tracks the first failure as its primary failure, but also tracks the additional failures", func() { Ω(reporter.End).Should(BeASuiteSummary(false, NSpecs(1), NPassed(0), NFailed(1))) specReport := reporter.Did.Find("the test") Ω(specReport).Should(HaveFailed("fail-A", clA, CapturedGinkgoWriterOutput("run Arun Brun DC"))) Ω(specReport.Failure.FailureNodeType).Should(Equal(types.NodeTypeBeforeEach)) Ω(rt).Should(HaveTracked("bef-1", "aft-1", "dc-1")) Ω(specReport.AdditionalFailures).Should(HaveLen(2)) Ω(specReport.AdditionalFailures[0].State).Should(Equal(types.SpecStateFailed)) Ω(specReport.AdditionalFailures[0].Failure.Message).Should(Equal("fail-B")) Ω(specReport.AdditionalFailures[0].Failure.Location).Should(Equal(clB)) Ω(specReport.AdditionalFailures[0].Failure.FailureNodeType).Should(Equal(types.NodeTypeAfterEach)) Ω(specReport.AdditionalFailures[1].State).Should(Equal(types.SpecStateTimedout)) Ω(specReport.AdditionalFailures[1]).Should(HaveTimedOut("A node timeout occurred")) Ω(specReport.AdditionalFailures[1].Failure.AdditionalFailure).Should(HaveFailed("A node timeout occurred and then the following failure was recorded in the timedout node before it exited:\nfail-DC")) Ω(specReport.AdditionalFailures[1].Failure.FailureNodeType).Should(Equal(types.NodeTypeCleanupAfterEach)) }) It("also emits all the failures", func() { Ω(reporter.Failures).Should(HaveLen(4)) Ω(reporter.Failures[0]).Should(HaveFailed("fail-A", clA)) Ω(reporter.Failures[1]).Should(HaveFailed("fail-B", clB)) Ω(reporter.Failures[2]).Should(HaveTimedOut("A node timeout occurred")) Ω(reporter.Failures[3]).Should(HaveFailed("A node timeout occurred and then the following failure was recorded in the timedout node before it exited:\nfail-DC")) }) }) }) Describe("when there are multiple tests that fail", func() { BeforeEach(func() { success, _ := RunFixture("failed after each", func() { It("A", func() { F() }) It("B", func() { F() }) It("C", func() {}) It("D", func() { F() }) It("E", func() {}) It("F", func() { panic("boom") }) }) Ω(success).Should(BeFalse()) }) It("reports the correct number of failures", func() { Ω(reporter.End).Should(BeASuiteSummary(false, NSpecs(6), NPassed(2), NFailed(4))) Ω(reporter.Did.WithState(types.SpecStatePassed).Names()).Should(ConsistOf("C", "E")) Ω(reporter.Did.WithState(types.SpecStateFailed).Names()).Should(ConsistOf("A", "B", "D")) Ω(reporter.Did.WithState(types.SpecStatePanicked).Names()).Should(ConsistOf("F")) }) It("emits all the failures", func() { Ω(reporter.Failures).Should(HaveLen(4)) }) }) })