예제 #1
0
func (runnable *runnableNode) run() (outcome runOutcome, failure failureData) {
	done := make(chan interface{}, 1)

	defer func() {
		if e := recover(); e != nil {
			outcome = runOutcomePanicked
			failure = failureData{
				message:        "Test Panicked",
				codeLocation:   types.GenerateCodeLocation(2),
				forwardedPanic: e,
			}
		}
	}()

	if runnable.isAsync {
		go runnable.asyncFunc(done)
	} else {
		runnable.syncFunc()
		done <- true
	}

	select {
	case <-done:
		outcome = runOutcomeCompleted
	case <-time.After(runnable.timeoutThreshold):
		outcome = runOutcomeTimedOut
		failure = failureData{
			message:      "Timed out",
			codeLocation: runnable.codeLocation,
		}
	}

	return
}
예제 #2
0
파일: suite.go 프로젝트: paybyphone/ginkgo
func (suite *suite) fail(message string, callerSkip int) {
	if suite.exampleCollection != nil {
		suite.exampleCollection.fail(failureData{
			message:      message,
			codeLocation: types.GenerateCodeLocation(callerSkip + 2),
		})
	}
}
예제 #3
0
func init() {
	Describe("MeasureNode", func() {
		var measure *measureNode
		var i int
		var codeLocation types.CodeLocation

		BeforeEach(func() {
			i = 0
			codeLocation = types.GenerateCodeLocation(0)
			measure = newMeasureNode("foo", func(b Benchmarker) {
				b.RecordValue("bar", float64(i))
				i += 1
			}, flagTypeFocused, codeLocation, 10)
		})

		It("should report on itself accurately", func() {
			Ω(measure.getText()).Should(Equal("foo"))
			Ω(measure.getFlag()).Should(Equal(flagTypeFocused))
			Ω(measure.getCodeLocation()).Should(Equal(codeLocation))
			Ω(measure.nodeType()).Should(Equal(nodeTypeMeasure))
			Ω(measure.samples).Should(Equal(10))
		})

		Context("when run", func() {
			It("should provide the body function with a benchmarker and be able to aggregate reports", func() {
				measure.run()
				measure.run()
				measure.run()
				measure.run()

				report := measure.measurementsReport()
				Ω(report).Should(HaveLen(1))
				Ω(report["bar"].Name).Should(Equal("bar"))
				Ω(report["bar"].Results).Should(Equal([]float64{0, 1, 2, 3}))
			})
		})
	})
}
예제 #4
0
func init() {
	Describe("Container Node", func() {
		var (
			codeLocation types.CodeLocation
			container    *containerNode
		)

		BeforeEach(func() {
			codeLocation = types.GenerateCodeLocation(0)
			container = newContainerNode("description text", flagTypeFocused, codeLocation)
		})

		Describe("creating a container node", func() {
			It("stores off the passed in properties", func() {
				Ω(container.text).Should(Equal("description text"))
				Ω(container.flag).Should(Equal(flagTypeFocused))
				Ω(container.codeLocation).Should(Equal(codeLocation))
			})
		})

		Describe("appending", func() {
			Describe("it nodes", func() {
				It("can append container nodes and it nodes", func() {
					itA := newItNode("itA", func() {}, flagTypeNone, types.GenerateCodeLocation(0), 0)
					itB := newItNode("itB", func() {}, flagTypeNone, types.GenerateCodeLocation(0), 0)
					subContainer := newContainerNode("subcontainer", flagTypeNone, types.GenerateCodeLocation(0))
					container.pushSubjectNode(itA)
					container.pushContainerNode(subContainer)
					container.pushSubjectNode(itB)
					Ω(container.subjectAndContainerNodes).Should(Equal([]node{
						itA,
						subContainer,
						itB,
					}))
				})
			})

			Describe("other runnable nodes", func() {
				var (
					runnableA *runnableNode
					runnableB *runnableNode
				)

				BeforeEach(func() {
					runnableA = newRunnableNode(func() {}, types.GenerateCodeLocation(0), 0)
					runnableB = newRunnableNode(func() {}, types.GenerateCodeLocation(0), 0)
				})

				It("can append multiple beforeEach nodes", func() {
					container.pushBeforeEachNode(runnableA)
					container.pushBeforeEachNode(runnableB)
					Ω(container.beforeEachNodes).Should(Equal([]*runnableNode{
						runnableA,
						runnableB,
					}))
				})

				It("can append multiple justBeforeEach nodes", func() {
					container.pushJustBeforeEachNode(runnableA)
					container.pushJustBeforeEachNode(runnableB)
					Ω(container.justBeforeEachNodes).Should(Equal([]*runnableNode{
						runnableA,
						runnableB,
					}))
				})

				It("can append multiple afterEach nodes", func() {
					container.pushAfterEachNode(runnableA)
					container.pushAfterEachNode(runnableB)
					Ω(container.afterEachNodes).Should(Equal([]*runnableNode{
						runnableA,
						runnableB,
					}))
				})
			})
		})

		Describe("generating examples", func() {
			var (
				itA          *itNode
				itB          *itNode
				subContainer *containerNode
				subItA       *itNode
				subItB       *itNode
			)

			BeforeEach(func() {
				itA = newItNode("itA", func() {}, flagTypeNone, types.GenerateCodeLocation(0), 0)
				itB = newItNode("itB", func() {}, flagTypeNone, types.GenerateCodeLocation(0), 0)
				subContainer = newContainerNode("subcontainer", flagTypeNone, types.GenerateCodeLocation(0))
				subItA = newItNode("subItA", func() {}, flagTypeNone, types.GenerateCodeLocation(0), 0)
				subItB = newItNode("subItB", func() {}, flagTypeNone, types.GenerateCodeLocation(0), 0)

				container.pushSubjectNode(itA)
				container.pushContainerNode(subContainer)
				container.pushSubjectNode(itB)

				subContainer.pushSubjectNode(subItA)
				subContainer.pushSubjectNode(subItB)
			})

			It("generates an example for each It in the hierarchy", func() {
				examples := container.generateExamples()
				Ω(examples).Should(HaveLen(4))

				Ω(examples[0].subject).Should(Equal(itA))
				Ω(examples[0].containers).Should(Equal([]*containerNode{container}))

				Ω(examples[1].subject).Should(Equal(subItA))
				Ω(examples[1].containers).Should(Equal([]*containerNode{container, subContainer}))

				Ω(examples[2].subject).Should(Equal(subItB))
				Ω(examples[2].containers).Should(Equal([]*containerNode{container, subContainer}))

				Ω(examples[3].subject).Should(Equal(itB))
				Ω(examples[3].containers).Should(Equal([]*containerNode{container}))
			})

			It("ignores containers in the hierarchy that are empty", func() {
				emptyContainer := newContainerNode("empty container", flagTypeNone, types.GenerateCodeLocation(0))
				emptyContainer.pushBeforeEachNode(newRunnableNode(func() {}, types.GenerateCodeLocation(0), 0))

				container.pushContainerNode(emptyContainer)
				examples := container.generateExamples()
				Ω(examples).Should(HaveLen(4))
			})
		})

		Describe("shuffling the container", func() {
			texts := func(container *containerNode) []string {
				texts := make([]string, 0)
				for _, node := range container.subjectAndContainerNodes {
					texts = append(texts, node.getText())
				}
				return texts
			}

			BeforeEach(func() {
				itA := newItNode("Banana", func() {}, flagTypeNone, types.GenerateCodeLocation(0), 0)
				itB := newItNode("Apple", func() {}, flagTypeNone, types.GenerateCodeLocation(0), 0)
				itC := newItNode("Orange", func() {}, flagTypeNone, types.GenerateCodeLocation(0), 0)
				containerA := newContainerNode("Cucumber", flagTypeNone, types.GenerateCodeLocation(0))
				containerB := newContainerNode("Airplane", flagTypeNone, types.GenerateCodeLocation(0))

				container.pushSubjectNode(itA)
				container.pushContainerNode(containerA)
				container.pushSubjectNode(itB)
				container.pushContainerNode(containerB)
				container.pushSubjectNode(itC)
			})

			It("should be sortable", func() {
				sort.Sort(container)
				Ω(texts(container)).Should(Equal([]string{"Airplane", "Apple", "Banana", "Cucumber", "Orange"}))
			})

			It("shuffles all the examples after sorting them", func() {
				container.shuffle(rand.New(rand.NewSource(17)))
				expectedOrder := shuffleStrings([]string{"Airplane", "Apple", "Banana", "Cucumber", "Orange"}, 17)
				Ω(texts(container)).Should(Equal(expectedOrder), "The permutation should be the same across test runs")
			})
		})
	})
}
예제 #5
0
파일: ginkgo.go 프로젝트: paybyphone/ginkgo
//AfterEach blocks are run after It blocks.   When multiple AfterEach blocks are defined in nested
//Describe and Context blocks the innermost AfterEach blocks are run first.
//
//Like It blocks, BeforeEach blocks can be made asynchronous by providing a body function that accepts
//a Done channel
func AfterEach(body interface{}, timeout ...float64) bool {
	globalSuite.pushAfterEachNode(body, types.GenerateCodeLocation(1), parseTimeout(timeout...))
	return true
}
예제 #6
0
파일: ginkgo.go 프로젝트: paybyphone/ginkgo
//You can mark Maeasurements as pending using XMeasure
func XMeasure(text string, _ ...interface{}) bool {
	globalSuite.pushMeasureNode(text, func(b Benchmarker) {}, flagTypePending, types.GenerateCodeLocation(1), 0)
	return true
}
예제 #7
0
파일: ginkgo.go 프로젝트: paybyphone/ginkgo
//You can focus individual Measures using FMeasure
func FMeasure(text string, body func(Benchmarker), samples int) bool {
	globalSuite.pushMeasureNode(text, body, flagTypeFocused, types.GenerateCodeLocation(1), samples)
	return true
}
예제 #8
0
파일: ginkgo.go 프로젝트: paybyphone/ginkgo
//You can mark Its as pending using XIt
func XIt(text string, _ ...interface{}) bool {
	globalSuite.pushItNode(text, func() {}, flagTypePending, types.GenerateCodeLocation(1), 0)
	return true
}
예제 #9
0
파일: ginkgo.go 프로젝트: paybyphone/ginkgo
//You can focus individual Its using FIt
func FIt(text string, body interface{}, timeout ...float64) bool {
	globalSuite.pushItNode(text, body, flagTypeFocused, types.GenerateCodeLocation(1), parseTimeout(timeout...))
	return true
}
예제 #10
0
파일: ginkgo.go 프로젝트: paybyphone/ginkgo
//You can mark the tests within a describe block as pending using XContext
func XContext(text string, body func()) bool {
	globalSuite.pushContainerNode(text, body, flagTypePending, types.GenerateCodeLocation(1))
	return true
}
예제 #11
0
파일: ginkgo.go 프로젝트: paybyphone/ginkgo
//You can focus the tests within a describe block using FDescribe
func FDescribe(text string, body func()) bool {
	globalSuite.pushContainerNode(text, body, flagTypeFocused, types.GenerateCodeLocation(1))
	return true
}
예제 #12
0
		{types.ExampleStateFailed, "Failure"},
		{types.ExampleStateTimedOut, "Timeout"},
		{types.ExampleStatePanicked, "Panic"},
	}

	for _, exampleStateCase := range exampleStateCases {
		exampleStateCase := exampleStateCase
		Describe("a failing test", func() {
			var example *types.ExampleSummary
			BeforeEach(func() {
				example = &types.ExampleSummary{
					ComponentTexts: []string{"[Top Level]", "A", "B", "C"},
					State:          exampleStateCase.state,
					RunTime:        5 * time.Second,
					Failure: types.ExampleFailure{
						ComponentCodeLocation: types.GenerateCodeLocation(0),
						Message:               "I failed",
					},
				}
				reporter.ExampleWillRun(example)
				reporter.ExampleDidComplete(example)

				reporter.SpecSuiteDidEnd(&types.SuiteSummary{
					NumberOfExamplesThatWillBeRun: 1,
					NumberOfFailedExamples:        1,
					RunTime:                       10 * time.Second,
				})
			})

			It("should record test as failing", func() {
				output := readOutputFile()
예제 #13
0
func init() {
	Describe("Example Collection", func() {
		var (
			fakeT *fakeTestingT
			fakeR *reporters.FakeReporter

			examplesThatWereRun []string

			collection *exampleCollection
			writer     *fakeGinkgoWriter
		)

		exampleWithItFunc := func(itText string, flag flagType, fail bool) *example {
			return newExample(newItNode(itText, func() {
				examplesThatWereRun = append(examplesThatWereRun, itText)
				time.Sleep(time.Duration(0.001 * float64(time.Second)))
				if fail {
					collection.fail(failureData{
						message: itText + " Failed",
					})
				}
			}, flag, types.GenerateCodeLocation(0), 0))
		}

		BeforeEach(func() {
			writer = &fakeGinkgoWriter{}
			fakeT = &fakeTestingT{}
			fakeR = reporters.NewFakeReporter()
			examplesThatWereRun = make([]string, 0)
		})

		Describe("enumerating and assigning example indices", func() {
			var examples []*example
			BeforeEach(func() {
				examples = []*example{
					exampleWithItFunc("C", flagTypeNone, false),
					exampleWithItFunc("A", flagTypeNone, false),
					exampleWithItFunc("B", flagTypeNone, false),
				}
				collection = newExampleCollection(fakeT, "collection description", examples, []Reporter{fakeR}, writer, config.GinkgoConfigType{})
			})

			It("should enumerate and assign example indices", func() {
				Ω(examples[0].summary("suite-id").ExampleIndex).Should(Equal(0))
				Ω(examples[1].summary("suite-id").ExampleIndex).Should(Equal(1))
				Ω(examples[2].summary("suite-id").ExampleIndex).Should(Equal(2))
			})
		})

		Describe("shuffling the collection", func() {
			BeforeEach(func() {
				collection = newExampleCollection(fakeT, "collection description", []*example{
					exampleWithItFunc("C", flagTypeNone, false),
					exampleWithItFunc("A", flagTypeNone, false),
					exampleWithItFunc("B", flagTypeNone, false),
				}, []Reporter{fakeR}, writer, config.GinkgoConfigType{})
			})

			It("should be sortable", func() {
				sort.Sort(collection)
				collection.run()
				Ω(examplesThatWereRun).Should(Equal([]string{"A", "B", "C"}))
			})

			It("shuffles all the examples after sorting them", func() {
				collection.shuffle(rand.New(rand.NewSource(17)))
				collection.run()
				Ω(examplesThatWereRun).Should(Equal(shuffleStrings([]string{"A", "B", "C"}, 17)), "The permutation should be the same across test runs")
			})
		})

		Describe("reporting to multiple reporter", func() {
			var otherFakeR *reporters.FakeReporter
			BeforeEach(func() {
				otherFakeR = reporters.NewFakeReporter()

				collection = newExampleCollection(fakeT, "collection description", []*example{
					exampleWithItFunc("C", flagTypeNone, false),
					exampleWithItFunc("A", flagTypeNone, false),
					exampleWithItFunc("B", flagTypeNone, false),
				}, []Reporter{fakeR, otherFakeR}, writer, config.GinkgoConfigType{})
				collection.run()
			})

			It("reports to both reporters", func() {
				Ω(otherFakeR.BeginSummary).Should(Equal(fakeR.BeginSummary))
				Ω(otherFakeR.EndSummary).Should(Equal(fakeR.EndSummary))
				Ω(otherFakeR.ExampleSummaries).Should(Equal(fakeR.ExampleSummaries))
			})
		})

		Describe("logging GinkgoWriter output", func() {
			Context("when a test fails", func() {
				BeforeEach(func() {
					collection = newExampleCollection(fakeT, "collection description", []*example{
						exampleWithItFunc("C", flagTypeNone, true),
					}, []Reporter{fakeR}, writer, config.GinkgoConfigType{})
					collection.run()
				})

				It("should truncate and write to stdout", func() {
					Ω(writer.didTruncate).Should(BeTrue())
					Ω(writer.didDump).Should(BeTrue())
				})
			})

			Context("when a test passes", func() {
				BeforeEach(func() {
					collection = newExampleCollection(fakeT, "collection description", []*example{
						exampleWithItFunc("C", flagTypeNone, false),
					}, []Reporter{fakeR}, writer, config.GinkgoConfigType{})
					collection.run()
				})

				It("should truncate but not write to stdout", func() {
					Ω(writer.didTruncate).Should(BeTrue())
					Ω(writer.didDump).Should(BeFalse())
				})
			})
		})

		Describe("running an example collection", func() {
			var (
				example1  *example
				example2  *example
				example3  *example
				conf      config.GinkgoConfigType
				runResult bool
			)

			BeforeEach(func() {
				conf = config.GinkgoConfigType{FocusString: "", ParallelTotal: 1, ParallelNode: 1}

				example1 = exampleWithItFunc("it 1", flagTypeNone, false)
				example2 = exampleWithItFunc("it 2", flagTypeNone, false)
				example3 = exampleWithItFunc("it 3", flagTypeNone, false)
			})

			JustBeforeEach(func() {
				collection = newExampleCollection(fakeT, "collection description", []*example{example1, example2, example3}, []Reporter{fakeR}, writer, conf)
				runResult = collection.run()
			})

			Context("when all the examples pass", func() {
				It("should return true", func() {
					Ω(runResult).Should(BeTrue())
				})

				It("runs all the tests", func() {
					Ω(examplesThatWereRun).Should(Equal([]string{"it 1", "it 2", "it 3"}))
				})

				It("marks the suite as passed", func() {
					Ω(fakeT.didFail).Should(BeFalse())
				})

				It("publishes the correct starting suite summary", func() {
					summary := fakeR.BeginSummary
					Ω(summary.SuiteDescription).Should(Equal("collection description"))
					Ω(summary.SuiteSucceeded).Should(BeTrue())
					Ω(summary.NumberOfExamplesBeforeParallelization).Should(Equal(3))
					Ω(summary.NumberOfTotalExamples).Should(Equal(3))
					Ω(summary.NumberOfExamplesThatWillBeRun).Should(Equal(3))
					Ω(summary.NumberOfPendingExamples).Should(Equal(0))
					Ω(summary.NumberOfSkippedExamples).Should(Equal(0))
					Ω(summary.NumberOfPassedExamples).Should(Equal(0))
					Ω(summary.NumberOfFailedExamples).Should(Equal(0))
					Ω(summary.RunTime).Should(Equal(time.Duration(0)))
				})

				It("publishes the correct example summaries", func() {
					Ω(fakeR.ExampleWillRunSummaries).Should(HaveLen(3))
					Ω(fakeR.ExampleSummaries).Should(HaveLen(3))
					Ω(fakeR.ExampleSummaries[0]).Should(Equal(example1.summary(fakeR.BeginSummary.SuiteID)))
					Ω(fakeR.ExampleSummaries[1]).Should(Equal(example2.summary(fakeR.BeginSummary.SuiteID)))
					Ω(fakeR.ExampleSummaries[2]).Should(Equal(example3.summary(fakeR.BeginSummary.SuiteID)))
				})

				It("publishes the correct ending suite summary", func() {
					summary := fakeR.EndSummary
					Ω(summary.SuiteDescription).Should(Equal("collection description"))
					Ω(summary.SuiteSucceeded).Should(BeTrue())
					Ω(summary.NumberOfExamplesBeforeParallelization).Should(Equal(3))
					Ω(summary.NumberOfTotalExamples).Should(Equal(3))
					Ω(summary.NumberOfExamplesThatWillBeRun).Should(Equal(3))
					Ω(summary.NumberOfPendingExamples).Should(Equal(0))
					Ω(summary.NumberOfSkippedExamples).Should(Equal(0))
					Ω(summary.NumberOfPassedExamples).Should(Equal(3))
					Ω(summary.NumberOfFailedExamples).Should(Equal(0))
					Ω(summary.RunTime.Seconds()).Should(BeNumerically("~", 3*0.001, 0.01))
				})

				It("should publish a consistent suite ID across all summaries", func() {
					suiteId := fakeR.BeginSummary.SuiteID
					Ω(suiteId).ShouldNot(BeEmpty())
					Ω(fakeR.EndSummary.SuiteID).Should(Equal(suiteId))
					for _, exampleSummary := range fakeR.ExampleSummaries {
						Ω(exampleSummary.SuiteID).Should(Equal(suiteId))
					}
				})
			})

			Context("when examples fail", func() {
				BeforeEach(func() {
					example2 = exampleWithItFunc("failing it 2", flagTypeNone, true)
					example3 = exampleWithItFunc("failing it 3", flagTypeNone, true)
				})

				It("should return false", func() {
					Ω(runResult).Should(BeFalse())
				})

				It("runs all the tests", func() {
					Ω(examplesThatWereRun).Should(Equal([]string{"it 1", "failing it 2", "failing it 3"}))
				})

				It("marks the suite as failed", func() {
					Ω(fakeT.didFail).Should(BeTrue())
				})

				It("publishes the correct starting suite summary", func() {
					summary := fakeR.BeginSummary
					Ω(summary.SuiteDescription).Should(Equal("collection description"))
					Ω(summary.SuiteSucceeded).Should(BeTrue())
					Ω(summary.NumberOfExamplesBeforeParallelization).Should(Equal(3))
					Ω(summary.NumberOfTotalExamples).Should(Equal(3))
					Ω(summary.NumberOfExamplesThatWillBeRun).Should(Equal(3))
					Ω(summary.NumberOfPendingExamples).Should(Equal(0))
					Ω(summary.NumberOfSkippedExamples).Should(Equal(0))
					Ω(summary.NumberOfPassedExamples).Should(Equal(0))
					Ω(summary.NumberOfFailedExamples).Should(Equal(0))
					Ω(summary.RunTime).Should(Equal(time.Duration(0)))
				})

				It("publishes the correct example summaries", func() {
					Ω(fakeR.ExampleSummaries).Should(HaveLen(3))
					Ω(fakeR.ExampleSummaries[0]).Should(Equal(example1.summary(fakeR.BeginSummary.SuiteID)))
					Ω(fakeR.ExampleSummaries[1]).Should(Equal(example2.summary(fakeR.BeginSummary.SuiteID)))
					Ω(fakeR.ExampleSummaries[2]).Should(Equal(example3.summary(fakeR.BeginSummary.SuiteID)))
				})

				It("publishes the correct ending suite summary", func() {
					summary := fakeR.EndSummary
					Ω(summary.SuiteDescription).Should(Equal("collection description"))
					Ω(summary.SuiteSucceeded).Should(BeFalse())
					Ω(summary.NumberOfExamplesBeforeParallelization).Should(Equal(3))
					Ω(summary.NumberOfTotalExamples).Should(Equal(3))
					Ω(summary.NumberOfExamplesThatWillBeRun).Should(Equal(3))
					Ω(summary.NumberOfPendingExamples).Should(Equal(0))
					Ω(summary.NumberOfSkippedExamples).Should(Equal(0))
					Ω(summary.NumberOfPassedExamples).Should(Equal(1))
					Ω(summary.NumberOfFailedExamples).Should(Equal(2))
					Ω(summary.RunTime.Seconds()).Should(BeNumerically("~", 3*0.001, 0.01))
				})
			})

			Context("when examples are pending", func() {
				BeforeEach(func() {
					example1 = exampleWithItFunc("pending it 1", flagTypePending, false)
				})

				It("skips the pending examples", func() {
					Ω(examplesThatWereRun).Should(Equal([]string{"it 2", "it 3"}))
				})

				It("marks the suite as passed", func() {
					Ω(fakeT.didFail).Should(BeFalse())
				})

				It("publishes the correct starting suite summary", func() {
					summary := fakeR.BeginSummary
					Ω(summary.SuiteDescription).Should(Equal("collection description"))
					Ω(summary.SuiteSucceeded).Should(BeTrue())
					Ω(summary.NumberOfExamplesBeforeParallelization).Should(Equal(3))
					Ω(summary.NumberOfTotalExamples).Should(Equal(3))
					Ω(summary.NumberOfExamplesThatWillBeRun).Should(Equal(2))
					Ω(summary.NumberOfPendingExamples).Should(Equal(1))
					Ω(summary.NumberOfSkippedExamples).Should(Equal(0))
					Ω(summary.NumberOfPassedExamples).Should(Equal(0))
					Ω(summary.NumberOfFailedExamples).Should(Equal(0))
					Ω(summary.RunTime).Should(Equal(time.Duration(0)))
				})

				It("publishes the correct example summaries", func() {
					Ω(fakeR.ExampleSummaries).Should(HaveLen(3))
					Ω(fakeR.ExampleSummaries[0]).Should(Equal(example1.summary(fakeR.BeginSummary.SuiteID)))
					Ω(fakeR.ExampleSummaries[1]).Should(Equal(example2.summary(fakeR.BeginSummary.SuiteID)))
					Ω(fakeR.ExampleSummaries[2]).Should(Equal(example3.summary(fakeR.BeginSummary.SuiteID)))
				})

				It("publishes the correct ending suite summary", func() {
					summary := fakeR.EndSummary
					Ω(summary.SuiteDescription).Should(Equal("collection description"))
					Ω(summary.SuiteSucceeded).Should(BeTrue())
					Ω(summary.NumberOfExamplesBeforeParallelization).Should(Equal(3))
					Ω(summary.NumberOfTotalExamples).Should(Equal(3))
					Ω(summary.NumberOfExamplesThatWillBeRun).Should(Equal(2))
					Ω(summary.NumberOfPendingExamples).Should(Equal(1))
					Ω(summary.NumberOfSkippedExamples).Should(Equal(0))
					Ω(summary.NumberOfPassedExamples).Should(Equal(2))
					Ω(summary.NumberOfFailedExamples).Should(Equal(0))
					Ω(summary.RunTime.Seconds()).Should(BeNumerically("~", 3*0.001, 0.01))
				})

				Context("and --failOnPending is set", func() {
					BeforeEach(func() {
						conf.FailOnPending = true
					})

					It("should mark the suite as failed", func() {
						Ω(fakeT.didFail).Should(BeTrue())
						summary := fakeR.EndSummary
						Ω(summary.SuiteSucceeded).Should(BeFalse())
					})
				})
			})

			Context("when examples are focused", func() {
				BeforeEach(func() {
					example1 = exampleWithItFunc("focused it 1", flagTypeFocused, false)
					example3 = exampleWithItFunc("focused it 3", flagTypeFocused, false)
				})

				It("skips the non-focused examples", func() {
					Ω(examplesThatWereRun).Should(Equal([]string{"focused it 1", "focused it 3"}))
				})

				It("marks the suite as passed", func() {
					Ω(fakeT.didFail).Should(BeFalse())
				})

				It("publishes the correct starting suite summary", func() {
					summary := fakeR.BeginSummary
					Ω(summary.SuiteDescription).Should(Equal("collection description"))
					Ω(summary.SuiteSucceeded).Should(BeTrue())
					Ω(summary.NumberOfExamplesBeforeParallelization).Should(Equal(3))
					Ω(summary.NumberOfTotalExamples).Should(Equal(3))
					Ω(summary.NumberOfExamplesThatWillBeRun).Should(Equal(2))
					Ω(summary.NumberOfPendingExamples).Should(Equal(0))
					Ω(summary.NumberOfSkippedExamples).Should(Equal(1))
					Ω(summary.NumberOfPassedExamples).Should(Equal(0))
					Ω(summary.NumberOfFailedExamples).Should(Equal(0))
					Ω(summary.RunTime).Should(Equal(time.Duration(0)))
				})

				It("publishes the correct example summaries", func() {
					Ω(fakeR.ExampleSummaries).Should(HaveLen(3))
					Ω(fakeR.ExampleSummaries[0]).Should(Equal(example1.summary(fakeR.BeginSummary.SuiteID)))
					Ω(fakeR.ExampleSummaries[1]).Should(Equal(example2.summary(fakeR.BeginSummary.SuiteID)))
					Ω(fakeR.ExampleSummaries[2]).Should(Equal(example3.summary(fakeR.BeginSummary.SuiteID)))
				})

				It("publishes the correct ending suite summary", func() {
					summary := fakeR.EndSummary
					Ω(summary.SuiteDescription).Should(Equal("collection description"))
					Ω(summary.SuiteSucceeded).Should(BeTrue())
					Ω(summary.NumberOfExamplesBeforeParallelization).Should(Equal(3))
					Ω(summary.NumberOfTotalExamples).Should(Equal(3))
					Ω(summary.NumberOfExamplesThatWillBeRun).Should(Equal(2))
					Ω(summary.NumberOfPendingExamples).Should(Equal(0))
					Ω(summary.NumberOfSkippedExamples).Should(Equal(1))
					Ω(summary.NumberOfPassedExamples).Should(Equal(2))
					Ω(summary.NumberOfFailedExamples).Should(Equal(0))
					Ω(summary.RunTime.Seconds()).Should(BeNumerically("~", 3*0.001, 0.01))
				})
			})

			Context("when a regexp focusString is provided", func() {
				BeforeEach(func() {
					conf.FocusString = `collection description.*pickles\d$`
					example1 = exampleWithItFunc("focused it 1", flagTypeFocused, false)
					example2 = exampleWithItFunc("another it pickles2", flagTypeNone, false)
					example3 = exampleWithItFunc("focused it pickles3", flagTypeFocused, false)
				})

				It("ignores the programmatic focus and applies the regexp focusString", func() {
					Ω(examplesThatWereRun).Should(Equal([]string{"another it pickles2", "focused it pickles3"}))
				})

				It("publishes the correct starting suite summary", func() {
					summary := fakeR.BeginSummary
					Ω(summary.SuiteDescription).Should(Equal("collection description"))
					Ω(summary.SuiteSucceeded).Should(BeTrue())
					Ω(summary.NumberOfExamplesBeforeParallelization).Should(Equal(3))
					Ω(summary.NumberOfTotalExamples).Should(Equal(3))
					Ω(summary.NumberOfExamplesThatWillBeRun).Should(Equal(2))
					Ω(summary.NumberOfPendingExamples).Should(Equal(0))
					Ω(summary.NumberOfSkippedExamples).Should(Equal(1))
					Ω(summary.NumberOfPassedExamples).Should(Equal(0))
					Ω(summary.NumberOfFailedExamples).Should(Equal(0))
					Ω(summary.RunTime).Should(Equal(time.Duration(0)))
				})

				It("publishes the correct example summaries", func() {
					Ω(fakeR.ExampleSummaries).Should(HaveLen(3))
					Ω(fakeR.ExampleSummaries[0]).Should(Equal(example1.summary(fakeR.BeginSummary.SuiteID)))
					Ω(fakeR.ExampleSummaries[1]).Should(Equal(example2.summary(fakeR.BeginSummary.SuiteID)))
					Ω(fakeR.ExampleSummaries[2]).Should(Equal(example3.summary(fakeR.BeginSummary.SuiteID)))
				})

				It("publishes the correct ending suite summary", func() {
					summary := fakeR.EndSummary
					Ω(summary.SuiteDescription).Should(Equal("collection description"))
					Ω(summary.SuiteSucceeded).Should(BeTrue())
					Ω(summary.NumberOfExamplesBeforeParallelization).Should(Equal(3))
					Ω(summary.NumberOfTotalExamples).Should(Equal(3))
					Ω(summary.NumberOfExamplesThatWillBeRun).Should(Equal(2))
					Ω(summary.NumberOfPendingExamples).Should(Equal(0))
					Ω(summary.NumberOfSkippedExamples).Should(Equal(1))
					Ω(summary.NumberOfPassedExamples).Should(Equal(2))
					Ω(summary.NumberOfFailedExamples).Should(Equal(0))
					Ω(summary.RunTime.Seconds()).Should(BeNumerically("~", 3*0.001, 0.01))
				})
			})

			Context("when a regexp skipString is provided", func() {
				BeforeEach(func() {
					conf.SkipString = `collection description.*pickles\d$`
					example1 = exampleWithItFunc("focused it 1", flagTypeFocused, false)
					example2 = exampleWithItFunc("another it pickles2", flagTypeNone, false)
					example3 = exampleWithItFunc("focused it pickles3", flagTypeFocused, false)
				})

				It("ignores the programmatic focus and applies the regexp skipString", func() {
					Ω(examplesThatWereRun).Should(Equal([]string{"focused it 1"}))
				})

				It("publishes the correct starting suite summary", func() {
					summary := fakeR.BeginSummary
					Ω(summary.SuiteDescription).Should(Equal("collection description"))
					Ω(summary.SuiteSucceeded).Should(BeTrue())
					Ω(summary.NumberOfExamplesBeforeParallelization).Should(Equal(3))
					Ω(summary.NumberOfTotalExamples).Should(Equal(3))
					Ω(summary.NumberOfExamplesThatWillBeRun).Should(Equal(1))
					Ω(summary.NumberOfPendingExamples).Should(Equal(0))
					Ω(summary.NumberOfSkippedExamples).Should(Equal(2))
					Ω(summary.NumberOfPassedExamples).Should(Equal(0))
					Ω(summary.NumberOfFailedExamples).Should(Equal(0))
					Ω(summary.RunTime).Should(Equal(time.Duration(0)))
				})

				It("publishes the correct example summaries", func() {
					Ω(fakeR.ExampleSummaries).Should(HaveLen(3))
					Ω(fakeR.ExampleSummaries[0]).Should(Equal(example1.summary(fakeR.BeginSummary.SuiteID)))
					Ω(fakeR.ExampleSummaries[1]).Should(Equal(example2.summary(fakeR.BeginSummary.SuiteID)))
					Ω(fakeR.ExampleSummaries[2]).Should(Equal(example3.summary(fakeR.BeginSummary.SuiteID)))
				})

				It("publishes the correct ending suite summary", func() {
					summary := fakeR.EndSummary
					Ω(summary.SuiteDescription).Should(Equal("collection description"))
					Ω(summary.SuiteSucceeded).Should(BeTrue())
					Ω(summary.NumberOfExamplesBeforeParallelization).Should(Equal(3))
					Ω(summary.NumberOfTotalExamples).Should(Equal(3))
					Ω(summary.NumberOfExamplesThatWillBeRun).Should(Equal(1))
					Ω(summary.NumberOfPendingExamples).Should(Equal(0))
					Ω(summary.NumberOfSkippedExamples).Should(Equal(2))
					Ω(summary.NumberOfPassedExamples).Should(Equal(1))
					Ω(summary.NumberOfFailedExamples).Should(Equal(0))
					Ω(summary.RunTime.Seconds()).Should(BeNumerically("~", 3*0.001, 0.01))
				})
			})

			Context("when both a regexp skipString and focusString are provided", func() {
				BeforeEach(func() {
					conf.SkipString = `collection description.*2`
					conf.FocusString = `collection description.*A`
					example1 = exampleWithItFunc("A1", flagTypeFocused, false)
					example2 = exampleWithItFunc("A2", flagTypeNone, false)
					example3 = exampleWithItFunc("B1", flagTypeFocused, false)
				})

				It("ignores the programmatic focus and ANDs the focusString and skipString", func() {
					Ω(examplesThatWereRun).Should(Equal([]string{"A1"}))
				})

				It("publishes the correct starting suite summary", func() {
					summary := fakeR.BeginSummary
					Ω(summary.SuiteDescription).Should(Equal("collection description"))
					Ω(summary.SuiteSucceeded).Should(BeTrue())
					Ω(summary.NumberOfExamplesBeforeParallelization).Should(Equal(3))
					Ω(summary.NumberOfTotalExamples).Should(Equal(3))
					Ω(summary.NumberOfExamplesThatWillBeRun).Should(Equal(1))
					Ω(summary.NumberOfPendingExamples).Should(Equal(0))
					Ω(summary.NumberOfSkippedExamples).Should(Equal(2))
					Ω(summary.NumberOfPassedExamples).Should(Equal(0))
					Ω(summary.NumberOfFailedExamples).Should(Equal(0))
					Ω(summary.RunTime).Should(Equal(time.Duration(0)))
				})

				It("publishes the correct example summaries", func() {
					Ω(fakeR.ExampleSummaries).Should(HaveLen(3))
					Ω(fakeR.ExampleSummaries[0]).Should(Equal(example1.summary(fakeR.BeginSummary.SuiteID)))
					Ω(fakeR.ExampleSummaries[1]).Should(Equal(example2.summary(fakeR.BeginSummary.SuiteID)))
					Ω(fakeR.ExampleSummaries[2]).Should(Equal(example3.summary(fakeR.BeginSummary.SuiteID)))
				})

				It("publishes the correct ending suite summary", func() {
					summary := fakeR.EndSummary
					Ω(summary.SuiteDescription).Should(Equal("collection description"))
					Ω(summary.SuiteSucceeded).Should(BeTrue())
					Ω(summary.NumberOfExamplesBeforeParallelization).Should(Equal(3))
					Ω(summary.NumberOfTotalExamples).Should(Equal(3))
					Ω(summary.NumberOfExamplesThatWillBeRun).Should(Equal(1))
					Ω(summary.NumberOfPendingExamples).Should(Equal(0))
					Ω(summary.NumberOfSkippedExamples).Should(Equal(2))
					Ω(summary.NumberOfPassedExamples).Should(Equal(1))
					Ω(summary.NumberOfFailedExamples).Should(Equal(0))
					Ω(summary.RunTime.Seconds()).Should(BeNumerically("~", 3*0.001, 0.01))
				})
			})

			Context("when a examples are run in parallel", func() {
				BeforeEach(func() {
					conf.ParallelTotal = 2
					conf.ParallelNode = 2
				})

				It("trims the example set before running them", func() {
					Ω(examplesThatWereRun).Should(Equal([]string{"it 2", "it 3"}))
				})

				It("publishes the correct starting suite summary", func() {
					summary := fakeR.BeginSummary
					Ω(summary.SuiteDescription).Should(Equal("collection description"))
					Ω(summary.SuiteSucceeded).Should(BeTrue())
					Ω(summary.NumberOfExamplesBeforeParallelization).Should(Equal(3))
					Ω(summary.NumberOfTotalExamples).Should(Equal(2))
					Ω(summary.NumberOfExamplesThatWillBeRun).Should(Equal(2))
					Ω(summary.NumberOfPendingExamples).Should(Equal(0))
					Ω(summary.NumberOfSkippedExamples).Should(Equal(0))
					Ω(summary.NumberOfPassedExamples).Should(Equal(0))
					Ω(summary.NumberOfFailedExamples).Should(Equal(0))
					Ω(summary.RunTime).Should(Equal(time.Duration(0)))
				})

				It("publishes the correct example summaries", func() {
					Ω(fakeR.ExampleSummaries).Should(HaveLen(2))
					Ω(fakeR.ExampleSummaries[0]).Should(Equal(example2.summary(fakeR.BeginSummary.SuiteID)))
					Ω(fakeR.ExampleSummaries[1]).Should(Equal(example3.summary(fakeR.BeginSummary.SuiteID)))
				})

				It("publishes the correct ending suite summary", func() {
					summary := fakeR.EndSummary
					Ω(summary.SuiteDescription).Should(Equal("collection description"))
					Ω(summary.SuiteSucceeded).Should(BeTrue())
					Ω(summary.NumberOfExamplesBeforeParallelization).Should(Equal(3))
					Ω(summary.NumberOfTotalExamples).Should(Equal(2))
					Ω(summary.NumberOfExamplesThatWillBeRun).Should(Equal(2))
					Ω(summary.NumberOfPendingExamples).Should(Equal(0))
					Ω(summary.NumberOfSkippedExamples).Should(Equal(0))
					Ω(summary.NumberOfPassedExamples).Should(Equal(2))
					Ω(summary.NumberOfFailedExamples).Should(Equal(0))
					Ω(summary.RunTime.Seconds()).Should(BeNumerically("~", 3*0.001, 0.01))
				})
			})
		})

		Describe("measurements", func() {
			exampleWithMeasure := func(text string) *example {
				return newExample(newMeasureNode(text, func(b Benchmarker) {
					examplesThatWereRun = append(examplesThatWereRun, text)
				}, flagTypeNone, types.GenerateCodeLocation(0), 1))
			}

			var conf config.GinkgoConfigType

			BeforeEach(func() {
				conf = config.GinkgoConfigType{}
			})

			JustBeforeEach(func() {
				collection = newExampleCollection(fakeT, "collection description", []*example{
					exampleWithItFunc("C", flagTypeNone, false),
					exampleWithItFunc("A", flagTypeNone, false),
					exampleWithItFunc("B", flagTypeNone, false),
					exampleWithMeasure("measure"),
				}, []Reporter{fakeR}, writer, conf)

				collection.run()
			})

			It("runs the measurement", func() {
				Ω(examplesThatWereRun).Should(ContainElement("A"))
				Ω(examplesThatWereRun).Should(ContainElement("measure"))
			})

			Context("when instructed to skip measurements", func() {
				BeforeEach(func() {
					conf = config.GinkgoConfigType{
						SkipMeasurements: true,
					}
				})

				It("skips the measurements", func() {
					Ω(examplesThatWereRun).Should(ContainElement("A"))
					Ω(examplesThatWereRun).ShouldNot(ContainElement("measure"))
				})
			})
		})
	})
}
예제 #14
0
func init() {
	Describe("Suite", func() {
		var (
			specSuite *suite
			fakeT     *fakeTestingT
			fakeR     *reporters.FakeReporter
			writer    *fakeGinkgoWriter
		)

		BeforeEach(func() {
			writer = &fakeGinkgoWriter{}
			fakeT = &fakeTestingT{}
			fakeR = reporters.NewFakeReporter()
			specSuite = newSuite()
		})

		Describe("running a suite", func() {
			var (
				runOrder          []string
				randomizeAllSpecs bool
				randomSeed        int64
				focusString       string
				parallelNode      int
				parallelTotal     int
				runResult         bool
			)

			var f = func(runText string) func() {
				return func() {
					runOrder = append(runOrder, runText)
				}
			}

			BeforeEach(func() {
				randomizeAllSpecs = false
				randomSeed = 11
				parallelNode = 1
				parallelTotal = 1
				focusString = ""

				runOrder = make([]string, 0)
				specSuite.pushBeforeEachNode(f("top BE"), types.GenerateCodeLocation(0), 0)
				specSuite.pushJustBeforeEachNode(f("top JBE"), types.GenerateCodeLocation(0), 0)
				specSuite.pushAfterEachNode(f("top AE"), types.GenerateCodeLocation(0), 0)

				specSuite.pushContainerNode("container", func() {
					specSuite.pushBeforeEachNode(f("BE"), types.GenerateCodeLocation(0), 0)
					specSuite.pushJustBeforeEachNode(f("JBE"), types.GenerateCodeLocation(0), 0)
					specSuite.pushAfterEachNode(f("AE"), types.GenerateCodeLocation(0), 0)
					specSuite.pushItNode("it", f("IT"), flagTypeNone, types.GenerateCodeLocation(0), 0)

					specSuite.pushContainerNode("inner container", func() {
						specSuite.pushItNode("inner it", f("inner IT"), flagTypeNone, types.GenerateCodeLocation(0), 0)
					}, flagTypeNone, types.GenerateCodeLocation(0))
				}, flagTypeNone, types.GenerateCodeLocation(0))

				specSuite.pushContainerNode("container 2", func() {
					specSuite.pushBeforeEachNode(f("BE 2"), types.GenerateCodeLocation(0), 0)
					specSuite.pushItNode("it 2", f("IT 2"), flagTypeNone, types.GenerateCodeLocation(0), 0)
				}, flagTypeNone, types.GenerateCodeLocation(0))

				specSuite.pushItNode("top level it", f("top IT"), flagTypeNone, types.GenerateCodeLocation(0), 0)
			})

			JustBeforeEach(func() {
				runResult = specSuite.run(fakeT, "suite description", []Reporter{fakeR}, writer, config.GinkgoConfigType{
					RandomSeed:        randomSeed,
					RandomizeAllSpecs: randomizeAllSpecs,
					FocusString:       focusString,
					ParallelNode:      parallelNode,
					ParallelTotal:     parallelTotal,
				})
			})

			It("provides the config and suite description to the reporter", func() {
				Ω(fakeR.Config.RandomSeed).Should(Equal(int64(randomSeed)))
				Ω(fakeR.Config.RandomizeAllSpecs).Should(Equal(randomizeAllSpecs))
				Ω(fakeR.BeginSummary.SuiteDescription).Should(Equal("suite description"))
			})

			It("provides information about the current test", func() {
				description := CurrentGinkgoTestDescription()
				Ω(description.ComponentTexts).Should(Equal([]string{"Suite", "running a suite", "provides information about the current test"}))
				Ω(description.FullTestText).Should(Equal("Suite running a suite provides information about the current test"))
				Ω(description.TestText).Should(Equal("provides information about the current test"))
				Ω(description.IsMeasurement).Should(BeFalse())
				Ω(description.FileName).Should(ContainSubstring("suite_test.go"))
				Ω(description.LineNumber).Should(BeNumerically(">", 50))
				Ω(description.LineNumber).Should(BeNumerically("<", 150))
			})

			Measure("should run measurements", func(b Benchmarker) {
				r := rand.New(rand.NewSource(time.Now().UnixNano()))

				runtime := b.Time("sleeping", func() {
					sleepTime := time.Duration(r.Float64() * 0.01 * float64(time.Second))
					time.Sleep(sleepTime)
				})
				Ω(runtime.Seconds()).Should(BeNumerically("<=", 0.012))
				Ω(runtime.Seconds()).Should(BeNumerically(">=", 0))

				randomValue := r.Float64() * 10.0
				b.RecordValue("random value", randomValue)
				Ω(randomValue).Should(BeNumerically("<=", 10.0))
				Ω(randomValue).Should(BeNumerically(">=", 0.0))
			}, 10)

			It("creates a node hierarchy, converts it to an example collection, and runs it", func() {
				Ω(runOrder).Should(Equal([]string{
					"top BE", "BE", "top JBE", "JBE", "IT", "AE", "top AE",
					"top BE", "BE", "top JBE", "JBE", "inner IT", "AE", "top AE",
					"top BE", "BE 2", "top JBE", "IT 2", "top AE",
					"top BE", "top JBE", "top IT", "top AE",
				}))
			})

			Context("when told to randomize all examples", func() {
				BeforeEach(func() {
					randomizeAllSpecs = true
				})

				It("does", func() {
					Ω(runOrder).Should(Equal([]string{
						"top BE", "top JBE", "top IT", "top AE",
						"top BE", "BE", "top JBE", "JBE", "inner IT", "AE", "top AE",
						"top BE", "BE", "top JBE", "JBE", "IT", "AE", "top AE",
						"top BE", "BE 2", "top JBE", "IT 2", "top AE",
					}))
				})
			})

			Describe("with ginkgo.parallel.total > 1", func() {
				BeforeEach(func() {
					parallelTotal = 2
					randomizeAllSpecs = true
				})

				Context("for one worker", func() {
					BeforeEach(func() {
						parallelNode = 1
					})

					It("should run a subset of tests", func() {
						Ω(runOrder).Should(Equal([]string{
							"top BE", "top JBE", "top IT", "top AE",
							"top BE", "BE", "top JBE", "JBE", "inner IT", "AE", "top AE",
						}))
					})
				})

				Context("for another worker", func() {
					BeforeEach(func() {
						parallelNode = 2
					})

					It("should run a (different) subset of tests", func() {
						Ω(runOrder).Should(Equal([]string{
							"top BE", "BE", "top JBE", "JBE", "IT", "AE", "top AE",
							"top BE", "BE 2", "top JBE", "IT 2", "top AE",
						}))
					})
				})
			})

			Context("when provided with a filter", func() {
				BeforeEach(func() {
					focusString = `inner|\d`
				})

				It("converts the filter to a regular expression and uses it to filter the running examples", func() {
					Ω(runOrder).Should(Equal([]string{
						"top BE", "BE", "top JBE", "JBE", "inner IT", "AE", "top AE",
						"top BE", "BE 2", "top JBE", "IT 2", "top AE",
					}))
				})
			})

			Context("when the specs pass", func() {
				It("doesn't report a failure", func() {
					Ω(fakeT.didFail).Should(BeFalse())
				})

				It("should return true", func() {
					Ω(runResult).Should(BeTrue())
				})
			})

			Context("when a spec fails", func() {
				var location types.CodeLocation
				BeforeEach(func() {
					specSuite.pushItNode("top level it", func() {
						location = types.GenerateCodeLocation(0)
						func() { specSuite.fail("oops!", 0) }()
					}, flagTypeNone, types.GenerateCodeLocation(0), 0)
				})

				It("should return false", func() {
					Ω(runResult).Should(BeFalse())
				})

				It("reports a failure", func() {
					Ω(fakeT.didFail).Should(BeTrue())
				})

				It("generates the correct failure data", func() {
					Ω(fakeR.ExampleSummaries[0].Failure.Message).Should(Equal("oops!"))
					Ω(fakeR.ExampleSummaries[0].Failure.Location.FileName).Should(Equal(location.FileName))
					Ω(fakeR.ExampleSummaries[0].Failure.Location.LineNumber).Should(Equal(location.LineNumber + 1))
				})
			})
		})
	})
}
예제 #15
0
func init() {
	Describe("Example", func() {
		var it *itNode

		BeforeEach(func() {
			it = newItNode("It", func() {}, flagTypeNone, types.GenerateCodeLocation(0), 0)
		})

		Describe("creating examples and adding container nodes", func() {
			var (
				containerA *containerNode
				containerB *containerNode
				ex         *example
			)

			BeforeEach(func() {
				containerA = newContainerNode("A", flagTypeNone, types.GenerateCodeLocation(0))
				containerB = newContainerNode("B", flagTypeNone, types.GenerateCodeLocation(0))
			})

			JustBeforeEach(func() {
				ex = newExample(it)
				ex.addContainerNode(containerB)
				ex.addContainerNode(containerA)
			})

			It("should store off the it node", func() {
				Ω(ex.subject).Should(Equal(it))
			})

			It("should store off the container nodes in reverse order", func() {
				Ω(ex.containers).Should(Equal([]*containerNode{containerA, containerB}))
			})

			It("should provide the concatenated strings", func() {
				Ω(ex.concatenatedString()).Should(Equal("A B It"))
			})

			Context("when neither the It node nor the containers is focused or pending", func() {
				It("should not be focused or pending", func() {
					Ω(ex.focused).Should(BeFalse())
					Ω(ex.state).Should(BeZero())
				})
			})

			Context("when the It node is focused", func() {
				BeforeEach(func() {
					it.flag = flagTypeFocused
				})

				It("should be focused", func() {
					Ω(ex.focused).Should(BeTrue())
				})
			})

			Context("when one of the containers is focused", func() {
				BeforeEach(func() {
					containerB.flag = flagTypeFocused
				})

				It("should be focused", func() {
					Ω(ex.focused).Should(BeTrue())
				})
			})

			Context("when the It node is pending", func() {
				BeforeEach(func() {
					it.flag = flagTypePending
				})

				It("should be in the pending state", func() {
					Ω(ex.state).Should(Equal(types.ExampleStatePending))
				})
			})

			Context("when one of the containers is pending", func() {
				BeforeEach(func() {
					containerB.flag = flagTypePending
				})

				It("should be in the pending state", func() {
					Ω(ex.state).Should(Equal(types.ExampleStatePending))
				})
			})

			Context("when one container is pending and another container is focused", func() {
				BeforeEach(func() {
					containerA.flag = flagTypeFocused
					containerB.flag = flagTypePending
				})

				It("should be focused and have the pending state", func() {
					Ω(ex.focused).Should(BeTrue())
					Ω(ex.state).Should(Equal(types.ExampleStatePending))
				})
			})
		})

		Describe("Skipping an example", func() {
			It("should mark the example as skipped", func() {
				ex := newExample(it)
				ex.skip()
				Ω(ex.state).Should(Equal(types.ExampleStateSkipped))
			})
		})

		Describe("skippedOrPending", func() {
			It("should be false if the example is neither pending nor skipped", func() {
				ex := newExample(it)
				Ω(ex.skippedOrPending()).Should(BeFalse())
			})

			It("should be true if the example is pending", func() {
				it.flag = flagTypePending
				ex := newExample(it)
				Ω(ex.skippedOrPending()).Should(BeTrue())
			})

			It("should be true if the example is skipped", func() {
				ex := newExample(it)
				ex.skip()
				Ω(ex.skippedOrPending()).Should(BeTrue())
			})
		})

		Describe("pending", func() {
			It("should be false if the example is not pending", func() {
				ex := newExample(it)
				Ω(ex.pending()).Should(BeFalse())
			})

			It("should be true if the example is pending", func() {
				it.flag = flagTypePending
				ex := newExample(it)
				Ω(ex.pending()).Should(BeTrue())
			})
		})

		Describe("running examples and getting summaries", func() {
			var (
				orderedList    []string
				it             *itNode
				innerContainer *containerNode
				outerContainer *containerNode
				ex             *example
			)

			newNode := func(identifier string) *runnableNode {
				return newRunnableNode(func() {
					orderedList = append(orderedList, identifier)
				}, types.GenerateCodeLocation(0), 0)
			}

			BeforeEach(func() {
				orderedList = make([]string, 0)
				it = newItNode("it", func() {
					orderedList = append(orderedList, "IT")
					time.Sleep(time.Duration(0.01 * float64(time.Second)))
				}, flagTypeNone, types.GenerateCodeLocation(0), 0)
				ex = newExample(it)

				innerContainer = newContainerNode("inner", flagTypeNone, types.GenerateCodeLocation(0))
				innerContainer.pushBeforeEachNode(newNode("INNER_BEFORE_A"))
				innerContainer.pushBeforeEachNode(newNode("INNER_BEFORE_B"))
				innerContainer.pushJustBeforeEachNode(newNode("INNER_JUST_BEFORE_A"))
				innerContainer.pushJustBeforeEachNode(newNode("INNER_JUST_BEFORE_B"))
				innerContainer.pushAfterEachNode(newNode("INNER_AFTER_A"))
				innerContainer.pushAfterEachNode(newNode("INNER_AFTER_B"))

				ex.addContainerNode(innerContainer)

				outerContainer = newContainerNode("outer", flagTypeNone, types.GenerateCodeLocation(0))
				outerContainer.pushBeforeEachNode(newNode("OUTER_BEFORE_A"))
				outerContainer.pushBeforeEachNode(newNode("OUTER_BEFORE_B"))
				outerContainer.pushJustBeforeEachNode(newNode("OUTER_JUST_BEFORE_A"))
				outerContainer.pushJustBeforeEachNode(newNode("OUTER_JUST_BEFORE_B"))
				outerContainer.pushAfterEachNode(newNode("OUTER_AFTER_A"))
				outerContainer.pushAfterEachNode(newNode("OUTER_AFTER_B"))

				ex.addContainerNode(outerContainer)
			})

			It("should report that it has an it node", func() {
				Ω(ex.subjectComponentType()).Should(Equal(types.ExampleComponentTypeIt))
			})

			It("runs the before/justBefore/after nodes in each of the containers, and the it node, in the correct order", func() {
				ex.run()
				Ω(orderedList).Should(Equal([]string{
					"OUTER_BEFORE_A",
					"OUTER_BEFORE_B",
					"INNER_BEFORE_A",
					"INNER_BEFORE_B",
					"OUTER_JUST_BEFORE_A",
					"OUTER_JUST_BEFORE_B",
					"INNER_JUST_BEFORE_A",
					"INNER_JUST_BEFORE_B",
					"IT",
					"INNER_AFTER_A",
					"INNER_AFTER_B",
					"OUTER_AFTER_A",
					"OUTER_AFTER_B",
				}))
			})

			Describe("the summary", func() {
				It("has the texts and code locations for the container nodes and the it node", func() {
					ex.run()
					summary := ex.summary("suite-id")
					Ω(summary.ComponentTexts).Should(Equal([]string{
						"outer", "inner", "it",
					}))
					Ω(summary.ComponentCodeLocations).Should(Equal([]types.CodeLocation{
						outerContainer.codeLocation, innerContainer.codeLocation, it.codeLocation,
					}))
				})

				It("should have the passed in SuiteID", func() {
					ex.run()
					summary := ex.summary("suite-id")
					Ω(summary.SuiteID).Should(Equal("suite-id"))
				})

				It("should include the example's index", func() {
					ex.exampleIndex = 17
					ex.run()
					summary := ex.summary("suite-id")
					Ω(summary.ExampleIndex).Should(Equal(17))
				})
			})

			Describe("the GinkgoTestDescription", func() {
				It("should have the GinkgoTestDescription", func() {
					ginkgoTestDescription := ex.ginkgoTestDescription()
					Ω(ginkgoTestDescription.ComponentTexts).Should(Equal([]string{
						"inner", "it",
					}))

					Ω(ginkgoTestDescription.FullTestText).Should(Equal("inner it"))
					Ω(ginkgoTestDescription.TestText).Should(Equal("it"))
					Ω(ginkgoTestDescription.IsMeasurement).Should(BeFalse())
					Ω(ginkgoTestDescription.FileName).Should(Equal(it.codeLocation.FileName))
					Ω(ginkgoTestDescription.LineNumber).Should(Equal(it.codeLocation.LineNumber))
				})
			})

			Context("when none of the runnable nodes fail", func() {
				It("has a summary reporting no failure", func() {
					ex.run()
					summary := ex.summary("suite-id")
					Ω(summary.State).Should(Equal(types.ExampleStatePassed))
					Ω(summary.RunTime.Seconds()).Should(BeNumerically(">", 0.01))
					Ω(summary.IsMeasurement).Should(BeFalse())
				})
			})

			componentTypes := []string{"BeforeEach", "JustBeforeEach", "AfterEach"}
			expectedComponentTypes := []types.ExampleComponentType{types.ExampleComponentTypeBeforeEach, types.ExampleComponentTypeJustBeforeEach, types.ExampleComponentTypeAfterEach}
			pushFuncs := []func(container *containerNode, node *runnableNode){(*containerNode).pushBeforeEachNode, (*containerNode).pushJustBeforeEachNode, (*containerNode).pushAfterEachNode}

			for i := range componentTypes {
				Context(fmt.Sprintf("when a %s node fails", componentTypes[i]), func() {
					var componentCodeLocation types.CodeLocation

					BeforeEach(func() {
						componentCodeLocation = types.GenerateCodeLocation(0)
					})

					Context("because an expectation failed", func() {
						var failure failureData

						BeforeEach(func() {
							failure = failureData{
								message:      fmt.Sprintf("%s failed", componentTypes[i]),
								codeLocation: types.GenerateCodeLocation(0),
							}
							node := newRunnableNode(func() {
								ex.fail(failure)
								ex.fail(failureData{message: "IGNORE ME!"})
							}, componentCodeLocation, 0)

							pushFuncs[i](innerContainer, node)
						})

						It("has a summary with the correct failure report", func() {
							ex.run()
							summary := ex.summary("suite-id")

							Ω(summary.State).Should(Equal(types.ExampleStateFailed))
							Ω(summary.Failure.Message).Should(Equal(failure.message))
							Ω(summary.Failure.Location).Should(Equal(failure.codeLocation))
							Ω(summary.Failure.ForwardedPanic).Should(BeNil())
							Ω(summary.Failure.ComponentIndex).Should(Equal(1), "Should be the inner container that failed")
							Ω(summary.Failure.ComponentType).Should(Equal(expectedComponentTypes[i]))
							Ω(summary.Failure.ComponentCodeLocation).Should(Equal(componentCodeLocation))

							Ω(ex.failed()).Should(BeTrue())
						})
					})

					Context("because the function panicked", func() {
						var panicCodeLocation types.CodeLocation

						BeforeEach(func() {
							node := newRunnableNode(func() {
								panicCodeLocation = types.GenerateCodeLocation(0)
								panic("kaboom!")
							}, componentCodeLocation, 0)

							pushFuncs[i](innerContainer, node)
						})

						It("has a summary with the correct failure report", func() {
							ex.run()
							summary := ex.summary("suite-id")

							Ω(summary.State).Should(Equal(types.ExampleStatePanicked))
							Ω(summary.Failure.Message).Should(Equal("Test Panicked"))
							Ω(summary.Failure.Location.FileName).Should(Equal(panicCodeLocation.FileName))
							Ω(summary.Failure.Location.LineNumber).Should(Equal(panicCodeLocation.LineNumber+1), "Expect panic code location to be correct")
							Ω(summary.Failure.ForwardedPanic).Should(Equal("kaboom!"))
							Ω(summary.Failure.ComponentIndex).Should(Equal(1), "Should be the inner container that failed")
							Ω(summary.Failure.ComponentType).Should(Equal(expectedComponentTypes[i]))
							Ω(summary.Failure.ComponentCodeLocation).Should(Equal(componentCodeLocation))

							Ω(ex.failed()).Should(BeTrue())
						})
					})

					Context("because the function timed out", func() {
						BeforeEach(func() {
							node := newRunnableNode(func(done Done) {
								time.Sleep(time.Duration(0.002 * float64(time.Second)))
								done <- true
							}, componentCodeLocation, time.Duration(0.001*float64(time.Second)))

							pushFuncs[i](innerContainer, node)
						})

						It("has a summary with the correct failure report", func() {
							ex.run()
							summary := ex.summary("suite-id")

							Ω(summary.State).Should(Equal(types.ExampleStateTimedOut))
							Ω(summary.Failure.Message).Should(Equal("Timed out"))
							Ω(summary.Failure.Location).Should(Equal(componentCodeLocation))
							Ω(summary.Failure.ForwardedPanic).Should(BeNil())
							Ω(summary.Failure.ComponentIndex).Should(Equal(1), "Should be the inner container that failed")
							Ω(summary.Failure.ComponentType).Should(Equal(expectedComponentTypes[i]))
							Ω(summary.Failure.ComponentCodeLocation).Should(Equal(componentCodeLocation))

							Ω(ex.failed()).Should(BeTrue())
						})
					})
				})
			}

			Context("when the it node fails", func() {
				var componentCodeLocation types.CodeLocation

				BeforeEach(func() {
					componentCodeLocation = types.GenerateCodeLocation(0)
				})

				Context("because an expectation failed", func() {
					var failure failureData

					BeforeEach(func() {
						failure = failureData{
							message:      "it failed",
							codeLocation: types.GenerateCodeLocation(0),
						}
						ex.subject = newItNode("it", func() {
							ex.fail(failure)
							ex.fail(failureData{message: "IGNORE ME!"})
						}, flagTypeNone, componentCodeLocation, 0)
					})

					It("has a summary with the correct failure report", func() {
						ex.run()
						summary := ex.summary("suite-id")

						Ω(summary.State).Should(Equal(types.ExampleStateFailed))
						Ω(summary.Failure.Message).Should(Equal(failure.message))
						Ω(summary.Failure.Location).Should(Equal(failure.codeLocation))
						Ω(summary.Failure.ForwardedPanic).Should(BeNil())
						Ω(summary.Failure.ComponentIndex).Should(Equal(2), "Should be the it node that failed")
						Ω(summary.Failure.ComponentType).Should(Equal(types.ExampleComponentTypeIt))
						Ω(summary.Failure.ComponentCodeLocation).Should(Equal(componentCodeLocation))

						Ω(ex.failed()).Should(BeTrue())
					})
				})

				Context("because the function panicked", func() {
					var panicCodeLocation types.CodeLocation

					BeforeEach(func() {
						ex.subject = newItNode("it", func() {
							panicCodeLocation = types.GenerateCodeLocation(0)
							panic("kaboom!")
						}, flagTypeNone, componentCodeLocation, 0)
					})

					It("has a summary with the correct failure report", func() {
						ex.run()
						summary := ex.summary("suite-id")

						Ω(summary.State).Should(Equal(types.ExampleStatePanicked))
						Ω(summary.Failure.Message).Should(Equal("Test Panicked"))
						Ω(summary.Failure.Location.FileName).Should(Equal(panicCodeLocation.FileName))
						Ω(summary.Failure.Location.LineNumber).Should(Equal(panicCodeLocation.LineNumber+1), "Expect panic code location to be correct")
						Ω(summary.Failure.ForwardedPanic).Should(Equal("kaboom!"))
						Ω(summary.Failure.ComponentIndex).Should(Equal(2), "Should be the it node that failed")
						Ω(summary.Failure.ComponentType).Should(Equal(types.ExampleComponentTypeIt))
						Ω(summary.Failure.ComponentCodeLocation).Should(Equal(componentCodeLocation))

						Ω(ex.failed()).Should(BeTrue())
					})
				})

				Context("because the function timed out", func() {
					BeforeEach(func() {
						ex.subject = newItNode("it", func(done Done) {
							time.Sleep(time.Duration(0.002 * float64(time.Second)))
							done <- true
						}, flagTypeNone, componentCodeLocation, time.Duration(0.001*float64(time.Second)))
					})

					It("has a summary with the correct failure report", func() {
						ex.run()
						summary := ex.summary("suite-id")

						Ω(summary.State).Should(Equal(types.ExampleStateTimedOut))
						Ω(summary.Failure.Message).Should(Equal("Timed out"))
						Ω(summary.Failure.Location).Should(Equal(componentCodeLocation))
						Ω(summary.Failure.ForwardedPanic).Should(BeNil())
						Ω(summary.Failure.ComponentIndex).Should(Equal(2), "Should be the it node that failed")
						Ω(summary.Failure.ComponentType).Should(Equal(types.ExampleComponentTypeIt))
						Ω(summary.Failure.ComponentCodeLocation).Should(Equal(componentCodeLocation))

						Ω(ex.failed()).Should(BeTrue())
					})
				})
			})
		})

		Describe("running measurement examples and getting summaries", func() {
			var (
				runs                  int
				componentCodeLocation types.CodeLocation
				ex                    *example
			)

			BeforeEach(func() {
				runs = 0
				componentCodeLocation = types.GenerateCodeLocation(0)
			})

			It("should report that it has a measurement", func() {
				ex = newExample(newMeasureNode("measure", func(b Benchmarker) {}, flagTypeNone, componentCodeLocation, 1))
				Ω(ex.subjectComponentType()).Should(Equal(types.ExampleComponentTypeMeasure))
			})

			Context("when the measurement does not fail", func() {
				BeforeEach(func() {
					ex = newExample(newMeasureNode("measure", func(b Benchmarker) {
						b.RecordValue("foo", float64(runs))
						runs++
					}, flagTypeNone, componentCodeLocation, 5))
				})

				It("runs the measurement samples number of times and returns statistics", func() {
					ex.run()
					summary := ex.summary("suite-id")

					Ω(runs).Should(Equal(5))

					Ω(summary.State).Should(Equal(types.ExampleStatePassed))
					Ω(summary.IsMeasurement).Should(BeTrue())
					Ω(summary.NumberOfSamples).Should(Equal(5))
					Ω(summary.Measurements).Should(HaveLen(1))
					Ω(summary.Measurements["foo"].Name).Should(Equal("foo"))
					Ω(summary.Measurements["foo"].Results).Should(Equal([]float64{0, 1, 2, 3, 4}))
				})
			})

			Context("when one of the measurement samples fails", func() {
				BeforeEach(func() {
					ex = newExample(newMeasureNode("measure", func(b Benchmarker) {
						b.RecordValue("foo", float64(runs))
						runs++
						if runs == 3 {
							ex.fail(failureData{})
						}
					}, flagTypeNone, componentCodeLocation, 5))
				})

				It("marks the measurement as failed and doesn't run any more samples", func() {
					ex.run()
					summary := ex.summary("suite-id")

					Ω(runs).Should(Equal(3))

					Ω(summary.State).Should(Equal(types.ExampleStateFailed))
					Ω(summary.IsMeasurement).Should(BeTrue())
					Ω(summary.NumberOfSamples).Should(Equal(5))
					Ω(summary.Measurements).Should(BeEmpty())
				})
			})
		})

		Describe("running AfterEach nodes when other nodes fail", func() {
			var (
				orderedList []string
				ex          *example
			)

			newNode := func(identifier string, fail bool) *runnableNode {
				return newRunnableNode(func() {
					orderedList = append(orderedList, identifier)
					if fail {
						ex.fail(failureData{
							message: identifier + " failed",
						})
					}
				}, types.GenerateCodeLocation(0), 0)
			}

			newIt := func(identifier string, fail bool) *itNode {
				return newItNode(identifier, func() {
					orderedList = append(orderedList, identifier)
					if fail {
						ex.fail(failureData{})
					}
				}, flagTypeNone, types.GenerateCodeLocation(0), 0)
			}

			BeforeEach(func() {
				orderedList = make([]string, 0)
			})

			Context("when the it node fails", func() {
				BeforeEach(func() {
					ex = newExample(newIt("it", true))

					innerContainer := newContainerNode("inner", flagTypeNone, types.GenerateCodeLocation(0))
					innerContainer.pushBeforeEachNode(newNode("INNER_BEFORE_A", false))
					innerContainer.pushBeforeEachNode(newNode("INNER_BEFORE_B", false))
					innerContainer.pushJustBeforeEachNode(newNode("INNER_JUST_BEFORE_A", false))
					innerContainer.pushJustBeforeEachNode(newNode("INNER_JUST_BEFORE_B", false))
					innerContainer.pushAfterEachNode(newNode("INNER_AFTER_A", false))
					innerContainer.pushAfterEachNode(newNode("INNER_AFTER_B", false))

					ex.addContainerNode(innerContainer)

					outerContainer := newContainerNode("outer", flagTypeNone, types.GenerateCodeLocation(0))
					outerContainer.pushBeforeEachNode(newNode("OUTER_BEFORE_A", false))
					outerContainer.pushBeforeEachNode(newNode("OUTER_BEFORE_B", false))
					outerContainer.pushJustBeforeEachNode(newNode("OUTER_JUST_BEFORE_A", false))
					outerContainer.pushJustBeforeEachNode(newNode("OUTER_JUST_BEFORE_B", false))
					outerContainer.pushAfterEachNode(newNode("OUTER_AFTER_A", false))
					outerContainer.pushAfterEachNode(newNode("OUTER_AFTER_B", false))

					ex.addContainerNode(outerContainer)
					ex.run()
				})

				It("should run all the AfterEach nodes", func() {
					Ω(orderedList).Should(Equal([]string{
						"OUTER_BEFORE_A", "OUTER_BEFORE_B", "INNER_BEFORE_A", "INNER_BEFORE_B",
						"OUTER_JUST_BEFORE_A", "OUTER_JUST_BEFORE_B", "INNER_JUST_BEFORE_A", "INNER_JUST_BEFORE_B",
						"it",
						"INNER_AFTER_A", "INNER_AFTER_B", "OUTER_AFTER_A", "OUTER_AFTER_B",
					}))

				})
			})

			Context("when an inner BeforeEach node fails", func() {
				BeforeEach(func() {
					ex = newExample(newIt("it", true))

					innerContainer := newContainerNode("inner", flagTypeNone, types.GenerateCodeLocation(0))
					innerContainer.pushBeforeEachNode(newNode("INNER_BEFORE_A", true))
					innerContainer.pushBeforeEachNode(newNode("INNER_BEFORE_B", false))
					innerContainer.pushJustBeforeEachNode(newNode("INNER_JUST_BEFORE_A", false))
					innerContainer.pushJustBeforeEachNode(newNode("INNER_JUST_BEFORE_B", false))
					innerContainer.pushAfterEachNode(newNode("INNER_AFTER_A", false))
					innerContainer.pushAfterEachNode(newNode("INNER_AFTER_B", false))

					ex.addContainerNode(innerContainer)

					outerContainer := newContainerNode("outer", flagTypeNone, types.GenerateCodeLocation(0))
					outerContainer.pushBeforeEachNode(newNode("OUTER_BEFORE_A", false))
					outerContainer.pushBeforeEachNode(newNode("OUTER_BEFORE_B", false))
					outerContainer.pushJustBeforeEachNode(newNode("OUTER_JUST_BEFORE_A", false))
					outerContainer.pushJustBeforeEachNode(newNode("OUTER_JUST_BEFORE_B", false))
					outerContainer.pushAfterEachNode(newNode("OUTER_AFTER_A", false))
					outerContainer.pushAfterEachNode(newNode("OUTER_AFTER_B", false))

					ex.addContainerNode(outerContainer)
					ex.run()
				})

				It("should run all the AfterEach nodes at nesting levels equal to or lower than the failed BeforeEach block", func() {
					Ω(orderedList).Should(Equal([]string{
						"OUTER_BEFORE_A", "OUTER_BEFORE_B", "INNER_BEFORE_A",
						"INNER_AFTER_A", "INNER_AFTER_B", "OUTER_AFTER_A", "OUTER_AFTER_B",
					}))
				})
			})

			Context("when an outer BeforeEach node fails", func() {
				BeforeEach(func() {
					ex = newExample(newIt("it", true))

					innerContainer := newContainerNode("inner", flagTypeNone, types.GenerateCodeLocation(0))
					innerContainer.pushBeforeEachNode(newNode("INNER_BEFORE_A", false))
					innerContainer.pushBeforeEachNode(newNode("INNER_BEFORE_B", false))
					innerContainer.pushJustBeforeEachNode(newNode("INNER_JUST_BEFORE_A", false))
					innerContainer.pushJustBeforeEachNode(newNode("INNER_JUST_BEFORE_B", false))
					innerContainer.pushAfterEachNode(newNode("INNER_AFTER_A", false))
					innerContainer.pushAfterEachNode(newNode("INNER_AFTER_B", false))

					ex.addContainerNode(innerContainer)

					outerContainer := newContainerNode("outer", flagTypeNone, types.GenerateCodeLocation(0))
					outerContainer.pushBeforeEachNode(newNode("OUTER_BEFORE_A", false))
					outerContainer.pushBeforeEachNode(newNode("OUTER_BEFORE_B", true))
					outerContainer.pushJustBeforeEachNode(newNode("OUTER_JUST_BEFORE_A", false))
					outerContainer.pushJustBeforeEachNode(newNode("OUTER_JUST_BEFORE_B", false))
					outerContainer.pushAfterEachNode(newNode("OUTER_AFTER_A", false))
					outerContainer.pushAfterEachNode(newNode("OUTER_AFTER_B", false))

					ex.addContainerNode(outerContainer)
					ex.run()
				})

				It("should run all the AfterEach nodes at nesting levels equal to or lower than the failed BeforeEach block", func() {
					Ω(orderedList).Should(Equal([]string{
						"OUTER_BEFORE_A", "OUTER_BEFORE_B",
						"OUTER_AFTER_A", "OUTER_AFTER_B",
					}))
				})
			})

			Context("when a JustBeforeEach node fails", func() {
				BeforeEach(func() {
					ex = newExample(newIt("it", true))

					innerContainer := newContainerNode("inner", flagTypeNone, types.GenerateCodeLocation(0))
					innerContainer.pushBeforeEachNode(newNode("INNER_BEFORE_A", false))
					innerContainer.pushBeforeEachNode(newNode("INNER_BEFORE_B", false))
					innerContainer.pushJustBeforeEachNode(newNode("INNER_JUST_BEFORE_A", false))
					innerContainer.pushJustBeforeEachNode(newNode("INNER_JUST_BEFORE_B", false))
					innerContainer.pushAfterEachNode(newNode("INNER_AFTER_A", false))
					innerContainer.pushAfterEachNode(newNode("INNER_AFTER_B", false))

					ex.addContainerNode(innerContainer)

					outerContainer := newContainerNode("outer", flagTypeNone, types.GenerateCodeLocation(0))
					outerContainer.pushBeforeEachNode(newNode("OUTER_BEFORE_A", false))
					outerContainer.pushBeforeEachNode(newNode("OUTER_BEFORE_B", false))
					outerContainer.pushJustBeforeEachNode(newNode("OUTER_JUST_BEFORE_A", true))
					outerContainer.pushJustBeforeEachNode(newNode("OUTER_JUST_BEFORE_B", false))
					outerContainer.pushAfterEachNode(newNode("OUTER_AFTER_A", false))
					outerContainer.pushAfterEachNode(newNode("OUTER_AFTER_B", false))

					ex.addContainerNode(outerContainer)
					ex.run()
				})

				It("should run all the AfterEach nodes", func() {
					Ω(orderedList).Should(Equal([]string{
						"OUTER_BEFORE_A", "OUTER_BEFORE_B", "INNER_BEFORE_A", "INNER_BEFORE_B",
						"OUTER_JUST_BEFORE_A",
						"INNER_AFTER_A", "INNER_AFTER_B", "OUTER_AFTER_A", "OUTER_AFTER_B",
					}))
				})
			})

			Context("when an AfterEach node fails", func() {
				BeforeEach(func() {
					ex = newExample(newIt("it", true))

					innerContainer := newContainerNode("inner", flagTypeNone, types.GenerateCodeLocation(0))
					innerContainer.pushBeforeEachNode(newNode("INNER_BEFORE_A", false))
					innerContainer.pushBeforeEachNode(newNode("INNER_BEFORE_B", false))
					innerContainer.pushJustBeforeEachNode(newNode("INNER_JUST_BEFORE_A", true))
					innerContainer.pushJustBeforeEachNode(newNode("INNER_JUST_BEFORE_B", false))
					innerContainer.pushAfterEachNode(newNode("INNER_AFTER_A", false))
					innerContainer.pushAfterEachNode(newNode("INNER_AFTER_B", true))

					ex.addContainerNode(innerContainer)

					outerContainer := newContainerNode("outer", flagTypeNone, types.GenerateCodeLocation(0))
					outerContainer.pushBeforeEachNode(newNode("OUTER_BEFORE_A", false))
					outerContainer.pushBeforeEachNode(newNode("OUTER_BEFORE_B", false))
					outerContainer.pushJustBeforeEachNode(newNode("OUTER_JUST_BEFORE_A", false))
					outerContainer.pushJustBeforeEachNode(newNode("OUTER_JUST_BEFORE_B", false))
					outerContainer.pushAfterEachNode(newNode("OUTER_AFTER_A", true))
					outerContainer.pushAfterEachNode(newNode("OUTER_AFTER_B", false))

					ex.addContainerNode(outerContainer)
					ex.run()
				})

				It("should nonetheless continue to run subsequent after each nodes", func() {
					Ω(orderedList).Should(Equal([]string{
						"OUTER_BEFORE_A", "OUTER_BEFORE_B", "INNER_BEFORE_A", "INNER_BEFORE_B",
						"OUTER_JUST_BEFORE_A", "OUTER_JUST_BEFORE_B", "INNER_JUST_BEFORE_A",
						"INNER_AFTER_A", "INNER_AFTER_B", "OUTER_AFTER_A", "OUTER_AFTER_B",
					}))
				})

				It("should not override the failure data of the earliest failure", func() {
					Ω(ex.summary("suite-id").Failure.Message).Should(Equal("INNER_JUST_BEFORE_A failed"))
				})
			})
		})
	})
}
예제 #16
0
func init() {
	Describe("RunnableNode", func() {
		Describe("basic construction parameters", func() {
			It("should store off the passed in code location", func() {
				codeLocation := types.GenerateCodeLocation(0)
				Ω(newRunnableNode(func() {}, codeLocation, 0).codeLocation).Should(Equal(codeLocation))
			})
		})

		Describe("running the passed in function", func() {
			Context("when the function is synchronous and does not panic", func() {
				It("should run the function and report a runOutcomeCompleted", func() {
					didRun := false
					runnableNode := newRunnableNode(func() {
						didRun = true
					}, types.GenerateCodeLocation(0), 0)

					outcome, failure := runnableNode.run()

					Ω(didRun).Should(BeTrue())
					Ω(outcome).Should(Equal(runOutcomeCompleted))
					Ω(failure).Should(BeZero())
				})
			})

			Context("when the function is synchronous and *does* panic", func() {
				var (
					codeLocation types.CodeLocation
					outcome      runOutcome
					failure      failureData
				)

				BeforeEach(func() {
					node := newRunnableNode(func() {
						codeLocation = types.GenerateCodeLocation(0)
						panic("ack!")
					}, types.GenerateCodeLocation(0), 0)

					outcome, failure = node.run()
				})

				It("should run the function and report a runOutcomePanicked", func() {
					Ω(outcome).Should(Equal(runOutcomePanicked))
					Ω(failure.message).Should(Equal("Test Panicked"))
				})

				It("should include the code location of the panic itself", func() {
					Ω(failure.codeLocation.FileName).Should(Equal(codeLocation.FileName))
					Ω(failure.codeLocation.LineNumber).Should(Equal(codeLocation.LineNumber + 1))
				})

				It("should include the panic data", func() {
					Ω(failure.forwardedPanic).Should(Equal("ack!"))
				})
			})

			Context("when the function is asynchronous", func() {
				var (
					node               *runnableNode
					sleepDuration      time.Duration
					timeoutDuration    time.Duration
					numberOfGoRoutines int
				)

				BeforeEach(func() {
					sleepDuration = time.Duration(0.001 * float64(time.Second))
					timeoutDuration = time.Duration(1 * float64(time.Second))
				})

				JustBeforeEach(func() {
					node = newRunnableNode(func(done Done) {
						numberOfGoRoutines = runtime.NumGoroutine()
						time.Sleep(sleepDuration)
						done <- true
					}, types.GenerateCodeLocation(0), timeoutDuration)
				})

				It("should run the function as a goroutine", func() {
					initialNumberOfGoRoutines := runtime.NumGoroutine()
					outcome, failure := node.run()

					Ω(outcome).Should(Equal(runOutcomeCompleted))
					Ω(failure).Should(BeZero())

					Ω(numberOfGoRoutines).Should(Equal(initialNumberOfGoRoutines + 1))
				})

				Context("when the function takes longer than the timeout", func() {
					BeforeEach(func() {
						sleepDuration = time.Duration(0.002 * float64(time.Second))
						timeoutDuration = time.Duration(0.001 * float64(time.Second))
					})

					It("should timeout", func() {
						outcome, failure := node.run()
						Ω(outcome).Should(Equal(runOutcomeTimedOut))
						Ω(failure.message).Should(Equal("Timed out"))
						Ω(failure.codeLocation).Should(Equal(node.codeLocation))
					})
				})
			})

			Context("when the function takes the wrong kind of argument", func() {
				It("should panic", func() {
					Ω(func() {
						newRunnableNode(func(oops string) {
						}, types.GenerateCodeLocation(0), 0)
					}).Should(Panic())
				})
			})

			Context("when the function takes more than one argument", func() {
				It("should panic", func() {
					Ω(func() {
						newRunnableNode(func(done Done, oops string) {
						}, types.GenerateCodeLocation(0), 0)
					}).Should(Panic())
				})
			})
		})
	})

	Describe("ItNodes", func() {
		It("should save off the text and flags", func() {
			codeLocation := types.GenerateCodeLocation(0)
			it := newItNode("my it node", func() {}, flagTypeFocused, codeLocation, 0)
			Ω(it.flag).Should(Equal(flagTypeFocused))
			Ω(it.text).Should(Equal("my it node"))
			Ω(it.codeLocation).Should(Equal(codeLocation))
		})
	})
}